162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2005-2011 Atheros Communications Inc.
462306a36Sopenharmony_ci * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
562306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "mac.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <net/cfg80211.h>
1162306a36Sopenharmony_ci#include <net/mac80211.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/acpi.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/bitfield.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "hif.h"
1862306a36Sopenharmony_ci#include "core.h"
1962306a36Sopenharmony_ci#include "debug.h"
2062306a36Sopenharmony_ci#include "wmi.h"
2162306a36Sopenharmony_ci#include "htt.h"
2262306a36Sopenharmony_ci#include "txrx.h"
2362306a36Sopenharmony_ci#include "testmode.h"
2462306a36Sopenharmony_ci#include "wmi-tlv.h"
2562306a36Sopenharmony_ci#include "wmi-ops.h"
2662306a36Sopenharmony_ci#include "wow.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*********/
2962306a36Sopenharmony_ci/* Rates */
3062306a36Sopenharmony_ci/*********/
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct ieee80211_rate ath10k_rates[] = {
3362306a36Sopenharmony_ci	{ .bitrate = 10,
3462306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_CCK_LP_1M },
3562306a36Sopenharmony_ci	{ .bitrate = 20,
3662306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_CCK_LP_2M,
3762306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_CCK_SP_2M,
3862306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
3962306a36Sopenharmony_ci	{ .bitrate = 55,
4062306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_CCK_LP_5_5M,
4162306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_CCK_SP_5_5M,
4262306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4362306a36Sopenharmony_ci	{ .bitrate = 110,
4462306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_CCK_LP_11M,
4562306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_CCK_SP_11M,
4662306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	{ .bitrate = 60, .hw_value = ATH10K_HW_RATE_OFDM_6M },
4962306a36Sopenharmony_ci	{ .bitrate = 90, .hw_value = ATH10K_HW_RATE_OFDM_9M },
5062306a36Sopenharmony_ci	{ .bitrate = 120, .hw_value = ATH10K_HW_RATE_OFDM_12M },
5162306a36Sopenharmony_ci	{ .bitrate = 180, .hw_value = ATH10K_HW_RATE_OFDM_18M },
5262306a36Sopenharmony_ci	{ .bitrate = 240, .hw_value = ATH10K_HW_RATE_OFDM_24M },
5362306a36Sopenharmony_ci	{ .bitrate = 360, .hw_value = ATH10K_HW_RATE_OFDM_36M },
5462306a36Sopenharmony_ci	{ .bitrate = 480, .hw_value = ATH10K_HW_RATE_OFDM_48M },
5562306a36Sopenharmony_ci	{ .bitrate = 540, .hw_value = ATH10K_HW_RATE_OFDM_54M },
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct ieee80211_rate ath10k_rates_rev2[] = {
5962306a36Sopenharmony_ci	{ .bitrate = 10,
6062306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_1M },
6162306a36Sopenharmony_ci	{ .bitrate = 20,
6262306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_2M,
6362306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_2M,
6462306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
6562306a36Sopenharmony_ci	{ .bitrate = 55,
6662306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_5_5M,
6762306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_5_5M,
6862306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
6962306a36Sopenharmony_ci	{ .bitrate = 110,
7062306a36Sopenharmony_ci	  .hw_value = ATH10K_HW_RATE_REV2_CCK_LP_11M,
7162306a36Sopenharmony_ci	  .hw_value_short = ATH10K_HW_RATE_REV2_CCK_SP_11M,
7262306a36Sopenharmony_ci	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	{ .bitrate = 60, .hw_value = ATH10K_HW_RATE_OFDM_6M },
7562306a36Sopenharmony_ci	{ .bitrate = 90, .hw_value = ATH10K_HW_RATE_OFDM_9M },
7662306a36Sopenharmony_ci	{ .bitrate = 120, .hw_value = ATH10K_HW_RATE_OFDM_12M },
7762306a36Sopenharmony_ci	{ .bitrate = 180, .hw_value = ATH10K_HW_RATE_OFDM_18M },
7862306a36Sopenharmony_ci	{ .bitrate = 240, .hw_value = ATH10K_HW_RATE_OFDM_24M },
7962306a36Sopenharmony_ci	{ .bitrate = 360, .hw_value = ATH10K_HW_RATE_OFDM_36M },
8062306a36Sopenharmony_ci	{ .bitrate = 480, .hw_value = ATH10K_HW_RATE_OFDM_48M },
8162306a36Sopenharmony_ci	{ .bitrate = 540, .hw_value = ATH10K_HW_RATE_OFDM_54M },
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic const struct cfg80211_sar_freq_ranges ath10k_sar_freq_ranges[] = {
8562306a36Sopenharmony_ci	{.start_freq = 2402, .end_freq = 2494 },
8662306a36Sopenharmony_ci	{.start_freq = 5170, .end_freq = 5875 },
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct cfg80211_sar_capa ath10k_sar_capa = {
9062306a36Sopenharmony_ci	.type = NL80211_SAR_TYPE_POWER,
9162306a36Sopenharmony_ci	.num_freq_ranges = (ARRAY_SIZE(ath10k_sar_freq_ranges)),
9262306a36Sopenharmony_ci	.freq_ranges = &ath10k_sar_freq_ranges[0],
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define ATH10K_MAC_FIRST_OFDM_RATE_IDX 4
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define ath10k_a_rates (ath10k_rates + ATH10K_MAC_FIRST_OFDM_RATE_IDX)
9862306a36Sopenharmony_ci#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - \
9962306a36Sopenharmony_ci			     ATH10K_MAC_FIRST_OFDM_RATE_IDX)
10062306a36Sopenharmony_ci#define ath10k_g_rates (ath10k_rates + 0)
10162306a36Sopenharmony_ci#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define ath10k_g_rates_rev2 (ath10k_rates_rev2 + 0)
10462306a36Sopenharmony_ci#define ath10k_g_rates_rev2_size (ARRAY_SIZE(ath10k_rates_rev2))
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define ath10k_wmi_legacy_rates ath10k_rates
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic bool ath10k_mac_bitrate_is_cck(int bitrate)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	switch (bitrate) {
11162306a36Sopenharmony_ci	case 10:
11262306a36Sopenharmony_ci	case 20:
11362306a36Sopenharmony_ci	case 55:
11462306a36Sopenharmony_ci	case 110:
11562306a36Sopenharmony_ci		return true;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return false;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic u8 ath10k_mac_bitrate_to_rate(int bitrate)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return DIV_ROUND_UP(bitrate, 5) |
12462306a36Sopenharmony_ci	       (ath10k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciu8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
12862306a36Sopenharmony_ci			     u8 hw_rate, bool cck)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	const struct ieee80211_rate *rate;
13162306a36Sopenharmony_ci	int i;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	for (i = 0; i < sband->n_bitrates; i++) {
13462306a36Sopenharmony_ci		rate = &sband->bitrates[i];
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if (ath10k_mac_bitrate_is_cck(rate->bitrate) != cck)
13762306a36Sopenharmony_ci			continue;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		if (rate->hw_value == hw_rate)
14062306a36Sopenharmony_ci			return i;
14162306a36Sopenharmony_ci		else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
14262306a36Sopenharmony_ci			 rate->hw_value_short == hw_rate)
14362306a36Sopenharmony_ci			return i;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciu8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
15062306a36Sopenharmony_ci			     u32 bitrate)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < sband->n_bitrates; i++)
15562306a36Sopenharmony_ci		if (sband->bitrates[i].bitrate == bitrate)
15662306a36Sopenharmony_ci			return i;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int ath10k_mac_get_rate_hw_value(int bitrate)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int i;
16462306a36Sopenharmony_ci	u8 hw_value_prefix = 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (ath10k_mac_bitrate_is_cck(bitrate))
16762306a36Sopenharmony_ci		hw_value_prefix = WMI_RATE_PREAMBLE_CCK << 6;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ath10k_rates); i++) {
17062306a36Sopenharmony_ci		if (ath10k_rates[i].bitrate == bitrate)
17162306a36Sopenharmony_ci			return hw_value_prefix | ath10k_rates[i].hw_value;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return -EINVAL;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int ath10k_mac_get_max_vht_mcs_map(u16 mcs_map, int nss)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	switch ((mcs_map >> (2 * nss)) & 0x3) {
18062306a36Sopenharmony_ci	case IEEE80211_VHT_MCS_SUPPORT_0_7: return BIT(8) - 1;
18162306a36Sopenharmony_ci	case IEEE80211_VHT_MCS_SUPPORT_0_8: return BIT(9) - 1;
18262306a36Sopenharmony_ci	case IEEE80211_VHT_MCS_SUPPORT_0_9: return BIT(10) - 1;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic u32
18862306a36Sopenharmony_ciath10k_mac_max_ht_nss(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int nss;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (nss = IEEE80211_HT_MCS_MASK_LEN - 1; nss >= 0; nss--)
19362306a36Sopenharmony_ci		if (ht_mcs_mask[nss])
19462306a36Sopenharmony_ci			return nss + 1;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 1;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic u32
20062306a36Sopenharmony_ciath10k_mac_max_vht_nss(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	int nss;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	for (nss = NL80211_VHT_NSS_MAX - 1; nss >= 0; nss--)
20562306a36Sopenharmony_ci		if (vht_mcs_mask[nss])
20662306a36Sopenharmony_ci			return nss + 1;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 1;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciint ath10k_mac_ext_resource_config(struct ath10k *ar, u32 val)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	enum wmi_host_platform_type platform_type;
21462306a36Sopenharmony_ci	int ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_TX_MODE_DYNAMIC, ar->wmi.svc_map))
21762306a36Sopenharmony_ci		platform_type = WMI_HOST_PLATFORM_LOW_PERF;
21862306a36Sopenharmony_ci	else
21962306a36Sopenharmony_ci		platform_type = WMI_HOST_PLATFORM_HIGH_PERF;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ret = ath10k_wmi_ext_resource_config(ar, platform_type, val);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP) {
22462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to configure ext resource: %d\n", ret);
22562306a36Sopenharmony_ci		return ret;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/**********/
23262306a36Sopenharmony_ci/* Crypto */
23362306a36Sopenharmony_ci/**********/
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int ath10k_send_key(struct ath10k_vif *arvif,
23662306a36Sopenharmony_ci			   struct ieee80211_key_conf *key,
23762306a36Sopenharmony_ci			   enum set_key_cmd cmd,
23862306a36Sopenharmony_ci			   const u8 *macaddr, u32 flags)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
24162306a36Sopenharmony_ci	struct wmi_vdev_install_key_arg arg = {
24262306a36Sopenharmony_ci		.vdev_id = arvif->vdev_id,
24362306a36Sopenharmony_ci		.key_idx = key->keyidx,
24462306a36Sopenharmony_ci		.key_len = key->keylen,
24562306a36Sopenharmony_ci		.key_data = key->key,
24662306a36Sopenharmony_ci		.key_flags = flags,
24762306a36Sopenharmony_ci		.macaddr = macaddr,
24862306a36Sopenharmony_ci	};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	switch (key->cipher) {
25362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
25462306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM];
25562306a36Sopenharmony_ci		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
25862306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_TKIP];
25962306a36Sopenharmony_ci		arg.key_txmic_len = 8;
26062306a36Sopenharmony_ci		arg.key_rxmic_len = 8;
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
26362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
26462306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_WEP];
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP_256:
26762306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM];
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP:
27062306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP_256:
27162306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_GCM];
27262306a36Sopenharmony_ci		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
27562306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
27662306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
27762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_AES_CMAC:
27862306a36Sopenharmony_ci		WARN_ON(1);
27962306a36Sopenharmony_ci		return -EINVAL;
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
28262306a36Sopenharmony_ci		return -EOPNOTSUPP;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
28662306a36Sopenharmony_ci		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (cmd == DISABLE_KEY) {
28962306a36Sopenharmony_ci		arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_NONE];
29062306a36Sopenharmony_ci		arg.key_data = NULL;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int ath10k_install_key(struct ath10k_vif *arvif,
29762306a36Sopenharmony_ci			      struct ieee80211_key_conf *key,
29862306a36Sopenharmony_ci			      enum set_key_cmd cmd,
29962306a36Sopenharmony_ci			      const u8 *macaddr, u32 flags)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
30262306a36Sopenharmony_ci	int ret;
30362306a36Sopenharmony_ci	unsigned long time_left;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	reinit_completion(&ar->install_key_done);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (arvif->nohwcrypt)
31062306a36Sopenharmony_ci		return 1;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ret = ath10k_send_key(arvif, key, cmd, macaddr, flags);
31362306a36Sopenharmony_ci	if (ret)
31462306a36Sopenharmony_ci		return ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	time_left = wait_for_completion_timeout(&ar->install_key_done, 3 * HZ);
31762306a36Sopenharmony_ci	if (time_left == 0)
31862306a36Sopenharmony_ci		return -ETIMEDOUT;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
32462306a36Sopenharmony_ci					const u8 *addr)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
32762306a36Sopenharmony_ci	struct ath10k_peer *peer;
32862306a36Sopenharmony_ci	int ret;
32962306a36Sopenharmony_ci	int i;
33062306a36Sopenharmony_ci	u32 flags;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP &&
33562306a36Sopenharmony_ci		    arvif->vif->type != NL80211_IFTYPE_ADHOC &&
33662306a36Sopenharmony_ci		    arvif->vif->type != NL80211_IFTYPE_MESH_POINT))
33762306a36Sopenharmony_ci		return -EINVAL;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
34062306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
34162306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!peer)
34462306a36Sopenharmony_ci		return -ENOENT;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
34762306a36Sopenharmony_ci		if (arvif->wep_keys[i] == NULL)
34862306a36Sopenharmony_ci			continue;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		switch (arvif->vif->type) {
35162306a36Sopenharmony_ci		case NL80211_IFTYPE_AP:
35262306a36Sopenharmony_ci			flags = WMI_KEY_PAIRWISE;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci			if (arvif->def_wep_key_idx == i)
35562306a36Sopenharmony_ci				flags |= WMI_KEY_TX_USAGE;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci			ret = ath10k_install_key(arvif, arvif->wep_keys[i],
35862306a36Sopenharmony_ci						 SET_KEY, addr, flags);
35962306a36Sopenharmony_ci			if (ret < 0)
36062306a36Sopenharmony_ci				return ret;
36162306a36Sopenharmony_ci			break;
36262306a36Sopenharmony_ci		case NL80211_IFTYPE_ADHOC:
36362306a36Sopenharmony_ci			ret = ath10k_install_key(arvif, arvif->wep_keys[i],
36462306a36Sopenharmony_ci						 SET_KEY, addr,
36562306a36Sopenharmony_ci						 WMI_KEY_PAIRWISE);
36662306a36Sopenharmony_ci			if (ret < 0)
36762306a36Sopenharmony_ci				return ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			ret = ath10k_install_key(arvif, arvif->wep_keys[i],
37062306a36Sopenharmony_ci						 SET_KEY, addr, WMI_KEY_GROUP);
37162306a36Sopenharmony_ci			if (ret < 0)
37262306a36Sopenharmony_ci				return ret;
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci		default:
37562306a36Sopenharmony_ci			WARN_ON(1);
37662306a36Sopenharmony_ci			return -EINVAL;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
38062306a36Sopenharmony_ci		peer->keys[i] = arvif->wep_keys[i];
38162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* In some cases (notably with static WEP IBSS with multiple keys)
38562306a36Sopenharmony_ci	 * multicast Tx becomes broken. Both pairwise and groupwise keys are
38662306a36Sopenharmony_ci	 * installed already. Using WMI_KEY_TX_USAGE in different combinations
38762306a36Sopenharmony_ci	 * didn't seem help. Using def_keyid vdev parameter seems to be
38862306a36Sopenharmony_ci	 * effective so use that.
38962306a36Sopenharmony_ci	 *
39062306a36Sopenharmony_ci	 * FIXME: Revisit. Perhaps this can be done in a less hacky way.
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	if (arvif->vif->type != NL80211_IFTYPE_ADHOC)
39362306a36Sopenharmony_ci		return 0;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (arvif->def_wep_key_idx == -1)
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(arvif->ar,
39962306a36Sopenharmony_ci					arvif->vdev_id,
40062306a36Sopenharmony_ci					arvif->ar->wmi.vdev_param->def_keyid,
40162306a36Sopenharmony_ci					arvif->def_wep_key_idx);
40262306a36Sopenharmony_ci	if (ret) {
40362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to re-set def wpa key idxon vdev %i: %d\n",
40462306a36Sopenharmony_ci			    arvif->vdev_id, ret);
40562306a36Sopenharmony_ci		return ret;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
41262306a36Sopenharmony_ci				  const u8 *addr)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
41562306a36Sopenharmony_ci	struct ath10k_peer *peer;
41662306a36Sopenharmony_ci	int first_errno = 0;
41762306a36Sopenharmony_ci	int ret;
41862306a36Sopenharmony_ci	int i;
41962306a36Sopenharmony_ci	u32 flags = 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
42462306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
42562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (!peer)
42862306a36Sopenharmony_ci		return -ENOENT;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
43162306a36Sopenharmony_ci		if (peer->keys[i] == NULL)
43262306a36Sopenharmony_ci			continue;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		/* key flags are not required to delete the key */
43562306a36Sopenharmony_ci		ret = ath10k_install_key(arvif, peer->keys[i],
43662306a36Sopenharmony_ci					 DISABLE_KEY, addr, flags);
43762306a36Sopenharmony_ci		if (ret < 0 && first_errno == 0)
43862306a36Sopenharmony_ci			first_errno = ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (ret < 0)
44162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
44262306a36Sopenharmony_ci				    i, ret);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
44562306a36Sopenharmony_ci		peer->keys[i] = NULL;
44662306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return first_errno;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cibool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
45362306a36Sopenharmony_ci				    u8 keyidx)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct ath10k_peer *peer;
45662306a36Sopenharmony_ci	int i;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* We don't know which vdev this peer belongs to,
46162306a36Sopenharmony_ci	 * since WMI doesn't give us that information.
46262306a36Sopenharmony_ci	 *
46362306a36Sopenharmony_ci	 * FIXME: multi-bss needs to be handled.
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, 0, addr);
46662306a36Sopenharmony_ci	if (!peer)
46762306a36Sopenharmony_ci		return false;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
47062306a36Sopenharmony_ci		if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
47162306a36Sopenharmony_ci			return true;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return false;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
47862306a36Sopenharmony_ci				 struct ieee80211_key_conf *key)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
48162306a36Sopenharmony_ci	struct ath10k_peer *peer;
48262306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
48362306a36Sopenharmony_ci	int first_errno = 0;
48462306a36Sopenharmony_ci	int ret;
48562306a36Sopenharmony_ci	int i;
48662306a36Sopenharmony_ci	u32 flags = 0;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	for (;;) {
49162306a36Sopenharmony_ci		/* since ath10k_install_key we can't hold data_lock all the
49262306a36Sopenharmony_ci		 * time, so we try to remove the keys incrementally
49362306a36Sopenharmony_ci		 */
49462306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
49562306a36Sopenharmony_ci		i = 0;
49662306a36Sopenharmony_ci		list_for_each_entry(peer, &ar->peers, list) {
49762306a36Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
49862306a36Sopenharmony_ci				if (peer->keys[i] == key) {
49962306a36Sopenharmony_ci					ether_addr_copy(addr, peer->addr);
50062306a36Sopenharmony_ci					peer->keys[i] = NULL;
50162306a36Sopenharmony_ci					break;
50262306a36Sopenharmony_ci				}
50362306a36Sopenharmony_ci			}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci			if (i < ARRAY_SIZE(peer->keys))
50662306a36Sopenharmony_ci				break;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		if (i == ARRAY_SIZE(peer->keys))
51162306a36Sopenharmony_ci			break;
51262306a36Sopenharmony_ci		/* key flags are not required to delete the key */
51362306a36Sopenharmony_ci		ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, flags);
51462306a36Sopenharmony_ci		if (ret < 0 && first_errno == 0)
51562306a36Sopenharmony_ci			first_errno = ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		if (ret)
51862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to remove key for %pM: %d\n",
51962306a36Sopenharmony_ci				    addr, ret);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return first_errno;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int ath10k_mac_vif_update_wep_key(struct ath10k_vif *arvif,
52662306a36Sopenharmony_ci					 struct ieee80211_key_conf *key)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
52962306a36Sopenharmony_ci	struct ath10k_peer *peer;
53062306a36Sopenharmony_ci	int ret;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	list_for_each_entry(peer, &ar->peers, list) {
53562306a36Sopenharmony_ci		if (ether_addr_equal(peer->addr, arvif->vif->addr))
53662306a36Sopenharmony_ci			continue;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		if (ether_addr_equal(peer->addr, arvif->bssid))
53962306a36Sopenharmony_ci			continue;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		if (peer->keys[key->keyidx] == key)
54262306a36Sopenharmony_ci			continue;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vif vdev %i update key %i needs update\n",
54562306a36Sopenharmony_ci			   arvif->vdev_id, key->keyidx);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		ret = ath10k_install_peer_wep_keys(arvif, peer->addr);
54862306a36Sopenharmony_ci		if (ret) {
54962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update wep keys on vdev %i for peer %pM: %d\n",
55062306a36Sopenharmony_ci				    arvif->vdev_id, peer->addr, ret);
55162306a36Sopenharmony_ci			return ret;
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*********************/
55962306a36Sopenharmony_ci/* General utilities */
56062306a36Sopenharmony_ci/*********************/
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic inline enum wmi_phy_mode
56362306a36Sopenharmony_cichan_to_phymode(const struct cfg80211_chan_def *chandef)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	enum wmi_phy_mode phymode = MODE_UNKNOWN;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	switch (chandef->chan->band) {
56862306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
56962306a36Sopenharmony_ci		switch (chandef->width) {
57062306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20_NOHT:
57162306a36Sopenharmony_ci			if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM)
57262306a36Sopenharmony_ci				phymode = MODE_11B;
57362306a36Sopenharmony_ci			else
57462306a36Sopenharmony_ci				phymode = MODE_11G;
57562306a36Sopenharmony_ci			break;
57662306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20:
57762306a36Sopenharmony_ci			phymode = MODE_11NG_HT20;
57862306a36Sopenharmony_ci			break;
57962306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_40:
58062306a36Sopenharmony_ci			phymode = MODE_11NG_HT40;
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		default:
58362306a36Sopenharmony_ci			phymode = MODE_UNKNOWN;
58462306a36Sopenharmony_ci			break;
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
58862306a36Sopenharmony_ci		switch (chandef->width) {
58962306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20_NOHT:
59062306a36Sopenharmony_ci			phymode = MODE_11A;
59162306a36Sopenharmony_ci			break;
59262306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20:
59362306a36Sopenharmony_ci			phymode = MODE_11NA_HT20;
59462306a36Sopenharmony_ci			break;
59562306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_40:
59662306a36Sopenharmony_ci			phymode = MODE_11NA_HT40;
59762306a36Sopenharmony_ci			break;
59862306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_80:
59962306a36Sopenharmony_ci			phymode = MODE_11AC_VHT80;
60062306a36Sopenharmony_ci			break;
60162306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_160:
60262306a36Sopenharmony_ci			phymode = MODE_11AC_VHT160;
60362306a36Sopenharmony_ci			break;
60462306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_80P80:
60562306a36Sopenharmony_ci			phymode = MODE_11AC_VHT80_80;
60662306a36Sopenharmony_ci			break;
60762306a36Sopenharmony_ci		default:
60862306a36Sopenharmony_ci			phymode = MODE_UNKNOWN;
60962306a36Sopenharmony_ci			break;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		break;
61262306a36Sopenharmony_ci	default:
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	WARN_ON(phymode == MODE_UNKNOWN);
61762306a36Sopenharmony_ci	return phymode;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic u8 ath10k_parse_mpdudensity(u8 mpdudensity)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci/*
62362306a36Sopenharmony_ci * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
62462306a36Sopenharmony_ci *   0 for no restriction
62562306a36Sopenharmony_ci *   1 for 1/4 us
62662306a36Sopenharmony_ci *   2 for 1/2 us
62762306a36Sopenharmony_ci *   3 for 1 us
62862306a36Sopenharmony_ci *   4 for 2 us
62962306a36Sopenharmony_ci *   5 for 4 us
63062306a36Sopenharmony_ci *   6 for 8 us
63162306a36Sopenharmony_ci *   7 for 16 us
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_ci	switch (mpdudensity) {
63462306a36Sopenharmony_ci	case 0:
63562306a36Sopenharmony_ci		return 0;
63662306a36Sopenharmony_ci	case 1:
63762306a36Sopenharmony_ci	case 2:
63862306a36Sopenharmony_ci	case 3:
63962306a36Sopenharmony_ci	/* Our lower layer calculations limit our precision to
64062306a36Sopenharmony_ci	 * 1 microsecond
64162306a36Sopenharmony_ci	 */
64262306a36Sopenharmony_ci		return 1;
64362306a36Sopenharmony_ci	case 4:
64462306a36Sopenharmony_ci		return 2;
64562306a36Sopenharmony_ci	case 5:
64662306a36Sopenharmony_ci		return 4;
64762306a36Sopenharmony_ci	case 6:
64862306a36Sopenharmony_ci		return 8;
64962306a36Sopenharmony_ci	case 7:
65062306a36Sopenharmony_ci		return 16;
65162306a36Sopenharmony_ci	default:
65262306a36Sopenharmony_ci		return 0;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ciint ath10k_mac_vif_chan(struct ieee80211_vif *vif,
65762306a36Sopenharmony_ci			struct cfg80211_chan_def *def)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	rcu_read_lock();
66262306a36Sopenharmony_ci	conf = rcu_dereference(vif->bss_conf.chanctx_conf);
66362306a36Sopenharmony_ci	if (!conf) {
66462306a36Sopenharmony_ci		rcu_read_unlock();
66562306a36Sopenharmony_ci		return -ENOENT;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	*def = conf->def;
66962306a36Sopenharmony_ci	rcu_read_unlock();
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void ath10k_mac_num_chanctxs_iter(struct ieee80211_hw *hw,
67562306a36Sopenharmony_ci					 struct ieee80211_chanctx_conf *conf,
67662306a36Sopenharmony_ci					 void *data)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	int *num = data;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	(*num)++;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int ath10k_mac_num_chanctxs(struct ath10k *ar)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	int num = 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	ieee80211_iter_chan_contexts_atomic(ar->hw,
68862306a36Sopenharmony_ci					    ath10k_mac_num_chanctxs_iter,
68962306a36Sopenharmony_ci					    &num);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return num;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic void
69562306a36Sopenharmony_ciath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
69662306a36Sopenharmony_ci				struct ieee80211_chanctx_conf *conf,
69762306a36Sopenharmony_ci				void *data)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct cfg80211_chan_def **def = data;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	*def = &conf->def;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void ath10k_wait_for_peer_delete_done(struct ath10k *ar, u32 vdev_id,
70562306a36Sopenharmony_ci					     const u8 *addr)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	unsigned long time_left;
70862306a36Sopenharmony_ci	int ret;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
71162306a36Sopenharmony_ci		ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
71262306a36Sopenharmony_ci		if (ret) {
71362306a36Sopenharmony_ci			ath10k_warn(ar, "failed wait for peer deleted");
71462306a36Sopenharmony_ci			return;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		time_left = wait_for_completion_timeout(&ar->peer_delete_done,
71862306a36Sopenharmony_ci							5 * HZ);
71962306a36Sopenharmony_ci		if (!time_left)
72062306a36Sopenharmony_ci			ath10k_warn(ar, "Timeout in receiving peer delete response\n");
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int ath10k_peer_create(struct ath10k *ar,
72562306a36Sopenharmony_ci			      struct ieee80211_vif *vif,
72662306a36Sopenharmony_ci			      struct ieee80211_sta *sta,
72762306a36Sopenharmony_ci			      u32 vdev_id,
72862306a36Sopenharmony_ci			      const u8 *addr,
72962306a36Sopenharmony_ci			      enum wmi_peer_type peer_type)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct ath10k_vif *arvif;
73262306a36Sopenharmony_ci	struct ath10k_peer *peer;
73362306a36Sopenharmony_ci	int num_peers = 0;
73462306a36Sopenharmony_ci	int ret;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	num_peers = ar->num_peers;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/* Each vdev consumes a peer entry as well */
74162306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list)
74262306a36Sopenharmony_ci		num_peers++;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (num_peers >= ar->max_num_peers)
74562306a36Sopenharmony_ci		return -ENOBUFS;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	ret = ath10k_wmi_peer_create(ar, vdev_id, addr, peer_type);
74862306a36Sopenharmony_ci	if (ret) {
74962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
75062306a36Sopenharmony_ci			    addr, vdev_id, ret);
75162306a36Sopenharmony_ci		return ret;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
75562306a36Sopenharmony_ci	if (ret) {
75662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
75762306a36Sopenharmony_ci			    addr, vdev_id, ret);
75862306a36Sopenharmony_ci		return ret;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, vdev_id, addr);
76462306a36Sopenharmony_ci	if (!peer) {
76562306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
76662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
76762306a36Sopenharmony_ci			    addr, vdev_id);
76862306a36Sopenharmony_ci		ath10k_wait_for_peer_delete_done(ar, vdev_id, addr);
76962306a36Sopenharmony_ci		return -ENOENT;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	peer->vif = vif;
77362306a36Sopenharmony_ci	peer->sta = sta;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ar->num_peers++;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return 0;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
78562306a36Sopenharmony_ci	u32 param;
78662306a36Sopenharmony_ci	int ret;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	param = ar->wmi.pdev_param->sta_kickout_th;
78962306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param,
79062306a36Sopenharmony_ci					ATH10K_KICKOUT_THRESHOLD);
79162306a36Sopenharmony_ci	if (ret) {
79262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set kickout threshold on vdev %i: %d\n",
79362306a36Sopenharmony_ci			    arvif->vdev_id, ret);
79462306a36Sopenharmony_ci		return ret;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
79862306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
79962306a36Sopenharmony_ci					ATH10K_KEEPALIVE_MIN_IDLE);
80062306a36Sopenharmony_ci	if (ret) {
80162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set keepalive minimum idle time on vdev %i: %d\n",
80262306a36Sopenharmony_ci			    arvif->vdev_id, ret);
80362306a36Sopenharmony_ci		return ret;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
80762306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
80862306a36Sopenharmony_ci					ATH10K_KEEPALIVE_MAX_IDLE);
80962306a36Sopenharmony_ci	if (ret) {
81062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set keepalive maximum idle time on vdev %i: %d\n",
81162306a36Sopenharmony_ci			    arvif->vdev_id, ret);
81262306a36Sopenharmony_ci		return ret;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
81662306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
81762306a36Sopenharmony_ci					ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
81862306a36Sopenharmony_ci	if (ret) {
81962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
82062306a36Sopenharmony_ci			    arvif->vdev_id, ret);
82162306a36Sopenharmony_ci		return ret;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	return 0;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
83062306a36Sopenharmony_ci	u32 vdev_param;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->rts_threshold;
83362306a36Sopenharmony_ci	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	int ret;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
84362306a36Sopenharmony_ci	if (ret)
84462306a36Sopenharmony_ci		return ret;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
84762306a36Sopenharmony_ci	if (ret)
84862306a36Sopenharmony_ci		return ret;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
85162306a36Sopenharmony_ci		unsigned long time_left;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		time_left = wait_for_completion_timeout
85462306a36Sopenharmony_ci			    (&ar->peer_delete_done, 5 * HZ);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (!time_left) {
85762306a36Sopenharmony_ci			ath10k_warn(ar, "Timeout in receiving peer delete response\n");
85862306a36Sopenharmony_ci			return -ETIMEDOUT;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	ar->num_peers--;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic void ath10k_peer_map_cleanup(struct ath10k *ar, struct ath10k_peer *peer)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	int peer_id, i;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	for_each_set_bit(peer_id, peer->peer_ids,
87462306a36Sopenharmony_ci			 ATH10K_MAX_NUM_PEER_IDS) {
87562306a36Sopenharmony_ci		ar->peer_map[peer_id] = NULL;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Double check that peer is properly un-referenced from
87962306a36Sopenharmony_ci	 * the peer_map
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
88262306a36Sopenharmony_ci		if (ar->peer_map[i] == peer) {
88362306a36Sopenharmony_ci			ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %pK idx %d)\n",
88462306a36Sopenharmony_ci				    peer->addr, peer, i);
88562306a36Sopenharmony_ci			ar->peer_map[i] = NULL;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	list_del(&peer->list);
89062306a36Sopenharmony_ci	kfree(peer);
89162306a36Sopenharmony_ci	ar->num_peers--;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct ath10k_peer *peer, *tmp;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
90162306a36Sopenharmony_ci	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
90262306a36Sopenharmony_ci		if (peer->vdev_id != vdev_id)
90362306a36Sopenharmony_ci			continue;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
90662306a36Sopenharmony_ci			    peer->addr, vdev_id);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		ath10k_peer_map_cleanup(ar, peer);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic void ath10k_peer_cleanup_all(struct ath10k *ar)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct ath10k_peer *peer, *tmp;
91662306a36Sopenharmony_ci	int i;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
92162306a36Sopenharmony_ci	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
92262306a36Sopenharmony_ci		list_del(&peer->list);
92362306a36Sopenharmony_ci		kfree(peer);
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++)
92762306a36Sopenharmony_ci		ar->peer_map[i] = NULL;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	ar->num_peers = 0;
93262306a36Sopenharmony_ci	ar->num_stations = 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic int ath10k_mac_tdls_peer_update(struct ath10k *ar, u32 vdev_id,
93662306a36Sopenharmony_ci				       struct ieee80211_sta *sta,
93762306a36Sopenharmony_ci				       enum wmi_tdls_peer_state state)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	int ret;
94062306a36Sopenharmony_ci	struct wmi_tdls_peer_update_cmd_arg arg = {};
94162306a36Sopenharmony_ci	struct wmi_tdls_peer_capab_arg cap = {};
94262306a36Sopenharmony_ci	struct wmi_channel_arg chan_arg = {};
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	arg.vdev_id = vdev_id;
94762306a36Sopenharmony_ci	arg.peer_state = state;
94862306a36Sopenharmony_ci	ether_addr_copy(arg.addr, sta->addr);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	cap.peer_max_sp = sta->max_sp;
95162306a36Sopenharmony_ci	cap.peer_uapsd_queues = sta->uapsd_queues;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (state == WMI_TDLS_PEER_STATE_CONNECTED &&
95462306a36Sopenharmony_ci	    !sta->tdls_initiator)
95562306a36Sopenharmony_ci		cap.is_peer_responder = 1;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	ret = ath10k_wmi_tdls_peer_update(ar, &arg, &cap, &chan_arg);
95862306a36Sopenharmony_ci	if (ret) {
95962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update tdls peer %pM on vdev %i: %i\n",
96062306a36Sopenharmony_ci			    arg.addr, vdev_id, ret);
96162306a36Sopenharmony_ci		return ret;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	return 0;
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci/************************/
96862306a36Sopenharmony_ci/* Interface management */
96962306a36Sopenharmony_ci/************************/
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_civoid ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (!arvif->beacon)
97862306a36Sopenharmony_ci		return;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (!arvif->beacon_buf)
98162306a36Sopenharmony_ci		dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
98262306a36Sopenharmony_ci				 arvif->beacon->len, DMA_TO_DEVICE);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED &&
98562306a36Sopenharmony_ci		    arvif->beacon_state != ATH10K_BEACON_SENT))
98662306a36Sopenharmony_ci		return;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	dev_kfree_skb_any(arvif->beacon);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	arvif->beacon = NULL;
99162306a36Sopenharmony_ci	arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	ath10k_mac_vif_beacon_free(arvif);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (arvif->beacon_buf) {
100362306a36Sopenharmony_ci		if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
100462306a36Sopenharmony_ci			kfree(arvif->beacon_buf);
100562306a36Sopenharmony_ci		else
100662306a36Sopenharmony_ci			dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
100762306a36Sopenharmony_ci					  arvif->beacon_buf,
100862306a36Sopenharmony_ci					  arvif->beacon_paddr);
100962306a36Sopenharmony_ci		arvif->beacon_buf = NULL;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic inline int ath10k_vdev_setup_sync(struct ath10k *ar)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	unsigned long time_left;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
102062306a36Sopenharmony_ci		return -ESHUTDOWN;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	time_left = wait_for_completion_timeout(&ar->vdev_setup_done,
102362306a36Sopenharmony_ci						ATH10K_VDEV_SETUP_TIMEOUT_HZ);
102462306a36Sopenharmony_ci	if (time_left == 0)
102562306a36Sopenharmony_ci		return -ETIMEDOUT;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return ar->last_wmi_vdev_start_status;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct cfg80211_chan_def *chandef = NULL;
103362306a36Sopenharmony_ci	struct ieee80211_channel *channel = NULL;
103462306a36Sopenharmony_ci	struct wmi_vdev_start_request_arg arg = {};
103562306a36Sopenharmony_ci	int ret = 0;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	ieee80211_iter_chan_contexts_atomic(ar->hw,
104062306a36Sopenharmony_ci					    ath10k_mac_get_any_chandef_iter,
104162306a36Sopenharmony_ci					    &chandef);
104262306a36Sopenharmony_ci	if (WARN_ON_ONCE(!chandef))
104362306a36Sopenharmony_ci		return -ENOENT;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	channel = chandef->chan;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	arg.vdev_id = vdev_id;
104862306a36Sopenharmony_ci	arg.channel.freq = channel->center_freq;
104962306a36Sopenharmony_ci	arg.channel.band_center_freq1 = chandef->center_freq1;
105062306a36Sopenharmony_ci	arg.channel.band_center_freq2 = chandef->center_freq2;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* TODO setup this dynamically, what in case we
105362306a36Sopenharmony_ci	 * don't have any vifs?
105462306a36Sopenharmony_ci	 */
105562306a36Sopenharmony_ci	arg.channel.mode = chan_to_phymode(chandef);
105662306a36Sopenharmony_ci	arg.channel.chan_radar =
105762306a36Sopenharmony_ci			!!(channel->flags & IEEE80211_CHAN_RADAR);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	arg.channel.min_power = 0;
106062306a36Sopenharmony_ci	arg.channel.max_power = channel->max_power * 2;
106162306a36Sopenharmony_ci	arg.channel.max_reg_power = channel->max_reg_power * 2;
106262306a36Sopenharmony_ci	arg.channel.max_antenna_gain = channel->max_antenna_gain;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	reinit_completion(&ar->vdev_setup_done);
106562306a36Sopenharmony_ci	reinit_completion(&ar->vdev_delete_done);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_start(ar, &arg);
106862306a36Sopenharmony_ci	if (ret) {
106962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
107062306a36Sopenharmony_ci			    vdev_id, ret);
107162306a36Sopenharmony_ci		return ret;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	ret = ath10k_vdev_setup_sync(ar);
107562306a36Sopenharmony_ci	if (ret) {
107662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
107762306a36Sopenharmony_ci			    vdev_id, ret);
107862306a36Sopenharmony_ci		return ret;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
108262306a36Sopenharmony_ci	if (ret) {
108362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to put up monitor vdev %i: %d\n",
108462306a36Sopenharmony_ci			    vdev_id, ret);
108562306a36Sopenharmony_ci		goto vdev_stop;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	ar->monitor_vdev_id = vdev_id;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
109162306a36Sopenharmony_ci		   ar->monitor_vdev_id);
109262306a36Sopenharmony_ci	return 0;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_civdev_stop:
109562306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
109662306a36Sopenharmony_ci	if (ret)
109762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop monitor vdev %i after start failure: %d\n",
109862306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return ret;
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic int ath10k_monitor_vdev_stop(struct ath10k *ar)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	int ret = 0;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
111062306a36Sopenharmony_ci	if (ret)
111162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
111262306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	reinit_completion(&ar->vdev_setup_done);
111562306a36Sopenharmony_ci	reinit_completion(&ar->vdev_delete_done);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
111862306a36Sopenharmony_ci	if (ret)
111962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to request monitor vdev %i stop: %d\n",
112062306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	ret = ath10k_vdev_setup_sync(ar);
112362306a36Sopenharmony_ci	if (ret)
112462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to synchronize monitor vdev %i stop: %d\n",
112562306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
112862306a36Sopenharmony_ci		   ar->monitor_vdev_id);
112962306a36Sopenharmony_ci	return ret;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic int ath10k_monitor_vdev_create(struct ath10k *ar)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	int bit, ret = 0;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (ar->free_vdev_map == 0) {
113962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to find free vdev id for monitor vdev\n");
114062306a36Sopenharmony_ci		return -ENOMEM;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	bit = __ffs64(ar->free_vdev_map);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	ar->monitor_vdev_id = bit;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
114862306a36Sopenharmony_ci				     WMI_VDEV_TYPE_MONITOR,
114962306a36Sopenharmony_ci				     0, ar->mac_addr);
115062306a36Sopenharmony_ci	if (ret) {
115162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to request monitor vdev %i creation: %d\n",
115262306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
115362306a36Sopenharmony_ci		return ret;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	ar->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
115762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
115862306a36Sopenharmony_ci		   ar->monitor_vdev_id);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return 0;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int ath10k_monitor_vdev_delete(struct ath10k *ar)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	int ret = 0;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
117062306a36Sopenharmony_ci	if (ret) {
117162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to request wmi monitor vdev %i removal: %d\n",
117262306a36Sopenharmony_ci			    ar->monitor_vdev_id, ret);
117362306a36Sopenharmony_ci		return ret;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	ar->free_vdev_map |= 1LL << ar->monitor_vdev_id;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
117962306a36Sopenharmony_ci		   ar->monitor_vdev_id);
118062306a36Sopenharmony_ci	return ret;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int ath10k_monitor_start(struct ath10k *ar)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	int ret;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	ret = ath10k_monitor_vdev_create(ar);
119062306a36Sopenharmony_ci	if (ret) {
119162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to create monitor vdev: %d\n", ret);
119262306a36Sopenharmony_ci		return ret;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
119662306a36Sopenharmony_ci	if (ret) {
119762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start monitor vdev: %d\n", ret);
119862306a36Sopenharmony_ci		ath10k_monitor_vdev_delete(ar);
119962306a36Sopenharmony_ci		return ret;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ar->monitor_started = true;
120362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor started\n");
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	return 0;
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic int ath10k_monitor_stop(struct ath10k *ar)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	int ret;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	ret = ath10k_monitor_vdev_stop(ar);
121562306a36Sopenharmony_ci	if (ret) {
121662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop monitor vdev: %d\n", ret);
121762306a36Sopenharmony_ci		return ret;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	ret = ath10k_monitor_vdev_delete(ar);
122162306a36Sopenharmony_ci	if (ret) {
122262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to delete monitor vdev: %d\n", ret);
122362306a36Sopenharmony_ci		return ret;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	ar->monitor_started = false;
122762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopped\n");
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	return 0;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	int num_ctx;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* At least one chanctx is required to derive a channel to start
123762306a36Sopenharmony_ci	 * monitor vdev on.
123862306a36Sopenharmony_ci	 */
123962306a36Sopenharmony_ci	num_ctx = ath10k_mac_num_chanctxs(ar);
124062306a36Sopenharmony_ci	if (num_ctx == 0)
124162306a36Sopenharmony_ci		return false;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* If there's already an existing special monitor interface then don't
124462306a36Sopenharmony_ci	 * bother creating another monitor vdev.
124562306a36Sopenharmony_ci	 */
124662306a36Sopenharmony_ci	if (ar->monitor_arvif)
124762306a36Sopenharmony_ci		return false;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	return ar->monitor ||
125062306a36Sopenharmony_ci	       (!test_bit(ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST,
125162306a36Sopenharmony_ci			  ar->running_fw->fw_file.fw_features) &&
125262306a36Sopenharmony_ci		(ar->filter_flags & FIF_OTHER_BSS)) ||
125362306a36Sopenharmony_ci	       test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic bool ath10k_mac_monitor_vdev_is_allowed(struct ath10k *ar)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	int num_ctx;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	num_ctx = ath10k_mac_num_chanctxs(ar);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* FIXME: Current interface combinations and cfg80211/mac80211 code
126362306a36Sopenharmony_ci	 * shouldn't allow this but make sure to prevent handling the following
126462306a36Sopenharmony_ci	 * case anyway since multi-channel DFS hasn't been tested at all.
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags) && num_ctx > 1)
126762306a36Sopenharmony_ci		return false;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return true;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic int ath10k_monitor_recalc(struct ath10k *ar)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	bool needed;
127562306a36Sopenharmony_ci	bool allowed;
127662306a36Sopenharmony_ci	int ret;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	needed = ath10k_mac_monitor_vdev_is_needed(ar);
128162306a36Sopenharmony_ci	allowed = ath10k_mac_monitor_vdev_is_allowed(ar);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
128462306a36Sopenharmony_ci		   "mac monitor recalc started? %d needed? %d allowed? %d\n",
128562306a36Sopenharmony_ci		   ar->monitor_started, needed, allowed);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	if (WARN_ON(needed && !allowed)) {
128862306a36Sopenharmony_ci		if (ar->monitor_started) {
128962306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopping disallowed monitor\n");
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci			ret = ath10k_monitor_stop(ar);
129262306a36Sopenharmony_ci			if (ret)
129362306a36Sopenharmony_ci				ath10k_warn(ar, "failed to stop disallowed monitor: %d\n",
129462306a36Sopenharmony_ci					    ret);
129562306a36Sopenharmony_ci				/* not serious */
129662306a36Sopenharmony_ci		}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		return -EPERM;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (needed == ar->monitor_started)
130262306a36Sopenharmony_ci		return 0;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	if (needed)
130562306a36Sopenharmony_ci		return ath10k_monitor_start(ar);
130662306a36Sopenharmony_ci	else
130762306a36Sopenharmony_ci		return ath10k_monitor_stop(ar);
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic bool ath10k_mac_can_set_cts_prot(struct ath10k_vif *arvif)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	if (!arvif->is_started) {
131762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "defer cts setup, vdev is not ready yet\n");
131862306a36Sopenharmony_ci		return false;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return true;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic int ath10k_mac_set_cts_prot(struct ath10k_vif *arvif)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
132762306a36Sopenharmony_ci	u32 vdev_param;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->protection_mode;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d cts_protection %d\n",
133462306a36Sopenharmony_ci		   arvif->vdev_id, arvif->use_cts_prot);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
133762306a36Sopenharmony_ci					 arvif->use_cts_prot ? 1 : 0);
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
134362306a36Sopenharmony_ci	u32 vdev_param, rts_cts = 0;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->enable_rtscts;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	if (arvif->num_legacy_stations > 0)
135262306a36Sopenharmony_ci		rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES,
135362306a36Sopenharmony_ci			      WMI_RTSCTS_PROFILE);
135462306a36Sopenharmony_ci	else
135562306a36Sopenharmony_ci		rts_cts |= SM(WMI_RTSCTS_FOR_SECOND_RATESERIES,
135662306a36Sopenharmony_ci			      WMI_RTSCTS_PROFILE);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d recalc rts/cts prot %d\n",
135962306a36Sopenharmony_ci		   arvif->vdev_id, rts_cts);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
136262306a36Sopenharmony_ci					 rts_cts);
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic int ath10k_start_cac(struct ath10k *ar)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	int ret;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	ret = ath10k_monitor_recalc(ar);
137462306a36Sopenharmony_ci	if (ret) {
137562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start monitor (cac): %d\n", ret);
137662306a36Sopenharmony_ci		clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
137762306a36Sopenharmony_ci		return ret;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
138162306a36Sopenharmony_ci		   ar->monitor_vdev_id);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	return 0;
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int ath10k_stop_cac(struct ath10k *ar)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	/* CAC is not running - do nothing */
139162306a36Sopenharmony_ci	if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
139262306a36Sopenharmony_ci		return 0;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
139562306a36Sopenharmony_ci	ath10k_monitor_stop(ar);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac finished\n");
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	return 0;
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cistatic void ath10k_mac_has_radar_iter(struct ieee80211_hw *hw,
140362306a36Sopenharmony_ci				      struct ieee80211_chanctx_conf *conf,
140462306a36Sopenharmony_ci				      void *data)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	bool *ret = data;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (!*ret && conf->radar_enabled)
140962306a36Sopenharmony_ci		*ret = true;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic bool ath10k_mac_has_radar_enabled(struct ath10k *ar)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	bool has_radar = false;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	ieee80211_iter_chan_contexts_atomic(ar->hw,
141762306a36Sopenharmony_ci					    ath10k_mac_has_radar_iter,
141862306a36Sopenharmony_ci					    &has_radar);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	return has_radar;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic void ath10k_recalc_radar_detection(struct ath10k *ar)
142462306a36Sopenharmony_ci{
142562306a36Sopenharmony_ci	int ret;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	ath10k_stop_cac(ar);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	if (!ath10k_mac_has_radar_enabled(ar))
143262306a36Sopenharmony_ci		return;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (ar->num_started_vdevs > 0)
143562306a36Sopenharmony_ci		return;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	ret = ath10k_start_cac(ar);
143862306a36Sopenharmony_ci	if (ret) {
143962306a36Sopenharmony_ci		/*
144062306a36Sopenharmony_ci		 * Not possible to start CAC on current channel so starting
144162306a36Sopenharmony_ci		 * radiation is not allowed, make this channel DFS_UNAVAILABLE
144262306a36Sopenharmony_ci		 * by indicating that radar was detected.
144362306a36Sopenharmony_ci		 */
144462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start CAC: %d\n", ret);
144562306a36Sopenharmony_ci		ieee80211_radar_detected(ar->hw);
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cistatic int ath10k_vdev_stop(struct ath10k_vif *arvif)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
145262306a36Sopenharmony_ci	int ret;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	reinit_completion(&ar->vdev_setup_done);
145762306a36Sopenharmony_ci	reinit_completion(&ar->vdev_delete_done);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
146062306a36Sopenharmony_ci	if (ret) {
146162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop WMI vdev %i: %d\n",
146262306a36Sopenharmony_ci			    arvif->vdev_id, ret);
146362306a36Sopenharmony_ci		return ret;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	ret = ath10k_vdev_setup_sync(ar);
146762306a36Sopenharmony_ci	if (ret) {
146862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to synchronize setup for vdev %i: %d\n",
146962306a36Sopenharmony_ci			    arvif->vdev_id, ret);
147062306a36Sopenharmony_ci		return ret;
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	WARN_ON(ar->num_started_vdevs == 0);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (ar->num_started_vdevs != 0) {
147662306a36Sopenharmony_ci		ar->num_started_vdevs--;
147762306a36Sopenharmony_ci		ath10k_recalc_radar_detection(ar);
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	return ret;
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
148462306a36Sopenharmony_ci				     const struct cfg80211_chan_def *chandef,
148562306a36Sopenharmony_ci				     bool restart)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
148862306a36Sopenharmony_ci	struct wmi_vdev_start_request_arg arg = {};
148962306a36Sopenharmony_ci	int ret = 0;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	reinit_completion(&ar->vdev_setup_done);
149462306a36Sopenharmony_ci	reinit_completion(&ar->vdev_delete_done);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
149762306a36Sopenharmony_ci	arg.dtim_period = arvif->dtim_period;
149862306a36Sopenharmony_ci	arg.bcn_intval = arvif->beacon_interval;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	arg.channel.freq = chandef->chan->center_freq;
150162306a36Sopenharmony_ci	arg.channel.band_center_freq1 = chandef->center_freq1;
150262306a36Sopenharmony_ci	arg.channel.band_center_freq2 = chandef->center_freq2;
150362306a36Sopenharmony_ci	arg.channel.mode = chan_to_phymode(chandef);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	arg.channel.min_power = 0;
150662306a36Sopenharmony_ci	arg.channel.max_power = chandef->chan->max_power * 2;
150762306a36Sopenharmony_ci	arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
150862306a36Sopenharmony_ci	arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
151162306a36Sopenharmony_ci		arg.ssid = arvif->u.ap.ssid;
151262306a36Sopenharmony_ci		arg.ssid_len = arvif->u.ap.ssid_len;
151362306a36Sopenharmony_ci		arg.hidden_ssid = arvif->u.ap.hidden_ssid;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		/* For now allow DFS for AP mode */
151662306a36Sopenharmony_ci		arg.channel.chan_radar =
151762306a36Sopenharmony_ci			!!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
151862306a36Sopenharmony_ci	} else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
151962306a36Sopenharmony_ci		arg.ssid = arvif->vif->cfg.ssid;
152062306a36Sopenharmony_ci		arg.ssid_len = arvif->vif->cfg.ssid_len;
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
152462306a36Sopenharmony_ci		   "mac vdev %d start center_freq %d phymode %s\n",
152562306a36Sopenharmony_ci		   arg.vdev_id, arg.channel.freq,
152662306a36Sopenharmony_ci		   ath10k_wmi_phymode_str(arg.channel.mode));
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	if (restart)
152962306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_restart(ar, &arg);
153062306a36Sopenharmony_ci	else
153162306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_start(ar, &arg);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (ret) {
153462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start WMI vdev %i: %d\n",
153562306a36Sopenharmony_ci			    arg.vdev_id, ret);
153662306a36Sopenharmony_ci		return ret;
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	ret = ath10k_vdev_setup_sync(ar);
154062306a36Sopenharmony_ci	if (ret) {
154162306a36Sopenharmony_ci		ath10k_warn(ar,
154262306a36Sopenharmony_ci			    "failed to synchronize setup for vdev %i restart %d: %d\n",
154362306a36Sopenharmony_ci			    arg.vdev_id, restart, ret);
154462306a36Sopenharmony_ci		return ret;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ar->num_started_vdevs++;
154862306a36Sopenharmony_ci	ath10k_recalc_radar_detection(ar);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	return ret;
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int ath10k_vdev_start(struct ath10k_vif *arvif,
155462306a36Sopenharmony_ci			     const struct cfg80211_chan_def *def)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	return ath10k_vdev_start_restart(arvif, def, false);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic int ath10k_vdev_restart(struct ath10k_vif *arvif,
156062306a36Sopenharmony_ci			       const struct cfg80211_chan_def *def)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	return ath10k_vdev_start_restart(arvif, def, true);
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif,
156662306a36Sopenharmony_ci				       struct sk_buff *bcn)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
156962306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
157062306a36Sopenharmony_ci	const u8 *p2p_ie;
157162306a36Sopenharmony_ci	int ret;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (arvif->vif->type != NL80211_IFTYPE_AP || !arvif->vif->p2p)
157462306a36Sopenharmony_ci		return 0;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	mgmt = (void *)bcn->data;
157762306a36Sopenharmony_ci	p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
157862306a36Sopenharmony_ci					 mgmt->u.beacon.variable,
157962306a36Sopenharmony_ci					 bcn->len - (mgmt->u.beacon.variable -
158062306a36Sopenharmony_ci						     bcn->data));
158162306a36Sopenharmony_ci	if (!p2p_ie)
158262306a36Sopenharmony_ci		return -ENOENT;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
158562306a36Sopenharmony_ci	if (ret) {
158662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
158762306a36Sopenharmony_ci			    arvif->vdev_id, ret);
158862306a36Sopenharmony_ci		return ret;
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	return 0;
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
159562306a36Sopenharmony_ci				       u8 oui_type, size_t ie_offset)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	size_t len;
159862306a36Sopenharmony_ci	const u8 *next;
159962306a36Sopenharmony_ci	const u8 *end;
160062306a36Sopenharmony_ci	u8 *ie;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (WARN_ON(skb->len < ie_offset))
160362306a36Sopenharmony_ci		return -EINVAL;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
160662306a36Sopenharmony_ci					   skb->data + ie_offset,
160762306a36Sopenharmony_ci					   skb->len - ie_offset);
160862306a36Sopenharmony_ci	if (!ie)
160962306a36Sopenharmony_ci		return -ENOENT;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	len = ie[1] + 2;
161262306a36Sopenharmony_ci	end = skb->data + skb->len;
161362306a36Sopenharmony_ci	next = ie + len;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	if (WARN_ON(next > end))
161662306a36Sopenharmony_ci		return -EINVAL;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	memmove(ie, next, end - next);
161962306a36Sopenharmony_ci	skb_trim(skb, skb->len - len);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return 0;
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_cistatic int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
162562306a36Sopenharmony_ci{
162662306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
162762306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
162862306a36Sopenharmony_ci	struct ieee80211_vif *vif = arvif->vif;
162962306a36Sopenharmony_ci	struct ieee80211_mutable_offsets offs = {};
163062306a36Sopenharmony_ci	struct sk_buff *bcn;
163162306a36Sopenharmony_ci	int ret;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
163462306a36Sopenharmony_ci		return 0;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
163762306a36Sopenharmony_ci	    arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
163862306a36Sopenharmony_ci		return 0;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0);
164162306a36Sopenharmony_ci	if (!bcn) {
164262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to get beacon template from mac80211\n");
164362306a36Sopenharmony_ci		return -EPERM;
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn);
164762306a36Sopenharmony_ci	if (ret) {
164862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
164962306a36Sopenharmony_ci		kfree_skb(bcn);
165062306a36Sopenharmony_ci		return ret;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	/* P2P IE is inserted by firmware automatically (as configured above)
165462306a36Sopenharmony_ci	 * so remove it from the base beacon template to avoid duplicate P2P
165562306a36Sopenharmony_ci	 * IEs in beacon frames.
165662306a36Sopenharmony_ci	 */
165762306a36Sopenharmony_ci	ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
165862306a36Sopenharmony_ci				    offsetof(struct ieee80211_mgmt,
165962306a36Sopenharmony_ci					     u.beacon.variable));
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0,
166262306a36Sopenharmony_ci				  0, NULL, 0);
166362306a36Sopenharmony_ci	kfree_skb(bcn);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (ret) {
166662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit beacon template command: %d\n",
166762306a36Sopenharmony_ci			    ret);
166862306a36Sopenharmony_ci		return ret;
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	return 0;
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cistatic int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
167562306a36Sopenharmony_ci{
167662306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
167762306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
167862306a36Sopenharmony_ci	struct ieee80211_vif *vif = arvif->vif;
167962306a36Sopenharmony_ci	struct sk_buff *prb;
168062306a36Sopenharmony_ci	int ret;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
168362306a36Sopenharmony_ci		return 0;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
168662306a36Sopenharmony_ci		return 0;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	 /* For mesh, probe response and beacon share the same template */
168962306a36Sopenharmony_ci	if (ieee80211_vif_is_mesh(vif))
169062306a36Sopenharmony_ci		return 0;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	prb = ieee80211_proberesp_get(hw, vif);
169362306a36Sopenharmony_ci	if (!prb) {
169462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
169562306a36Sopenharmony_ci		return -EPERM;
169662306a36Sopenharmony_ci	}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb);
169962306a36Sopenharmony_ci	kfree_skb(prb);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (ret) {
170262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit probe resp template command: %d\n",
170362306a36Sopenharmony_ci			    ret);
170462306a36Sopenharmony_ci		return ret;
170562306a36Sopenharmony_ci	}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	return 0;
170862306a36Sopenharmony_ci}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_cistatic int ath10k_mac_vif_fix_hidden_ssid(struct ath10k_vif *arvif)
171162306a36Sopenharmony_ci{
171262306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
171362306a36Sopenharmony_ci	struct cfg80211_chan_def def;
171462306a36Sopenharmony_ci	int ret;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	/* When originally vdev is started during assign_vif_chanctx() some
171762306a36Sopenharmony_ci	 * information is missing, notably SSID. Firmware revisions with beacon
171862306a36Sopenharmony_ci	 * offloading require the SSID to be provided during vdev (re)start to
171962306a36Sopenharmony_ci	 * handle hidden SSID properly.
172062306a36Sopenharmony_ci	 *
172162306a36Sopenharmony_ci	 * Vdev restart must be done after vdev has been both started and
172262306a36Sopenharmony_ci	 * upped. Otherwise some firmware revisions (at least 10.2) fail to
172362306a36Sopenharmony_ci	 * deliver vdev restart response event causing timeouts during vdev
172462306a36Sopenharmony_ci	 * syncing in ath10k.
172562306a36Sopenharmony_ci	 *
172662306a36Sopenharmony_ci	 * Note: The vdev down/up and template reinstallation could be skipped
172762306a36Sopenharmony_ci	 * since only wmi-tlv firmware are known to have beacon offload and
172862306a36Sopenharmony_ci	 * wmi-tlv doesn't seem to misbehave like 10.2 wrt vdev restart
172962306a36Sopenharmony_ci	 * response delivery. It's probably more robust to keep it as is.
173062306a36Sopenharmony_ci	 */
173162306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
173262306a36Sopenharmony_ci		return 0;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	if (WARN_ON(!arvif->is_started))
173562306a36Sopenharmony_ci		return -EINVAL;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	if (WARN_ON(!arvif->is_up))
173862306a36Sopenharmony_ci		return -EINVAL;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(arvif->vif, &def)))
174162306a36Sopenharmony_ci		return -EINVAL;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
174462306a36Sopenharmony_ci	if (ret) {
174562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to bring down ap vdev %i: %d\n",
174662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
174762306a36Sopenharmony_ci		return ret;
174862306a36Sopenharmony_ci	}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* Vdev down reset beacon & presp templates. Reinstall them. Otherwise
175162306a36Sopenharmony_ci	 * firmware will crash upon vdev up.
175262306a36Sopenharmony_ci	 */
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	ret = ath10k_mac_setup_bcn_tmpl(arvif);
175562306a36Sopenharmony_ci	if (ret) {
175662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update beacon template: %d\n", ret);
175762306a36Sopenharmony_ci		return ret;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	ret = ath10k_mac_setup_prb_tmpl(arvif);
176162306a36Sopenharmony_ci	if (ret) {
176262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update presp template: %d\n", ret);
176362306a36Sopenharmony_ci		return ret;
176462306a36Sopenharmony_ci	}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	ret = ath10k_vdev_restart(arvif, &def);
176762306a36Sopenharmony_ci	if (ret) {
176862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to restart ap vdev %i: %d\n",
176962306a36Sopenharmony_ci			    arvif->vdev_id, ret);
177062306a36Sopenharmony_ci		return ret;
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
177462306a36Sopenharmony_ci				 arvif->bssid);
177562306a36Sopenharmony_ci	if (ret) {
177662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to bring up ap vdev %i: %d\n",
177762306a36Sopenharmony_ci			    arvif->vdev_id, ret);
177862306a36Sopenharmony_ci		return ret;
177962306a36Sopenharmony_ci	}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	return 0;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic void ath10k_control_beaconing(struct ath10k_vif *arvif,
178562306a36Sopenharmony_ci				     struct ieee80211_bss_conf *info)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
178862306a36Sopenharmony_ci	int ret = 0;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (!info->enable_beacon) {
179362306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
179462306a36Sopenharmony_ci		if (ret)
179562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to down vdev_id %i: %d\n",
179662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci		arvif->is_up = false;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci		spin_lock_bh(&arvif->ar->data_lock);
180162306a36Sopenharmony_ci		ath10k_mac_vif_beacon_free(arvif);
180262306a36Sopenharmony_ci		spin_unlock_bh(&arvif->ar->data_lock);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci		return;
180562306a36Sopenharmony_ci	}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	arvif->tx_seq_no = 0x1000;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	arvif->aid = 0;
181062306a36Sopenharmony_ci	ether_addr_copy(arvif->bssid, info->bssid);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
181362306a36Sopenharmony_ci				 arvif->bssid);
181462306a36Sopenharmony_ci	if (ret) {
181562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to bring up vdev %d: %i\n",
181662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
181762306a36Sopenharmony_ci		return;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	arvif->is_up = true;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	ret = ath10k_mac_vif_fix_hidden_ssid(arvif);
182362306a36Sopenharmony_ci	if (ret) {
182462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to fix hidden ssid for vdev %i, expect trouble: %d\n",
182562306a36Sopenharmony_ci			    arvif->vdev_id, ret);
182662306a36Sopenharmony_ci		return;
182762306a36Sopenharmony_ci	}
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic void ath10k_control_ibss(struct ath10k_vif *arvif,
183362306a36Sopenharmony_ci				struct ieee80211_vif *vif)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
183662306a36Sopenharmony_ci	u32 vdev_param;
183762306a36Sopenharmony_ci	int ret = 0;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (!vif->cfg.ibss_joined) {
184262306a36Sopenharmony_ci		if (is_zero_ether_addr(arvif->bssid))
184362306a36Sopenharmony_ci			return;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		eth_zero_addr(arvif->bssid);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		return;
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	vdev_param = arvif->ar->wmi.vdev_param->atim_window;
185162306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
185262306a36Sopenharmony_ci					ATH10K_DEFAULT_ATIM);
185362306a36Sopenharmony_ci	if (ret)
185462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set IBSS ATIM for vdev %d: %d\n",
185562306a36Sopenharmony_ci			    arvif->vdev_id, ret);
185662306a36Sopenharmony_ci}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_cistatic int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
186162306a36Sopenharmony_ci	u32 param;
186262306a36Sopenharmony_ci	u32 value;
186362306a36Sopenharmony_ci	int ret;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (arvif->u.sta.uapsd)
186862306a36Sopenharmony_ci		value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER;
186962306a36Sopenharmony_ci	else
187062306a36Sopenharmony_ci		value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
187362306a36Sopenharmony_ci	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value);
187462306a36Sopenharmony_ci	if (ret) {
187562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n",
187662306a36Sopenharmony_ci			    value, arvif->vdev_id, ret);
187762306a36Sopenharmony_ci		return ret;
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	return 0;
188162306a36Sopenharmony_ci}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_cistatic int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
188662306a36Sopenharmony_ci	u32 param;
188762306a36Sopenharmony_ci	u32 value;
188862306a36Sopenharmony_ci	int ret;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (arvif->u.sta.uapsd)
189362306a36Sopenharmony_ci		value = WMI_STA_PS_PSPOLL_COUNT_UAPSD;
189462306a36Sopenharmony_ci	else
189562306a36Sopenharmony_ci		value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
189862306a36Sopenharmony_ci	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
189962306a36Sopenharmony_ci					  param, value);
190062306a36Sopenharmony_ci	if (ret) {
190162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n",
190262306a36Sopenharmony_ci			    value, arvif->vdev_id, ret);
190362306a36Sopenharmony_ci		return ret;
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	return 0;
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cistatic int ath10k_mac_num_vifs_started(struct ath10k *ar)
191062306a36Sopenharmony_ci{
191162306a36Sopenharmony_ci	struct ath10k_vif *arvif;
191262306a36Sopenharmony_ci	int num = 0;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list)
191762306a36Sopenharmony_ci		if (arvif->is_started)
191862306a36Sopenharmony_ci			num++;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	return num;
192162306a36Sopenharmony_ci}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_cistatic int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
192462306a36Sopenharmony_ci{
192562306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
192662306a36Sopenharmony_ci	struct ieee80211_vif *vif = arvif->vif;
192762306a36Sopenharmony_ci	struct ieee80211_conf *conf = &ar->hw->conf;
192862306a36Sopenharmony_ci	enum wmi_sta_powersave_param param;
192962306a36Sopenharmony_ci	enum wmi_sta_ps_mode psmode;
193062306a36Sopenharmony_ci	int ret;
193162306a36Sopenharmony_ci	int ps_timeout;
193262306a36Sopenharmony_ci	bool enable_ps;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (arvif->vif->type != NL80211_IFTYPE_STATION)
193762306a36Sopenharmony_ci		return 0;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	enable_ps = arvif->ps;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	if (enable_ps && ath10k_mac_num_vifs_started(ar) > 1 &&
194262306a36Sopenharmony_ci	    !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
194362306a36Sopenharmony_ci		      ar->running_fw->fw_file.fw_features)) {
194462306a36Sopenharmony_ci		ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
194562306a36Sopenharmony_ci			    arvif->vdev_id);
194662306a36Sopenharmony_ci		enable_ps = false;
194762306a36Sopenharmony_ci	}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	if (!arvif->is_started) {
195062306a36Sopenharmony_ci		/* mac80211 can update vif powersave state while disconnected.
195162306a36Sopenharmony_ci		 * Firmware doesn't behave nicely and consumes more power than
195262306a36Sopenharmony_ci		 * necessary if PS is disabled on a non-started vdev. Hence
195362306a36Sopenharmony_ci		 * force-enable PS for non-running vdevs.
195462306a36Sopenharmony_ci		 */
195562306a36Sopenharmony_ci		psmode = WMI_STA_PS_MODE_ENABLED;
195662306a36Sopenharmony_ci	} else if (enable_ps) {
195762306a36Sopenharmony_ci		psmode = WMI_STA_PS_MODE_ENABLED;
195862306a36Sopenharmony_ci		param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci		ps_timeout = conf->dynamic_ps_timeout;
196162306a36Sopenharmony_ci		if (ps_timeout == 0) {
196262306a36Sopenharmony_ci			/* Firmware doesn't like 0 */
196362306a36Sopenharmony_ci			ps_timeout = ieee80211_tu_to_usec(
196462306a36Sopenharmony_ci				vif->bss_conf.beacon_int) / 1000;
196562306a36Sopenharmony_ci		}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
196862306a36Sopenharmony_ci						  ps_timeout);
196962306a36Sopenharmony_ci		if (ret) {
197062306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n",
197162306a36Sopenharmony_ci				    arvif->vdev_id, ret);
197262306a36Sopenharmony_ci			return ret;
197362306a36Sopenharmony_ci		}
197462306a36Sopenharmony_ci	} else {
197562306a36Sopenharmony_ci		psmode = WMI_STA_PS_MODE_DISABLED;
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
197962306a36Sopenharmony_ci		   arvif->vdev_id, psmode ? "enable" : "disable");
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
198262306a36Sopenharmony_ci	if (ret) {
198362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set PS Mode %d for vdev %d: %d\n",
198462306a36Sopenharmony_ci			    psmode, arvif->vdev_id, ret);
198562306a36Sopenharmony_ci		return ret;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	return 0;
198962306a36Sopenharmony_ci}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_cistatic int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
199462306a36Sopenharmony_ci	struct wmi_sta_keepalive_arg arg = {};
199562306a36Sopenharmony_ci	int ret;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
200062306a36Sopenharmony_ci		return 0;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map))
200362306a36Sopenharmony_ci		return 0;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* Some firmware revisions have a bug and ignore the `enabled` field.
200662306a36Sopenharmony_ci	 * Instead use the interval to disable the keepalive.
200762306a36Sopenharmony_ci	 */
200862306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
200962306a36Sopenharmony_ci	arg.enabled = 1;
201062306a36Sopenharmony_ci	arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
201162306a36Sopenharmony_ci	arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	ret = ath10k_wmi_sta_keepalive(ar, &arg);
201462306a36Sopenharmony_ci	if (ret) {
201562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
201662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
201762306a36Sopenharmony_ci		return ret;
201862306a36Sopenharmony_ci	}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	return 0;
202162306a36Sopenharmony_ci}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_cistatic void ath10k_mac_vif_ap_csa_count_down(struct ath10k_vif *arvif)
202462306a36Sopenharmony_ci{
202562306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
202662306a36Sopenharmony_ci	struct ieee80211_vif *vif = arvif->vif;
202762306a36Sopenharmony_ci	int ret;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	lockdep_assert_held(&arvif->ar->conf_mutex);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	if (WARN_ON(!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)))
203262306a36Sopenharmony_ci		return;
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
203562306a36Sopenharmony_ci		return;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	if (!vif->bss_conf.csa_active)
203862306a36Sopenharmony_ci		return;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	if (!arvif->is_up)
204162306a36Sopenharmony_ci		return;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (!ieee80211_beacon_cntdwn_is_complete(vif)) {
204462306a36Sopenharmony_ci		ieee80211_beacon_update_cntdwn(vif);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		ret = ath10k_mac_setup_bcn_tmpl(arvif);
204762306a36Sopenharmony_ci		if (ret)
204862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
204962306a36Sopenharmony_ci				    ret);
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci		ret = ath10k_mac_setup_prb_tmpl(arvif);
205262306a36Sopenharmony_ci		if (ret)
205362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
205462306a36Sopenharmony_ci				    ret);
205562306a36Sopenharmony_ci	} else {
205662306a36Sopenharmony_ci		ieee80211_csa_finish(vif);
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_cistatic void ath10k_mac_vif_ap_csa_work(struct work_struct *work)
206162306a36Sopenharmony_ci{
206262306a36Sopenharmony_ci	struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
206362306a36Sopenharmony_ci						ap_csa_work);
206462306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
206762306a36Sopenharmony_ci	ath10k_mac_vif_ap_csa_count_down(arvif);
206862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_cistatic void ath10k_mac_handle_beacon_iter(void *data, u8 *mac,
207262306a36Sopenharmony_ci					  struct ieee80211_vif *vif)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct sk_buff *skb = data;
207562306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (void *)skb->data;
207662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION)
207962306a36Sopenharmony_ci		return;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	if (!ether_addr_equal(mgmt->bssid, vif->bss_conf.bssid))
208262306a36Sopenharmony_ci		return;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	cancel_delayed_work(&arvif->connection_loss_work);
208562306a36Sopenharmony_ci}
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_civoid ath10k_mac_handle_beacon(struct ath10k *ar, struct sk_buff *skb)
208862306a36Sopenharmony_ci{
208962306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(ar->hw,
209062306a36Sopenharmony_ci						   ATH10K_ITER_NORMAL_FLAGS,
209162306a36Sopenharmony_ci						   ath10k_mac_handle_beacon_iter,
209262306a36Sopenharmony_ci						   skb);
209362306a36Sopenharmony_ci}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_cistatic void ath10k_mac_handle_beacon_miss_iter(void *data, u8 *mac,
209662306a36Sopenharmony_ci					       struct ieee80211_vif *vif)
209762306a36Sopenharmony_ci{
209862306a36Sopenharmony_ci	u32 *vdev_id = data;
209962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
210062306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
210162306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	if (arvif->vdev_id != *vdev_id)
210462306a36Sopenharmony_ci		return;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	if (!arvif->is_up)
210762306a36Sopenharmony_ci		return;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	ieee80211_beacon_loss(vif);
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	/* Firmware doesn't report beacon loss events repeatedly. If AP probe
211262306a36Sopenharmony_ci	 * (done by mac80211) succeeds but beacons do not resume then it
211362306a36Sopenharmony_ci	 * doesn't make sense to continue operation. Queue connection loss work
211462306a36Sopenharmony_ci	 * which can be cancelled when beacon is received.
211562306a36Sopenharmony_ci	 */
211662306a36Sopenharmony_ci	ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work,
211762306a36Sopenharmony_ci				     ATH10K_CONNECTION_LOSS_HZ);
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_civoid ath10k_mac_handle_beacon_miss(struct ath10k *ar, u32 vdev_id)
212162306a36Sopenharmony_ci{
212262306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(ar->hw,
212362306a36Sopenharmony_ci						   ATH10K_ITER_NORMAL_FLAGS,
212462306a36Sopenharmony_ci						   ath10k_mac_handle_beacon_miss_iter,
212562306a36Sopenharmony_ci						   &vdev_id);
212662306a36Sopenharmony_ci}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_cistatic void ath10k_mac_vif_sta_connection_loss_work(struct work_struct *work)
212962306a36Sopenharmony_ci{
213062306a36Sopenharmony_ci	struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
213162306a36Sopenharmony_ci						connection_loss_work.work);
213262306a36Sopenharmony_ci	struct ieee80211_vif *vif = arvif->vif;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (!arvif->is_up)
213562306a36Sopenharmony_ci		return;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	ieee80211_connection_loss(vif);
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/**********************/
214162306a36Sopenharmony_ci/* Station management */
214262306a36Sopenharmony_ci/**********************/
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar,
214562306a36Sopenharmony_ci					     struct ieee80211_vif *vif)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	/* Some firmware revisions have unstable STA powersave when listen
214862306a36Sopenharmony_ci	 * interval is set too high (e.g. 5). The symptoms are firmware doesn't
214962306a36Sopenharmony_ci	 * generate NullFunc frames properly even if buffered frames have been
215062306a36Sopenharmony_ci	 * indicated in Beacon TIM. Firmware would seldom wake up to pull
215162306a36Sopenharmony_ci	 * buffered frames. Often pinging the device from AP would simply fail.
215262306a36Sopenharmony_ci	 *
215362306a36Sopenharmony_ci	 * As a workaround set it to 1.
215462306a36Sopenharmony_ci	 */
215562306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION)
215662306a36Sopenharmony_ci		return 1;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	return ar->hw->conf.listen_interval;
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_basic(struct ath10k *ar,
216262306a36Sopenharmony_ci				      struct ieee80211_vif *vif,
216362306a36Sopenharmony_ci				      struct ieee80211_sta *sta,
216462306a36Sopenharmony_ci				      struct wmi_peer_assoc_complete_arg *arg)
216562306a36Sopenharmony_ci{
216662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
216762306a36Sopenharmony_ci	u32 aid;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION)
217262306a36Sopenharmony_ci		aid = vif->cfg.aid;
217362306a36Sopenharmony_ci	else
217462306a36Sopenharmony_ci		aid = sta->aid;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	ether_addr_copy(arg->addr, sta->addr);
217762306a36Sopenharmony_ci	arg->vdev_id = arvif->vdev_id;
217862306a36Sopenharmony_ci	arg->peer_aid = aid;
217962306a36Sopenharmony_ci	arg->peer_flags |= arvif->ar->wmi.peer_flags->auth;
218062306a36Sopenharmony_ci	arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
218162306a36Sopenharmony_ci	arg->peer_num_spatial_streams = 1;
218262306a36Sopenharmony_ci	arg->peer_caps = vif->bss_conf.assoc_capability;
218362306a36Sopenharmony_ci}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
218662306a36Sopenharmony_ci				       struct ieee80211_vif *vif,
218762306a36Sopenharmony_ci				       struct ieee80211_sta *sta,
218862306a36Sopenharmony_ci				       struct wmi_peer_assoc_complete_arg *arg)
218962306a36Sopenharmony_ci{
219062306a36Sopenharmony_ci	struct ieee80211_bss_conf *info = &vif->bss_conf;
219162306a36Sopenharmony_ci	struct cfg80211_chan_def def;
219262306a36Sopenharmony_ci	struct cfg80211_bss *bss;
219362306a36Sopenharmony_ci	const u8 *rsnie = NULL;
219462306a36Sopenharmony_ci	const u8 *wpaie = NULL;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
219962306a36Sopenharmony_ci		return;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid,
220262306a36Sopenharmony_ci			       vif->cfg.ssid_len ? vif->cfg.ssid : NULL,
220362306a36Sopenharmony_ci			       vif->cfg.ssid_len,
220462306a36Sopenharmony_ci			       IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
220562306a36Sopenharmony_ci	if (bss) {
220662306a36Sopenharmony_ci		const struct cfg80211_bss_ies *ies;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci		rcu_read_lock();
220962306a36Sopenharmony_ci		rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci		ies = rcu_dereference(bss->ies);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci		wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
221462306a36Sopenharmony_ci						WLAN_OUI_TYPE_MICROSOFT_WPA,
221562306a36Sopenharmony_ci						ies->data,
221662306a36Sopenharmony_ci						ies->len);
221762306a36Sopenharmony_ci		rcu_read_unlock();
221862306a36Sopenharmony_ci		cfg80211_put_bss(ar->hw->wiphy, bss);
221962306a36Sopenharmony_ci	}
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	/* FIXME: base on RSN IE/WPA IE is a correct idea? */
222262306a36Sopenharmony_ci	if (rsnie || wpaie) {
222362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
222462306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->need_ptk_4_way;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	if (wpaie) {
222862306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
222962306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way;
223062306a36Sopenharmony_ci	}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	if (sta->mfp &&
223362306a36Sopenharmony_ci	    test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT,
223462306a36Sopenharmony_ci		     ar->running_fw->fw_file.fw_features)) {
223562306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->pmf;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_rates(struct ath10k *ar,
224062306a36Sopenharmony_ci				      struct ieee80211_vif *vif,
224162306a36Sopenharmony_ci				      struct ieee80211_sta *sta,
224262306a36Sopenharmony_ci				      struct wmi_peer_assoc_complete_arg *arg)
224362306a36Sopenharmony_ci{
224462306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
224562306a36Sopenharmony_ci	struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
224662306a36Sopenharmony_ci	struct cfg80211_chan_def def;
224762306a36Sopenharmony_ci	const struct ieee80211_supported_band *sband;
224862306a36Sopenharmony_ci	const struct ieee80211_rate *rates;
224962306a36Sopenharmony_ci	enum nl80211_band band;
225062306a36Sopenharmony_ci	u32 ratemask;
225162306a36Sopenharmony_ci	u8 rate;
225262306a36Sopenharmony_ci	int i;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
225762306a36Sopenharmony_ci		return;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	band = def.chan->band;
226062306a36Sopenharmony_ci	sband = ar->hw->wiphy->bands[band];
226162306a36Sopenharmony_ci	ratemask = sta->deflink.supp_rates[band];
226262306a36Sopenharmony_ci	ratemask &= arvif->bitrate_mask.control[band].legacy;
226362306a36Sopenharmony_ci	rates = sband->bitrates;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	rateset->num_rates = 0;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
226862306a36Sopenharmony_ci		if (!(ratemask & 1))
226962306a36Sopenharmony_ci			continue;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci		rate = ath10k_mac_bitrate_to_rate(rates->bitrate);
227262306a36Sopenharmony_ci		rateset->rates[rateset->num_rates] = rate;
227362306a36Sopenharmony_ci		rateset->num_rates++;
227462306a36Sopenharmony_ci	}
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_cistatic bool
227862306a36Sopenharmony_ciath10k_peer_assoc_h_ht_masked(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
227962306a36Sopenharmony_ci{
228062306a36Sopenharmony_ci	int nss;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	for (nss = 0; nss < IEEE80211_HT_MCS_MASK_LEN; nss++)
228362306a36Sopenharmony_ci		if (ht_mcs_mask[nss])
228462306a36Sopenharmony_ci			return false;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	return true;
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_cistatic bool
229062306a36Sopenharmony_ciath10k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
229162306a36Sopenharmony_ci{
229262306a36Sopenharmony_ci	int nss;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++)
229562306a36Sopenharmony_ci		if (vht_mcs_mask[nss])
229662306a36Sopenharmony_ci			return false;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	return true;
229962306a36Sopenharmony_ci}
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_ht(struct ath10k *ar,
230262306a36Sopenharmony_ci				   struct ieee80211_vif *vif,
230362306a36Sopenharmony_ci				   struct ieee80211_sta *sta,
230462306a36Sopenharmony_ci				   struct wmi_peer_assoc_complete_arg *arg)
230562306a36Sopenharmony_ci{
230662306a36Sopenharmony_ci	const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
230762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
230862306a36Sopenharmony_ci	struct cfg80211_chan_def def;
230962306a36Sopenharmony_ci	enum nl80211_band band;
231062306a36Sopenharmony_ci	const u8 *ht_mcs_mask;
231162306a36Sopenharmony_ci	const u16 *vht_mcs_mask;
231262306a36Sopenharmony_ci	int i, n;
231362306a36Sopenharmony_ci	u8 max_nss;
231462306a36Sopenharmony_ci	u32 stbc;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
231962306a36Sopenharmony_ci		return;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	if (!ht_cap->ht_supported)
232262306a36Sopenharmony_ci		return;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	band = def.chan->band;
232562306a36Sopenharmony_ci	ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
232662306a36Sopenharmony_ci	vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	if (ath10k_peer_assoc_h_ht_masked(ht_mcs_mask) &&
232962306a36Sopenharmony_ci	    ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
233062306a36Sopenharmony_ci		return;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	arg->peer_flags |= ar->wmi.peer_flags->ht;
233362306a36Sopenharmony_ci	arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
233462306a36Sopenharmony_ci				    ht_cap->ampdu_factor)) - 1;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	arg->peer_mpdu_density =
233762306a36Sopenharmony_ci		ath10k_parse_mpdudensity(ht_cap->ampdu_density);
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	arg->peer_ht_caps = ht_cap->cap;
234062306a36Sopenharmony_ci	arg->peer_rate_caps |= WMI_RC_HT_FLAG;
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
234362306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->ldbc;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) {
234662306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->bw40;
234762306a36Sopenharmony_ci		arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
234862306a36Sopenharmony_ci	}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) {
235162306a36Sopenharmony_ci		if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
235262306a36Sopenharmony_ci			arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci		if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
235562306a36Sopenharmony_ci			arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
235662306a36Sopenharmony_ci	}
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
235962306a36Sopenharmony_ci		arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
236062306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->stbc;
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
236462306a36Sopenharmony_ci		stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
236562306a36Sopenharmony_ci		stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
236662306a36Sopenharmony_ci		stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
236762306a36Sopenharmony_ci		arg->peer_rate_caps |= stbc;
236862306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->stbc;
236962306a36Sopenharmony_ci	}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
237262306a36Sopenharmony_ci		arg->peer_rate_caps |= WMI_RC_TS_FLAG;
237362306a36Sopenharmony_ci	else if (ht_cap->mcs.rx_mask[1])
237462306a36Sopenharmony_ci		arg->peer_rate_caps |= WMI_RC_DS_FLAG;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++)
237762306a36Sopenharmony_ci		if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) &&
237862306a36Sopenharmony_ci		    (ht_mcs_mask[i / 8] & BIT(i % 8))) {
237962306a36Sopenharmony_ci			max_nss = (i / 8) + 1;
238062306a36Sopenharmony_ci			arg->peer_ht_rates.rates[n++] = i;
238162306a36Sopenharmony_ci		}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	/*
238462306a36Sopenharmony_ci	 * This is a workaround for HT-enabled STAs which break the spec
238562306a36Sopenharmony_ci	 * and have no HT capabilities RX mask (no HT RX MCS map).
238662306a36Sopenharmony_ci	 *
238762306a36Sopenharmony_ci	 * As per spec, in section 20.3.5 Modulation and coding scheme (MCS),
238862306a36Sopenharmony_ci	 * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs.
238962306a36Sopenharmony_ci	 *
239062306a36Sopenharmony_ci	 * Firmware asserts if such situation occurs.
239162306a36Sopenharmony_ci	 */
239262306a36Sopenharmony_ci	if (n == 0) {
239362306a36Sopenharmony_ci		arg->peer_ht_rates.num_rates = 8;
239462306a36Sopenharmony_ci		for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
239562306a36Sopenharmony_ci			arg->peer_ht_rates.rates[i] = i;
239662306a36Sopenharmony_ci	} else {
239762306a36Sopenharmony_ci		arg->peer_ht_rates.num_rates = n;
239862306a36Sopenharmony_ci		arg->peer_num_spatial_streams = min(sta->deflink.rx_nss,
239962306a36Sopenharmony_ci						    max_nss);
240062306a36Sopenharmony_ci	}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
240362306a36Sopenharmony_ci		   arg->addr,
240462306a36Sopenharmony_ci		   arg->peer_ht_rates.num_rates,
240562306a36Sopenharmony_ci		   arg->peer_num_spatial_streams);
240662306a36Sopenharmony_ci}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_cistatic int ath10k_peer_assoc_qos_ap(struct ath10k *ar,
240962306a36Sopenharmony_ci				    struct ath10k_vif *arvif,
241062306a36Sopenharmony_ci				    struct ieee80211_sta *sta)
241162306a36Sopenharmony_ci{
241262306a36Sopenharmony_ci	u32 uapsd = 0;
241362306a36Sopenharmony_ci	u32 max_sp = 0;
241462306a36Sopenharmony_ci	int ret = 0;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	if (sta->wme && sta->uapsd_queues) {
241962306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
242062306a36Sopenharmony_ci			   sta->uapsd_queues, sta->max_sp);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
242362306a36Sopenharmony_ci			uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
242462306a36Sopenharmony_ci				 WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
242562306a36Sopenharmony_ci		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
242662306a36Sopenharmony_ci			uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
242762306a36Sopenharmony_ci				 WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
242862306a36Sopenharmony_ci		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
242962306a36Sopenharmony_ci			uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
243062306a36Sopenharmony_ci				 WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
243162306a36Sopenharmony_ci		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
243262306a36Sopenharmony_ci			uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
243362306a36Sopenharmony_ci				 WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci		if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
243662306a36Sopenharmony_ci			max_sp = sta->max_sp;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
243962306a36Sopenharmony_ci						 sta->addr,
244062306a36Sopenharmony_ci						 WMI_AP_PS_PEER_PARAM_UAPSD,
244162306a36Sopenharmony_ci						 uapsd);
244262306a36Sopenharmony_ci		if (ret) {
244362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set ap ps peer param uapsd for vdev %i: %d\n",
244462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
244562306a36Sopenharmony_ci			return ret;
244662306a36Sopenharmony_ci		}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
244962306a36Sopenharmony_ci						 sta->addr,
245062306a36Sopenharmony_ci						 WMI_AP_PS_PEER_PARAM_MAX_SP,
245162306a36Sopenharmony_ci						 max_sp);
245262306a36Sopenharmony_ci		if (ret) {
245362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set ap ps peer param max sp for vdev %i: %d\n",
245462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
245562306a36Sopenharmony_ci			return ret;
245662306a36Sopenharmony_ci		}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci		/* TODO setup this based on STA listen interval and
245962306a36Sopenharmony_ci		 * beacon interval. Currently we don't know
246062306a36Sopenharmony_ci		 * sta->listen_interval - mac80211 patch required.
246162306a36Sopenharmony_ci		 * Currently use 10 seconds
246262306a36Sopenharmony_ci		 */
246362306a36Sopenharmony_ci		ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
246462306a36Sopenharmony_ci						 WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
246562306a36Sopenharmony_ci						 10);
246662306a36Sopenharmony_ci		if (ret) {
246762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set ap ps peer param ageout time for vdev %i: %d\n",
246862306a36Sopenharmony_ci				    arvif->vdev_id, ret);
246962306a36Sopenharmony_ci			return ret;
247062306a36Sopenharmony_ci		}
247162306a36Sopenharmony_ci	}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	return 0;
247462306a36Sopenharmony_ci}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_cistatic u16
247762306a36Sopenharmony_ciath10k_peer_assoc_h_vht_limit(u16 tx_mcs_set,
247862306a36Sopenharmony_ci			      const u16 vht_mcs_limit[NL80211_VHT_NSS_MAX])
247962306a36Sopenharmony_ci{
248062306a36Sopenharmony_ci	int idx_limit;
248162306a36Sopenharmony_ci	int nss;
248262306a36Sopenharmony_ci	u16 mcs_map;
248362306a36Sopenharmony_ci	u16 mcs;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
248662306a36Sopenharmony_ci		mcs_map = ath10k_mac_get_max_vht_mcs_map(tx_mcs_set, nss) &
248762306a36Sopenharmony_ci			  vht_mcs_limit[nss];
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci		if (mcs_map)
249062306a36Sopenharmony_ci			idx_limit = fls(mcs_map) - 1;
249162306a36Sopenharmony_ci		else
249262306a36Sopenharmony_ci			idx_limit = -1;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci		switch (idx_limit) {
249562306a36Sopenharmony_ci		case 0:
249662306a36Sopenharmony_ci		case 1:
249762306a36Sopenharmony_ci		case 2:
249862306a36Sopenharmony_ci		case 3:
249962306a36Sopenharmony_ci		case 4:
250062306a36Sopenharmony_ci		case 5:
250162306a36Sopenharmony_ci		case 6:
250262306a36Sopenharmony_ci		default:
250362306a36Sopenharmony_ci			/* see ath10k_mac_can_set_bitrate_mask() */
250462306a36Sopenharmony_ci			WARN_ON(1);
250562306a36Sopenharmony_ci			fallthrough;
250662306a36Sopenharmony_ci		case -1:
250762306a36Sopenharmony_ci			mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED;
250862306a36Sopenharmony_ci			break;
250962306a36Sopenharmony_ci		case 7:
251062306a36Sopenharmony_ci			mcs = IEEE80211_VHT_MCS_SUPPORT_0_7;
251162306a36Sopenharmony_ci			break;
251262306a36Sopenharmony_ci		case 8:
251362306a36Sopenharmony_ci			mcs = IEEE80211_VHT_MCS_SUPPORT_0_8;
251462306a36Sopenharmony_ci			break;
251562306a36Sopenharmony_ci		case 9:
251662306a36Sopenharmony_ci			mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
251762306a36Sopenharmony_ci			break;
251862306a36Sopenharmony_ci		}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci		tx_mcs_set &= ~(0x3 << (nss * 2));
252162306a36Sopenharmony_ci		tx_mcs_set |= mcs << (nss * 2);
252262306a36Sopenharmony_ci	}
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	return tx_mcs_set;
252562306a36Sopenharmony_ci}
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_cistatic u32 get_160mhz_nss_from_maxrate(int rate)
252862306a36Sopenharmony_ci{
252962306a36Sopenharmony_ci	u32 nss;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	switch (rate) {
253262306a36Sopenharmony_ci	case 780:
253362306a36Sopenharmony_ci		nss = 1;
253462306a36Sopenharmony_ci		break;
253562306a36Sopenharmony_ci	case 1560:
253662306a36Sopenharmony_ci		nss = 2;
253762306a36Sopenharmony_ci		break;
253862306a36Sopenharmony_ci	case 2106:
253962306a36Sopenharmony_ci		nss = 3; /* not support MCS9 from spec*/
254062306a36Sopenharmony_ci		break;
254162306a36Sopenharmony_ci	case 3120:
254262306a36Sopenharmony_ci		nss = 4;
254362306a36Sopenharmony_ci		break;
254462306a36Sopenharmony_ci	default:
254562306a36Sopenharmony_ci		 nss = 1;
254662306a36Sopenharmony_ci	}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	return nss;
254962306a36Sopenharmony_ci}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_vht(struct ath10k *ar,
255262306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
255362306a36Sopenharmony_ci				    struct ieee80211_sta *sta,
255462306a36Sopenharmony_ci				    struct wmi_peer_assoc_complete_arg *arg)
255562306a36Sopenharmony_ci{
255662306a36Sopenharmony_ci	const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
255762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
255862306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
255962306a36Sopenharmony_ci	struct cfg80211_chan_def def;
256062306a36Sopenharmony_ci	enum nl80211_band band;
256162306a36Sopenharmony_ci	const u16 *vht_mcs_mask;
256262306a36Sopenharmony_ci	u8 ampdu_factor;
256362306a36Sopenharmony_ci	u8 max_nss, vht_mcs;
256462306a36Sopenharmony_ci	int i;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
256762306a36Sopenharmony_ci		return;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	if (!vht_cap->vht_supported)
257062306a36Sopenharmony_ci		return;
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	band = def.chan->band;
257362306a36Sopenharmony_ci	vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	if (ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
257662306a36Sopenharmony_ci		return;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	arg->peer_flags |= ar->wmi.peer_flags->vht;
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	if (def.chan->band == NL80211_BAND_2GHZ)
258162306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->vht_2g;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	arg->peer_vht_caps = vht_cap->cap;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	ampdu_factor = (vht_cap->cap &
258662306a36Sopenharmony_ci			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
258762306a36Sopenharmony_ci		       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	/* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
259062306a36Sopenharmony_ci	 * zero in VHT IE. Using it would result in degraded throughput.
259162306a36Sopenharmony_ci	 * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
259262306a36Sopenharmony_ci	 * it if VHT max_mpdu is smaller.
259362306a36Sopenharmony_ci	 */
259462306a36Sopenharmony_ci	arg->peer_max_mpdu = max(arg->peer_max_mpdu,
259562306a36Sopenharmony_ci				 (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
259662306a36Sopenharmony_ci					ampdu_factor)) - 1);
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
259962306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->bw80;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
260262306a36Sopenharmony_ci		arg->peer_flags |= ar->wmi.peer_flags->bw160;
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	/* Calculate peer NSS capability from VHT capabilities if STA
260562306a36Sopenharmony_ci	 * supports VHT.
260662306a36Sopenharmony_ci	 */
260762306a36Sopenharmony_ci	for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) {
260862306a36Sopenharmony_ci		vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
260962306a36Sopenharmony_ci			  (2 * i) & 3;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci		if ((vht_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) &&
261262306a36Sopenharmony_ci		    vht_mcs_mask[i])
261362306a36Sopenharmony_ci			max_nss = i + 1;
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci	arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss);
261662306a36Sopenharmony_ci	arg->peer_vht_rates.rx_max_rate =
261762306a36Sopenharmony_ci		__le16_to_cpu(vht_cap->vht_mcs.rx_highest);
261862306a36Sopenharmony_ci	arg->peer_vht_rates.rx_mcs_set =
261962306a36Sopenharmony_ci		__le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
262062306a36Sopenharmony_ci	arg->peer_vht_rates.tx_max_rate =
262162306a36Sopenharmony_ci		__le16_to_cpu(vht_cap->vht_mcs.tx_highest);
262262306a36Sopenharmony_ci	arg->peer_vht_rates.tx_mcs_set = ath10k_peer_assoc_h_vht_limit(
262362306a36Sopenharmony_ci		__le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask);
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	/* Configure bandwidth-NSS mapping to FW
262662306a36Sopenharmony_ci	 * for the chip's tx chains setting on 160Mhz bw
262762306a36Sopenharmony_ci	 */
262862306a36Sopenharmony_ci	if (arg->peer_phymode == MODE_11AC_VHT160 ||
262962306a36Sopenharmony_ci	    arg->peer_phymode == MODE_11AC_VHT80_80) {
263062306a36Sopenharmony_ci		u32 rx_nss;
263162306a36Sopenharmony_ci		u32 max_rate;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci		max_rate = arg->peer_vht_rates.rx_max_rate;
263462306a36Sopenharmony_ci		rx_nss = get_160mhz_nss_from_maxrate(max_rate);
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci		if (rx_nss == 0)
263762306a36Sopenharmony_ci			rx_nss = arg->peer_num_spatial_streams;
263862306a36Sopenharmony_ci		else
263962306a36Sopenharmony_ci			rx_nss = min(arg->peer_num_spatial_streams, rx_nss);
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci		max_rate = hw->vht160_mcs_tx_highest;
264262306a36Sopenharmony_ci		rx_nss = min(rx_nss, get_160mhz_nss_from_maxrate(max_rate));
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		arg->peer_bw_rxnss_override =
264562306a36Sopenharmony_ci			FIELD_PREP(WMI_PEER_NSS_MAP_ENABLE, 1) |
264662306a36Sopenharmony_ci			FIELD_PREP(WMI_PEER_NSS_160MHZ_MASK, (rx_nss - 1));
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci		if (arg->peer_phymode == MODE_11AC_VHT80_80) {
264962306a36Sopenharmony_ci			arg->peer_bw_rxnss_override |=
265062306a36Sopenharmony_ci			FIELD_PREP(WMI_PEER_NSS_80_80MHZ_MASK, (rx_nss - 1));
265162306a36Sopenharmony_ci		}
265262306a36Sopenharmony_ci	}
265362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
265462306a36Sopenharmony_ci		   "mac vht peer %pM max_mpdu %d flags 0x%x peer_rx_nss_override 0x%x\n",
265562306a36Sopenharmony_ci		   sta->addr, arg->peer_max_mpdu,
265662306a36Sopenharmony_ci		   arg->peer_flags, arg->peer_bw_rxnss_override);
265762306a36Sopenharmony_ci}
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_qos(struct ath10k *ar,
266062306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
266162306a36Sopenharmony_ci				    struct ieee80211_sta *sta,
266262306a36Sopenharmony_ci				    struct wmi_peer_assoc_complete_arg *arg)
266362306a36Sopenharmony_ci{
266462306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	switch (arvif->vdev_type) {
266762306a36Sopenharmony_ci	case WMI_VDEV_TYPE_AP:
266862306a36Sopenharmony_ci		if (sta->wme)
266962306a36Sopenharmony_ci			arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		if (sta->wme && sta->uapsd_queues) {
267262306a36Sopenharmony_ci			arg->peer_flags |= arvif->ar->wmi.peer_flags->apsd;
267362306a36Sopenharmony_ci			arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
267462306a36Sopenharmony_ci		}
267562306a36Sopenharmony_ci		break;
267662306a36Sopenharmony_ci	case WMI_VDEV_TYPE_STA:
267762306a36Sopenharmony_ci		if (sta->wme)
267862306a36Sopenharmony_ci			arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
267962306a36Sopenharmony_ci		break;
268062306a36Sopenharmony_ci	case WMI_VDEV_TYPE_IBSS:
268162306a36Sopenharmony_ci		if (sta->wme)
268262306a36Sopenharmony_ci			arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
268362306a36Sopenharmony_ci		break;
268462306a36Sopenharmony_ci	default:
268562306a36Sopenharmony_ci		break;
268662306a36Sopenharmony_ci	}
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
268962306a36Sopenharmony_ci		   sta->addr, !!(arg->peer_flags &
269062306a36Sopenharmony_ci		   arvif->ar->wmi.peer_flags->qos));
269162306a36Sopenharmony_ci}
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_cistatic bool ath10k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta)
269462306a36Sopenharmony_ci{
269562306a36Sopenharmony_ci	return sta->deflink.supp_rates[NL80211_BAND_2GHZ] >>
269662306a36Sopenharmony_ci	       ATH10K_MAC_FIRST_OFDM_RATE_IDX;
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_cistatic enum wmi_phy_mode ath10k_mac_get_phymode_vht(struct ath10k *ar,
270062306a36Sopenharmony_ci						    struct ieee80211_sta *sta)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) {
270562306a36Sopenharmony_ci		switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
270662306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
270762306a36Sopenharmony_ci			return MODE_11AC_VHT160;
270862306a36Sopenharmony_ci		case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
270962306a36Sopenharmony_ci			return MODE_11AC_VHT80_80;
271062306a36Sopenharmony_ci		default:
271162306a36Sopenharmony_ci			/* not sure if this is a valid case? */
271262306a36Sopenharmony_ci			return MODE_11AC_VHT160;
271362306a36Sopenharmony_ci		}
271462306a36Sopenharmony_ci	}
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
271762306a36Sopenharmony_ci		return MODE_11AC_VHT80;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
272062306a36Sopenharmony_ci		return MODE_11AC_VHT40;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20)
272362306a36Sopenharmony_ci		return MODE_11AC_VHT20;
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	return MODE_UNKNOWN;
272662306a36Sopenharmony_ci}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_cistatic void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
272962306a36Sopenharmony_ci					struct ieee80211_vif *vif,
273062306a36Sopenharmony_ci					struct ieee80211_sta *sta,
273162306a36Sopenharmony_ci					struct wmi_peer_assoc_complete_arg *arg)
273262306a36Sopenharmony_ci{
273362306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
273462306a36Sopenharmony_ci	struct cfg80211_chan_def def;
273562306a36Sopenharmony_ci	enum nl80211_band band;
273662306a36Sopenharmony_ci	const u8 *ht_mcs_mask;
273762306a36Sopenharmony_ci	const u16 *vht_mcs_mask;
273862306a36Sopenharmony_ci	enum wmi_phy_mode phymode = MODE_UNKNOWN;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
274162306a36Sopenharmony_ci		return;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	band = def.chan->band;
274462306a36Sopenharmony_ci	ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
274562306a36Sopenharmony_ci	vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	switch (band) {
274862306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
274962306a36Sopenharmony_ci		if (sta->deflink.vht_cap.vht_supported &&
275062306a36Sopenharmony_ci		    !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) {
275162306a36Sopenharmony_ci			if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
275262306a36Sopenharmony_ci				phymode = MODE_11AC_VHT40;
275362306a36Sopenharmony_ci			else
275462306a36Sopenharmony_ci				phymode = MODE_11AC_VHT20;
275562306a36Sopenharmony_ci		} else if (sta->deflink.ht_cap.ht_supported &&
275662306a36Sopenharmony_ci			   !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) {
275762306a36Sopenharmony_ci			if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
275862306a36Sopenharmony_ci				phymode = MODE_11NG_HT40;
275962306a36Sopenharmony_ci			else
276062306a36Sopenharmony_ci				phymode = MODE_11NG_HT20;
276162306a36Sopenharmony_ci		} else if (ath10k_mac_sta_has_ofdm_only(sta)) {
276262306a36Sopenharmony_ci			phymode = MODE_11G;
276362306a36Sopenharmony_ci		} else {
276462306a36Sopenharmony_ci			phymode = MODE_11B;
276562306a36Sopenharmony_ci		}
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci		break;
276862306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
276962306a36Sopenharmony_ci		/*
277062306a36Sopenharmony_ci		 * Check VHT first.
277162306a36Sopenharmony_ci		 */
277262306a36Sopenharmony_ci		if (sta->deflink.vht_cap.vht_supported &&
277362306a36Sopenharmony_ci		    !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) {
277462306a36Sopenharmony_ci			phymode = ath10k_mac_get_phymode_vht(ar, sta);
277562306a36Sopenharmony_ci		} else if (sta->deflink.ht_cap.ht_supported &&
277662306a36Sopenharmony_ci			   !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) {
277762306a36Sopenharmony_ci			if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40)
277862306a36Sopenharmony_ci				phymode = MODE_11NA_HT40;
277962306a36Sopenharmony_ci			else
278062306a36Sopenharmony_ci				phymode = MODE_11NA_HT20;
278162306a36Sopenharmony_ci		} else {
278262306a36Sopenharmony_ci			phymode = MODE_11A;
278362306a36Sopenharmony_ci		}
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci		break;
278662306a36Sopenharmony_ci	default:
278762306a36Sopenharmony_ci		break;
278862306a36Sopenharmony_ci	}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
279162306a36Sopenharmony_ci		   sta->addr, ath10k_wmi_phymode_str(phymode));
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci	arg->peer_phymode = phymode;
279462306a36Sopenharmony_ci	WARN_ON(phymode == MODE_UNKNOWN);
279562306a36Sopenharmony_ci}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_cistatic int ath10k_peer_assoc_prepare(struct ath10k *ar,
279862306a36Sopenharmony_ci				     struct ieee80211_vif *vif,
279962306a36Sopenharmony_ci				     struct ieee80211_sta *sta,
280062306a36Sopenharmony_ci				     struct wmi_peer_assoc_complete_arg *arg)
280162306a36Sopenharmony_ci{
280262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	memset(arg, 0, sizeof(*arg));
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
280762306a36Sopenharmony_ci	ath10k_peer_assoc_h_crypto(ar, vif, sta, arg);
280862306a36Sopenharmony_ci	ath10k_peer_assoc_h_rates(ar, vif, sta, arg);
280962306a36Sopenharmony_ci	ath10k_peer_assoc_h_ht(ar, vif, sta, arg);
281062306a36Sopenharmony_ci	ath10k_peer_assoc_h_phymode(ar, vif, sta, arg);
281162306a36Sopenharmony_ci	ath10k_peer_assoc_h_vht(ar, vif, sta, arg);
281262306a36Sopenharmony_ci	ath10k_peer_assoc_h_qos(ar, vif, sta, arg);
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	return 0;
281562306a36Sopenharmony_ci}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_cistatic const u32 ath10k_smps_map[] = {
281862306a36Sopenharmony_ci	[WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC,
281962306a36Sopenharmony_ci	[WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC,
282062306a36Sopenharmony_ci	[WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE,
282162306a36Sopenharmony_ci	[WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
282262306a36Sopenharmony_ci};
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_cistatic int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif,
282562306a36Sopenharmony_ci				  const u8 *addr,
282662306a36Sopenharmony_ci				  const struct ieee80211_sta_ht_cap *ht_cap)
282762306a36Sopenharmony_ci{
282862306a36Sopenharmony_ci	int smps;
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	if (!ht_cap->ht_supported)
283162306a36Sopenharmony_ci		return 0;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
283462306a36Sopenharmony_ci	smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci	if (smps >= ARRAY_SIZE(ath10k_smps_map))
283762306a36Sopenharmony_ci		return -EINVAL;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr,
284062306a36Sopenharmony_ci					 ar->wmi.peer_param->smps_state,
284162306a36Sopenharmony_ci					 ath10k_smps_map[smps]);
284262306a36Sopenharmony_ci}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_cistatic int ath10k_mac_vif_recalc_txbf(struct ath10k *ar,
284562306a36Sopenharmony_ci				      struct ieee80211_vif *vif,
284662306a36Sopenharmony_ci				      struct ieee80211_sta_vht_cap vht_cap)
284762306a36Sopenharmony_ci{
284862306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
284962306a36Sopenharmony_ci	int ret;
285062306a36Sopenharmony_ci	u32 param;
285162306a36Sopenharmony_ci	u32 value;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_AFTER_ASSOC)
285462306a36Sopenharmony_ci		return 0;
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	if (!(ar->vht_cap_info &
285762306a36Sopenharmony_ci	      (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
285862306a36Sopenharmony_ci	       IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
285962306a36Sopenharmony_ci	       IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
286062306a36Sopenharmony_ci	       IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
286162306a36Sopenharmony_ci		return 0;
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	param = ar->wmi.vdev_param->txbf;
286462306a36Sopenharmony_ci	value = 0;
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED))
286762306a36Sopenharmony_ci		return 0;
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	/* The following logic is correct. If a remote STA advertises support
287062306a36Sopenharmony_ci	 * for being a beamformer then we should enable us being a beamformee.
287162306a36Sopenharmony_ci	 */
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	if (ar->vht_cap_info &
287462306a36Sopenharmony_ci	    (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
287562306a36Sopenharmony_ci	     IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
287662306a36Sopenharmony_ci		if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
287762306a36Sopenharmony_ci			value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci		if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
288062306a36Sopenharmony_ci			value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE;
288162306a36Sopenharmony_ci	}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	if (ar->vht_cap_info &
288462306a36Sopenharmony_ci	    (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
288562306a36Sopenharmony_ci	     IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
288662306a36Sopenharmony_ci		if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
288762306a36Sopenharmony_ci			value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci		if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
289062306a36Sopenharmony_ci			value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER;
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE)
289462306a36Sopenharmony_ci		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER)
289762306a36Sopenharmony_ci		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value);
290062306a36Sopenharmony_ci	if (ret) {
290162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n",
290262306a36Sopenharmony_ci			    value, ret);
290362306a36Sopenharmony_ci		return ret;
290462306a36Sopenharmony_ci	}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	return 0;
290762306a36Sopenharmony_ci}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_cistatic bool ath10k_mac_is_connected(struct ath10k *ar)
291062306a36Sopenharmony_ci{
291162306a36Sopenharmony_ci	struct ath10k_vif *arvif;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list) {
291462306a36Sopenharmony_ci		if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA)
291562306a36Sopenharmony_ci			return true;
291662306a36Sopenharmony_ci	}
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	return false;
291962306a36Sopenharmony_ci}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_cistatic int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower)
292262306a36Sopenharmony_ci{
292362306a36Sopenharmony_ci	int ret;
292462306a36Sopenharmony_ci	u32 param;
292562306a36Sopenharmony_ci	int tx_power_2g, tx_power_5g;
292662306a36Sopenharmony_ci	bool connected;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	/* ath10k internally uses unit of 0.5 dBm so multiply by 2 */
293162306a36Sopenharmony_ci	tx_power_2g = txpower * 2;
293262306a36Sopenharmony_ci	tx_power_5g = txpower * 2;
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci	connected = ath10k_mac_is_connected(ar);
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	if (connected && ar->tx_power_2g_limit)
293762306a36Sopenharmony_ci		if (tx_power_2g > ar->tx_power_2g_limit)
293862306a36Sopenharmony_ci			tx_power_2g = ar->tx_power_2g_limit;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	if (connected && ar->tx_power_5g_limit)
294162306a36Sopenharmony_ci		if (tx_power_5g > ar->tx_power_5g_limit)
294262306a36Sopenharmony_ci			tx_power_5g = ar->tx_power_5g_limit;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac txpower 2g: %d, 5g: %d\n",
294562306a36Sopenharmony_ci		   tx_power_2g, tx_power_5g);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	param = ar->wmi.pdev_param->txpower_limit2g;
294862306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, tx_power_2g);
294962306a36Sopenharmony_ci	if (ret) {
295062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
295162306a36Sopenharmony_ci			    tx_power_2g, ret);
295262306a36Sopenharmony_ci		return ret;
295362306a36Sopenharmony_ci	}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	param = ar->wmi.pdev_param->txpower_limit5g;
295662306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, tx_power_5g);
295762306a36Sopenharmony_ci	if (ret) {
295862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
295962306a36Sopenharmony_ci			    tx_power_5g, ret);
296062306a36Sopenharmony_ci		return ret;
296162306a36Sopenharmony_ci	}
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_ci	return 0;
296462306a36Sopenharmony_ci}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_cistatic int ath10k_mac_txpower_recalc(struct ath10k *ar)
296762306a36Sopenharmony_ci{
296862306a36Sopenharmony_ci	struct ath10k_vif *arvif;
296962306a36Sopenharmony_ci	int ret, txpower = -1;
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list) {
297462306a36Sopenharmony_ci		/* txpower not initialized yet? */
297562306a36Sopenharmony_ci		if (arvif->txpower == INT_MIN)
297662306a36Sopenharmony_ci			continue;
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci		if (txpower == -1)
297962306a36Sopenharmony_ci			txpower = arvif->txpower;
298062306a36Sopenharmony_ci		else
298162306a36Sopenharmony_ci			txpower = min(txpower, arvif->txpower);
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	if (txpower == -1)
298562306a36Sopenharmony_ci		return 0;
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci	ret = ath10k_mac_txpower_setup(ar, txpower);
298862306a36Sopenharmony_ci	if (ret) {
298962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to setup tx power %d: %d\n",
299062306a36Sopenharmony_ci			    txpower, ret);
299162306a36Sopenharmony_ci		return ret;
299262306a36Sopenharmony_ci	}
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	return 0;
299562306a36Sopenharmony_ci}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_cistatic int ath10k_mac_set_sar_power(struct ath10k *ar)
299862306a36Sopenharmony_ci{
299962306a36Sopenharmony_ci	if (!ar->hw_params.dynamic_sar_support)
300062306a36Sopenharmony_ci		return -EOPNOTSUPP;
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	if (!ath10k_mac_is_connected(ar))
300362306a36Sopenharmony_ci		return 0;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	/* if connected, then arvif->txpower must be valid */
300662306a36Sopenharmony_ci	return ath10k_mac_txpower_recalc(ar);
300762306a36Sopenharmony_ci}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_cistatic int ath10k_mac_set_sar_specs(struct ieee80211_hw *hw,
301062306a36Sopenharmony_ci				    const struct cfg80211_sar_specs *sar)
301162306a36Sopenharmony_ci{
301262306a36Sopenharmony_ci	const struct cfg80211_sar_sub_specs *sub_specs;
301362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
301462306a36Sopenharmony_ci	u32 i;
301562306a36Sopenharmony_ci	int ret;
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	if (!ar->hw_params.dynamic_sar_support) {
302062306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
302162306a36Sopenharmony_ci		goto err;
302262306a36Sopenharmony_ci	}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	if (!sar || sar->type != NL80211_SAR_TYPE_POWER ||
302562306a36Sopenharmony_ci	    sar->num_sub_specs == 0) {
302662306a36Sopenharmony_ci		ret = -EINVAL;
302762306a36Sopenharmony_ci		goto err;
302862306a36Sopenharmony_ci	}
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	sub_specs = sar->sub_specs;
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	/* 0dbm is not a practical value for ath10k, so use 0
303362306a36Sopenharmony_ci	 * as no SAR limitation on it.
303462306a36Sopenharmony_ci	 */
303562306a36Sopenharmony_ci	ar->tx_power_2g_limit = 0;
303662306a36Sopenharmony_ci	ar->tx_power_5g_limit = 0;
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	/* note the power is in 0.25dbm unit, while ath10k uses
303962306a36Sopenharmony_ci	 * 0.5dbm unit.
304062306a36Sopenharmony_ci	 */
304162306a36Sopenharmony_ci	for (i = 0; i < sar->num_sub_specs; i++) {
304262306a36Sopenharmony_ci		if (sub_specs->freq_range_index == 0)
304362306a36Sopenharmony_ci			ar->tx_power_2g_limit = sub_specs->power / 2;
304462306a36Sopenharmony_ci		else if (sub_specs->freq_range_index == 1)
304562306a36Sopenharmony_ci			ar->tx_power_5g_limit = sub_specs->power / 2;
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci		sub_specs++;
304862306a36Sopenharmony_ci	}
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	ret = ath10k_mac_set_sar_power(ar);
305162306a36Sopenharmony_ci	if (ret) {
305262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set sar power: %d", ret);
305362306a36Sopenharmony_ci		goto err;
305462306a36Sopenharmony_ci	}
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_cierr:
305762306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
305862306a36Sopenharmony_ci	return ret;
305962306a36Sopenharmony_ci}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci/* can be called only in mac80211 callbacks due to `key_count` usage */
306262306a36Sopenharmony_cistatic void ath10k_bss_assoc(struct ieee80211_hw *hw,
306362306a36Sopenharmony_ci			     struct ieee80211_vif *vif,
306462306a36Sopenharmony_ci			     struct ieee80211_bss_conf *bss_conf)
306562306a36Sopenharmony_ci{
306662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
306762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
306862306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap ht_cap;
306962306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap vht_cap;
307062306a36Sopenharmony_ci	struct wmi_peer_assoc_complete_arg peer_arg;
307162306a36Sopenharmony_ci	struct ieee80211_sta *ap_sta;
307262306a36Sopenharmony_ci	int ret;
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n",
307762306a36Sopenharmony_ci		   arvif->vdev_id, arvif->bssid, arvif->aid);
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	rcu_read_lock();
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci	ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
308262306a36Sopenharmony_ci	if (!ap_sta) {
308362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to find station entry for bss %pM vdev %i\n",
308462306a36Sopenharmony_ci			    bss_conf->bssid, arvif->vdev_id);
308562306a36Sopenharmony_ci		rcu_read_unlock();
308662306a36Sopenharmony_ci		return;
308762306a36Sopenharmony_ci	}
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci	/* ap_sta must be accessed only within rcu section which must be left
309062306a36Sopenharmony_ci	 * before calling ath10k_setup_peer_smps() which might sleep.
309162306a36Sopenharmony_ci	 */
309262306a36Sopenharmony_ci	ht_cap = ap_sta->deflink.ht_cap;
309362306a36Sopenharmony_ci	vht_cap = ap_sta->deflink.vht_cap;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
309662306a36Sopenharmony_ci	if (ret) {
309762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n",
309862306a36Sopenharmony_ci			    bss_conf->bssid, arvif->vdev_id, ret);
309962306a36Sopenharmony_ci		rcu_read_unlock();
310062306a36Sopenharmony_ci		return;
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	rcu_read_unlock();
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
310662306a36Sopenharmony_ci	if (ret) {
310762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to run peer assoc for %pM vdev %i: %d\n",
310862306a36Sopenharmony_ci			    bss_conf->bssid, arvif->vdev_id, ret);
310962306a36Sopenharmony_ci		return;
311062306a36Sopenharmony_ci	}
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap);
311362306a36Sopenharmony_ci	if (ret) {
311462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to setup peer SMPS for vdev %i: %d\n",
311562306a36Sopenharmony_ci			    arvif->vdev_id, ret);
311662306a36Sopenharmony_ci		return;
311762306a36Sopenharmony_ci	}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
312062306a36Sopenharmony_ci	if (ret) {
312162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n",
312262306a36Sopenharmony_ci			    arvif->vdev_id, bss_conf->bssid, ret);
312362306a36Sopenharmony_ci		return;
312462306a36Sopenharmony_ci	}
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
312762306a36Sopenharmony_ci		   "mac vdev %d up (associated) bssid %pM aid %d\n",
312862306a36Sopenharmony_ci		   arvif->vdev_id, bss_conf->bssid, vif->cfg.aid);
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	WARN_ON(arvif->is_up);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	arvif->aid = vif->cfg.aid;
313362306a36Sopenharmony_ci	ether_addr_copy(arvif->bssid, bss_conf->bssid);
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar,
313662306a36Sopenharmony_ci					ar->wmi.pdev_param->peer_stats_info_enable, 1);
313762306a36Sopenharmony_ci	if (ret)
313862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable peer stats info: %d\n", ret);
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
314162306a36Sopenharmony_ci	if (ret) {
314262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set vdev %d up: %d\n",
314362306a36Sopenharmony_ci			    arvif->vdev_id, ret);
314462306a36Sopenharmony_ci		return;
314562306a36Sopenharmony_ci	}
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci	arvif->is_up = true;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	ath10k_mac_set_sar_power(ar);
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	/* Workaround: Some firmware revisions (tested with qca6174
315262306a36Sopenharmony_ci	 * WLAN.RM.2.0-00073) have buggy powersave state machine and must be
315362306a36Sopenharmony_ci	 * poked with peer param command.
315462306a36Sopenharmony_ci	 */
315562306a36Sopenharmony_ci	ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid,
315662306a36Sopenharmony_ci					ar->wmi.peer_param->dummy_var, 1);
315762306a36Sopenharmony_ci	if (ret) {
315862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n",
315962306a36Sopenharmony_ci			    arvif->bssid, arvif->vdev_id, ret);
316062306a36Sopenharmony_ci		return;
316162306a36Sopenharmony_ci	}
316262306a36Sopenharmony_ci}
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_cistatic void ath10k_bss_disassoc(struct ieee80211_hw *hw,
316562306a36Sopenharmony_ci				struct ieee80211_vif *vif)
316662306a36Sopenharmony_ci{
316762306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
316862306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
316962306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap vht_cap = {};
317062306a36Sopenharmony_ci	int ret;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n",
317562306a36Sopenharmony_ci		   arvif->vdev_id, arvif->bssid);
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
317862306a36Sopenharmony_ci	if (ret)
317962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to down vdev %i: %d\n",
318062306a36Sopenharmony_ci			    arvif->vdev_id, ret);
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	arvif->def_wep_key_idx = -1;
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
318562306a36Sopenharmony_ci	if (ret) {
318662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc txbf for vdev %i: %d\n",
318762306a36Sopenharmony_ci			    arvif->vdev_id, ret);
318862306a36Sopenharmony_ci		return;
318962306a36Sopenharmony_ci	}
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	arvif->is_up = false;
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	ath10k_mac_txpower_recalc(ar);
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	cancel_delayed_work_sync(&arvif->connection_loss_work);
319662306a36Sopenharmony_ci}
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_cistatic int ath10k_new_peer_tid_config(struct ath10k *ar,
319962306a36Sopenharmony_ci				      struct ieee80211_sta *sta,
320062306a36Sopenharmony_ci				      struct ath10k_vif *arvif)
320162306a36Sopenharmony_ci{
320262306a36Sopenharmony_ci	struct wmi_per_peer_per_tid_cfg_arg arg = {};
320362306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
320462306a36Sopenharmony_ci	bool config_apply;
320562306a36Sopenharmony_ci	int ret, i;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	for (i = 0; i < ATH10K_TID_MAX; i++) {
320862306a36Sopenharmony_ci		config_apply = false;
320962306a36Sopenharmony_ci		if (arvif->retry_long[i] || arvif->ampdu[i] ||
321062306a36Sopenharmony_ci		    arvif->rate_ctrl[i] || arvif->rtscts[i]) {
321162306a36Sopenharmony_ci			config_apply = true;
321262306a36Sopenharmony_ci			arg.tid = i;
321362306a36Sopenharmony_ci			arg.vdev_id = arvif->vdev_id;
321462306a36Sopenharmony_ci			arg.retry_count = arvif->retry_long[i];
321562306a36Sopenharmony_ci			arg.aggr_control = arvif->ampdu[i];
321662306a36Sopenharmony_ci			arg.rate_ctrl = arvif->rate_ctrl[i];
321762306a36Sopenharmony_ci			arg.rcode_flags = arvif->rate_code[i];
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci			if (arvif->rtscts[i])
322062306a36Sopenharmony_ci				arg.ext_tid_cfg_bitmap =
322162306a36Sopenharmony_ci					WMI_EXT_TID_RTS_CTS_CONFIG;
322262306a36Sopenharmony_ci			else
322362306a36Sopenharmony_ci				arg.ext_tid_cfg_bitmap = 0;
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci			arg.rtscts_ctrl = arvif->rtscts[i];
322662306a36Sopenharmony_ci		}
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci		if (arvif->noack[i]) {
322962306a36Sopenharmony_ci			arg.ack_policy = arvif->noack[i];
323062306a36Sopenharmony_ci			arg.rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_DEFAULT_LOWEST_RATE;
323162306a36Sopenharmony_ci			arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
323262306a36Sopenharmony_ci			config_apply = true;
323362306a36Sopenharmony_ci		}
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci		/* Assign default value(-1) to newly connected station.
323662306a36Sopenharmony_ci		 * This is to identify station specific tid configuration not
323762306a36Sopenharmony_ci		 * configured for the station.
323862306a36Sopenharmony_ci		 */
323962306a36Sopenharmony_ci		arsta->retry_long[i] = -1;
324062306a36Sopenharmony_ci		arsta->noack[i] = -1;
324162306a36Sopenharmony_ci		arsta->ampdu[i] = -1;
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci		if (!config_apply)
324462306a36Sopenharmony_ci			continue;
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci		ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci		ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
324962306a36Sopenharmony_ci		if (ret) {
325062306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set per tid retry/aggr config for sta %pM: %d\n",
325162306a36Sopenharmony_ci				    sta->addr, ret);
325262306a36Sopenharmony_ci			return ret;
325362306a36Sopenharmony_ci		}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci		memset(&arg, 0, sizeof(arg));
325662306a36Sopenharmony_ci	}
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	return 0;
325962306a36Sopenharmony_ci}
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_cistatic int ath10k_station_assoc(struct ath10k *ar,
326262306a36Sopenharmony_ci				struct ieee80211_vif *vif,
326362306a36Sopenharmony_ci				struct ieee80211_sta *sta,
326462306a36Sopenharmony_ci				bool reassoc)
326562306a36Sopenharmony_ci{
326662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
326762306a36Sopenharmony_ci	struct wmi_peer_assoc_complete_arg peer_arg;
326862306a36Sopenharmony_ci	int ret = 0;
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	ret = ath10k_peer_assoc_prepare(ar, vif, sta, &peer_arg);
327362306a36Sopenharmony_ci	if (ret) {
327462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
327562306a36Sopenharmony_ci			    sta->addr, arvif->vdev_id, ret);
327662306a36Sopenharmony_ci		return ret;
327762306a36Sopenharmony_ci	}
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
328062306a36Sopenharmony_ci	if (ret) {
328162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to run peer assoc for STA %pM vdev %i: %d\n",
328262306a36Sopenharmony_ci			    sta->addr, arvif->vdev_id, ret);
328362306a36Sopenharmony_ci		return ret;
328462306a36Sopenharmony_ci	}
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	/* Re-assoc is run only to update supported rates for given station. It
328762306a36Sopenharmony_ci	 * doesn't make much sense to reconfigure the peer completely.
328862306a36Sopenharmony_ci	 */
328962306a36Sopenharmony_ci	if (!reassoc) {
329062306a36Sopenharmony_ci		ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
329162306a36Sopenharmony_ci					     &sta->deflink.ht_cap);
329262306a36Sopenharmony_ci		if (ret) {
329362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
329462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
329562306a36Sopenharmony_ci			return ret;
329662306a36Sopenharmony_ci		}
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci		ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
329962306a36Sopenharmony_ci		if (ret) {
330062306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
330162306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
330262306a36Sopenharmony_ci			return ret;
330362306a36Sopenharmony_ci		}
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci		if (!sta->wme) {
330662306a36Sopenharmony_ci			arvif->num_legacy_stations++;
330762306a36Sopenharmony_ci			ret  = ath10k_recalc_rtscts_prot(arvif);
330862306a36Sopenharmony_ci			if (ret) {
330962306a36Sopenharmony_ci				ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
331062306a36Sopenharmony_ci					    arvif->vdev_id, ret);
331162306a36Sopenharmony_ci				return ret;
331262306a36Sopenharmony_ci			}
331362306a36Sopenharmony_ci		}
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci		/* Plumb cached keys only for static WEP */
331662306a36Sopenharmony_ci		if ((arvif->def_wep_key_idx != -1) && (!sta->tdls)) {
331762306a36Sopenharmony_ci			ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
331862306a36Sopenharmony_ci			if (ret) {
331962306a36Sopenharmony_ci				ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
332062306a36Sopenharmony_ci					    arvif->vdev_id, ret);
332162306a36Sopenharmony_ci				return ret;
332262306a36Sopenharmony_ci			}
332362306a36Sopenharmony_ci		}
332462306a36Sopenharmony_ci	}
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->wmi.svc_map))
332762306a36Sopenharmony_ci		return ret;
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	return ath10k_new_peer_tid_config(ar, sta, arvif);
333062306a36Sopenharmony_ci}
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_cistatic int ath10k_station_disassoc(struct ath10k *ar,
333362306a36Sopenharmony_ci				   struct ieee80211_vif *vif,
333462306a36Sopenharmony_ci				   struct ieee80211_sta *sta)
333562306a36Sopenharmony_ci{
333662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
333762306a36Sopenharmony_ci	int ret = 0;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	if (!sta->wme) {
334262306a36Sopenharmony_ci		arvif->num_legacy_stations--;
334362306a36Sopenharmony_ci		ret = ath10k_recalc_rtscts_prot(arvif);
334462306a36Sopenharmony_ci		if (ret) {
334562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
334662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
334762306a36Sopenharmony_ci			return ret;
334862306a36Sopenharmony_ci		}
334962306a36Sopenharmony_ci	}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	ret = ath10k_clear_peer_keys(arvif, sta->addr);
335262306a36Sopenharmony_ci	if (ret) {
335362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to clear all peer wep keys for vdev %i: %d\n",
335462306a36Sopenharmony_ci			    arvif->vdev_id, ret);
335562306a36Sopenharmony_ci		return ret;
335662306a36Sopenharmony_ci	}
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci	return ret;
335962306a36Sopenharmony_ci}
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci/**************/
336262306a36Sopenharmony_ci/* Regulatory */
336362306a36Sopenharmony_ci/**************/
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_cistatic int ath10k_update_channel_list(struct ath10k *ar)
336662306a36Sopenharmony_ci{
336762306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
336862306a36Sopenharmony_ci	struct ieee80211_supported_band **bands;
336962306a36Sopenharmony_ci	enum nl80211_band band;
337062306a36Sopenharmony_ci	struct ieee80211_channel *channel;
337162306a36Sopenharmony_ci	struct wmi_scan_chan_list_arg arg = {0};
337262306a36Sopenharmony_ci	struct wmi_channel_arg *ch;
337362306a36Sopenharmony_ci	bool passive;
337462306a36Sopenharmony_ci	int len;
337562306a36Sopenharmony_ci	int ret;
337662306a36Sopenharmony_ci	int i;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	bands = hw->wiphy->bands;
338162306a36Sopenharmony_ci	for (band = 0; band < NUM_NL80211_BANDS; band++) {
338262306a36Sopenharmony_ci		if (!bands[band])
338362306a36Sopenharmony_ci			continue;
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci		for (i = 0; i < bands[band]->n_channels; i++) {
338662306a36Sopenharmony_ci			if (bands[band]->channels[i].flags &
338762306a36Sopenharmony_ci			    IEEE80211_CHAN_DISABLED)
338862306a36Sopenharmony_ci				continue;
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci			arg.n_channels++;
339162306a36Sopenharmony_ci		}
339262306a36Sopenharmony_ci	}
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	len = sizeof(struct wmi_channel_arg) * arg.n_channels;
339562306a36Sopenharmony_ci	arg.channels = kzalloc(len, GFP_KERNEL);
339662306a36Sopenharmony_ci	if (!arg.channels)
339762306a36Sopenharmony_ci		return -ENOMEM;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci	ch = arg.channels;
340062306a36Sopenharmony_ci	for (band = 0; band < NUM_NL80211_BANDS; band++) {
340162306a36Sopenharmony_ci		if (!bands[band])
340262306a36Sopenharmony_ci			continue;
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci		for (i = 0; i < bands[band]->n_channels; i++) {
340562306a36Sopenharmony_ci			channel = &bands[band]->channels[i];
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci			if (channel->flags & IEEE80211_CHAN_DISABLED)
340862306a36Sopenharmony_ci				continue;
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci			ch->allow_ht = true;
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci			/* FIXME: when should we really allow VHT? */
341362306a36Sopenharmony_ci			ch->allow_vht = true;
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci			ch->allow_ibss =
341662306a36Sopenharmony_ci				!(channel->flags & IEEE80211_CHAN_NO_IR);
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci			ch->ht40plus =
341962306a36Sopenharmony_ci				!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci			ch->chan_radar =
342262306a36Sopenharmony_ci				!!(channel->flags & IEEE80211_CHAN_RADAR);
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci			passive = channel->flags & IEEE80211_CHAN_NO_IR;
342562306a36Sopenharmony_ci			ch->passive = passive;
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci			/* the firmware is ignoring the "radar" flag of the
342862306a36Sopenharmony_ci			 * channel and is scanning actively using Probe Requests
342962306a36Sopenharmony_ci			 * on "Radar detection"/DFS channels which are not
343062306a36Sopenharmony_ci			 * marked as "available"
343162306a36Sopenharmony_ci			 */
343262306a36Sopenharmony_ci			ch->passive |= ch->chan_radar;
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci			ch->freq = channel->center_freq;
343562306a36Sopenharmony_ci			ch->band_center_freq1 = channel->center_freq;
343662306a36Sopenharmony_ci			ch->min_power = 0;
343762306a36Sopenharmony_ci			ch->max_power = channel->max_power * 2;
343862306a36Sopenharmony_ci			ch->max_reg_power = channel->max_reg_power * 2;
343962306a36Sopenharmony_ci			ch->max_antenna_gain = channel->max_antenna_gain;
344062306a36Sopenharmony_ci			ch->reg_class_id = 0; /* FIXME */
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci			/* FIXME: why use only legacy modes, why not any
344362306a36Sopenharmony_ci			 * HT/VHT modes? Would that even make any
344462306a36Sopenharmony_ci			 * difference?
344562306a36Sopenharmony_ci			 */
344662306a36Sopenharmony_ci			if (channel->band == NL80211_BAND_2GHZ)
344762306a36Sopenharmony_ci				ch->mode = MODE_11G;
344862306a36Sopenharmony_ci			else
344962306a36Sopenharmony_ci				ch->mode = MODE_11A;
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci			if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
345262306a36Sopenharmony_ci				continue;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_WMI,
345562306a36Sopenharmony_ci				   "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
345662306a36Sopenharmony_ci				    ch - arg.channels, arg.n_channels,
345762306a36Sopenharmony_ci				   ch->freq, ch->max_power, ch->max_reg_power,
345862306a36Sopenharmony_ci				   ch->max_antenna_gain, ch->mode);
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci			ch++;
346162306a36Sopenharmony_ci		}
346262306a36Sopenharmony_ci	}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	ret = ath10k_wmi_scan_chan_list(ar, &arg);
346562306a36Sopenharmony_ci	kfree(arg.channels);
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_ci	return ret;
346862306a36Sopenharmony_ci}
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_cistatic enum wmi_dfs_region
347162306a36Sopenharmony_ciath10k_mac_get_dfs_region(enum nl80211_dfs_regions dfs_region)
347262306a36Sopenharmony_ci{
347362306a36Sopenharmony_ci	switch (dfs_region) {
347462306a36Sopenharmony_ci	case NL80211_DFS_UNSET:
347562306a36Sopenharmony_ci		return WMI_UNINIT_DFS_DOMAIN;
347662306a36Sopenharmony_ci	case NL80211_DFS_FCC:
347762306a36Sopenharmony_ci		return WMI_FCC_DFS_DOMAIN;
347862306a36Sopenharmony_ci	case NL80211_DFS_ETSI:
347962306a36Sopenharmony_ci		return WMI_ETSI_DFS_DOMAIN;
348062306a36Sopenharmony_ci	case NL80211_DFS_JP:
348162306a36Sopenharmony_ci		return WMI_MKK4_DFS_DOMAIN;
348262306a36Sopenharmony_ci	}
348362306a36Sopenharmony_ci	return WMI_UNINIT_DFS_DOMAIN;
348462306a36Sopenharmony_ci}
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_cistatic void ath10k_regd_update(struct ath10k *ar)
348762306a36Sopenharmony_ci{
348862306a36Sopenharmony_ci	struct reg_dmn_pair_mapping *regpair;
348962306a36Sopenharmony_ci	int ret;
349062306a36Sopenharmony_ci	enum wmi_dfs_region wmi_dfs_reg;
349162306a36Sopenharmony_ci	enum nl80211_dfs_regions nl_dfs_reg;
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	ret = ath10k_update_channel_list(ar);
349662306a36Sopenharmony_ci	if (ret)
349762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update channel list: %d\n", ret);
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci	regpair = ar->ath_common.regulatory.regpair;
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
350262306a36Sopenharmony_ci		nl_dfs_reg = ar->dfs_detector->region;
350362306a36Sopenharmony_ci		wmi_dfs_reg = ath10k_mac_get_dfs_region(nl_dfs_reg);
350462306a36Sopenharmony_ci	} else {
350562306a36Sopenharmony_ci		wmi_dfs_reg = WMI_UNINIT_DFS_DOMAIN;
350662306a36Sopenharmony_ci	}
350762306a36Sopenharmony_ci
350862306a36Sopenharmony_ci	/* Target allows setting up per-band regdomain but ath_common provides
350962306a36Sopenharmony_ci	 * a combined one only
351062306a36Sopenharmony_ci	 */
351162306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_regdomain(ar,
351262306a36Sopenharmony_ci					    regpair->reg_domain,
351362306a36Sopenharmony_ci					    regpair->reg_domain, /* 2ghz */
351462306a36Sopenharmony_ci					    regpair->reg_domain, /* 5ghz */
351562306a36Sopenharmony_ci					    regpair->reg_2ghz_ctl,
351662306a36Sopenharmony_ci					    regpair->reg_5ghz_ctl,
351762306a36Sopenharmony_ci					    wmi_dfs_reg);
351862306a36Sopenharmony_ci	if (ret)
351962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret);
352062306a36Sopenharmony_ci}
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_cistatic void ath10k_mac_update_channel_list(struct ath10k *ar,
352362306a36Sopenharmony_ci					   struct ieee80211_supported_band *band)
352462306a36Sopenharmony_ci{
352562306a36Sopenharmony_ci	int i;
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	if (ar->low_5ghz_chan && ar->high_5ghz_chan) {
352862306a36Sopenharmony_ci		for (i = 0; i < band->n_channels; i++) {
352962306a36Sopenharmony_ci			if (band->channels[i].center_freq < ar->low_5ghz_chan ||
353062306a36Sopenharmony_ci			    band->channels[i].center_freq > ar->high_5ghz_chan)
353162306a36Sopenharmony_ci				band->channels[i].flags |=
353262306a36Sopenharmony_ci					IEEE80211_CHAN_DISABLED;
353362306a36Sopenharmony_ci		}
353462306a36Sopenharmony_ci	}
353562306a36Sopenharmony_ci}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_cistatic void ath10k_reg_notifier(struct wiphy *wiphy,
353862306a36Sopenharmony_ci				struct regulatory_request *request)
353962306a36Sopenharmony_ci{
354062306a36Sopenharmony_ci	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
354162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
354262306a36Sopenharmony_ci	bool result;
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
354762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs region 0x%x\n",
354862306a36Sopenharmony_ci			   request->dfs_region);
354962306a36Sopenharmony_ci		result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
355062306a36Sopenharmony_ci							  request->dfs_region);
355162306a36Sopenharmony_ci		if (!result)
355262306a36Sopenharmony_ci			ath10k_warn(ar, "DFS region 0x%X not supported, will trigger radar for every pulse\n",
355362306a36Sopenharmony_ci				    request->dfs_region);
355462306a36Sopenharmony_ci	}
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
355762306a36Sopenharmony_ci	if (ar->state == ATH10K_STATE_ON)
355862306a36Sopenharmony_ci		ath10k_regd_update(ar);
355962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY)
356262306a36Sopenharmony_ci		ath10k_mac_update_channel_list(ar,
356362306a36Sopenharmony_ci					       ar->hw->wiphy->bands[NL80211_BAND_5GHZ]);
356462306a36Sopenharmony_ci}
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_cistatic void ath10k_stop_radar_confirmation(struct ath10k *ar)
356762306a36Sopenharmony_ci{
356862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
356962306a36Sopenharmony_ci	ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_STOPPED;
357062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	cancel_work_sync(&ar->radar_confirmation_work);
357362306a36Sopenharmony_ci}
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci/***************/
357662306a36Sopenharmony_ci/* TX handlers */
357762306a36Sopenharmony_ci/***************/
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_cienum ath10k_mac_tx_path {
358062306a36Sopenharmony_ci	ATH10K_MAC_TX_HTT,
358162306a36Sopenharmony_ci	ATH10K_MAC_TX_HTT_MGMT,
358262306a36Sopenharmony_ci	ATH10K_MAC_TX_WMI_MGMT,
358362306a36Sopenharmony_ci	ATH10K_MAC_TX_UNKNOWN,
358462306a36Sopenharmony_ci};
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_civoid ath10k_mac_tx_lock(struct ath10k *ar, int reason)
358762306a36Sopenharmony_ci{
358862306a36Sopenharmony_ci	lockdep_assert_held(&ar->htt.tx_lock);
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	WARN_ON(reason >= ATH10K_TX_PAUSE_MAX);
359162306a36Sopenharmony_ci	ar->tx_paused |= BIT(reason);
359262306a36Sopenharmony_ci	ieee80211_stop_queues(ar->hw);
359362306a36Sopenharmony_ci}
359462306a36Sopenharmony_ci
359562306a36Sopenharmony_cistatic void ath10k_mac_tx_unlock_iter(void *data, u8 *mac,
359662306a36Sopenharmony_ci				      struct ieee80211_vif *vif)
359762306a36Sopenharmony_ci{
359862306a36Sopenharmony_ci	struct ath10k *ar = data;
359962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci	if (arvif->tx_paused)
360262306a36Sopenharmony_ci		return;
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	ieee80211_wake_queue(ar->hw, arvif->vdev_id);
360562306a36Sopenharmony_ci}
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_civoid ath10k_mac_tx_unlock(struct ath10k *ar, int reason)
360862306a36Sopenharmony_ci{
360962306a36Sopenharmony_ci	lockdep_assert_held(&ar->htt.tx_lock);
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	WARN_ON(reason >= ATH10K_TX_PAUSE_MAX);
361262306a36Sopenharmony_ci	ar->tx_paused &= ~BIT(reason);
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	if (ar->tx_paused)
361562306a36Sopenharmony_ci		return;
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(ar->hw,
361862306a36Sopenharmony_ci						   ATH10K_ITER_RESUME_FLAGS,
361962306a36Sopenharmony_ci						   ath10k_mac_tx_unlock_iter,
362062306a36Sopenharmony_ci						   ar);
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	ieee80211_wake_queue(ar->hw, ar->hw->offchannel_tx_hw_queue);
362362306a36Sopenharmony_ci}
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_civoid ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason)
362662306a36Sopenharmony_ci{
362762306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
362862306a36Sopenharmony_ci
362962306a36Sopenharmony_ci	lockdep_assert_held(&ar->htt.tx_lock);
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	WARN_ON(reason >= BITS_PER_LONG);
363262306a36Sopenharmony_ci	arvif->tx_paused |= BIT(reason);
363362306a36Sopenharmony_ci	ieee80211_stop_queue(ar->hw, arvif->vdev_id);
363462306a36Sopenharmony_ci}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_civoid ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason)
363762306a36Sopenharmony_ci{
363862306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci	lockdep_assert_held(&ar->htt.tx_lock);
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	WARN_ON(reason >= BITS_PER_LONG);
364362306a36Sopenharmony_ci	arvif->tx_paused &= ~BIT(reason);
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	if (ar->tx_paused)
364662306a36Sopenharmony_ci		return;
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	if (arvif->tx_paused)
364962306a36Sopenharmony_ci		return;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	ieee80211_wake_queue(ar->hw, arvif->vdev_id);
365262306a36Sopenharmony_ci}
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_cistatic void ath10k_mac_vif_handle_tx_pause(struct ath10k_vif *arvif,
365562306a36Sopenharmony_ci					   enum wmi_tlv_tx_pause_id pause_id,
365662306a36Sopenharmony_ci					   enum wmi_tlv_tx_pause_action action)
365762306a36Sopenharmony_ci{
365862306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	lockdep_assert_held(&ar->htt.tx_lock);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci	switch (action) {
366362306a36Sopenharmony_ci	case WMI_TLV_TX_PAUSE_ACTION_STOP:
366462306a36Sopenharmony_ci		ath10k_mac_vif_tx_lock(arvif, pause_id);
366562306a36Sopenharmony_ci		break;
366662306a36Sopenharmony_ci	case WMI_TLV_TX_PAUSE_ACTION_WAKE:
366762306a36Sopenharmony_ci		ath10k_mac_vif_tx_unlock(arvif, pause_id);
366862306a36Sopenharmony_ci		break;
366962306a36Sopenharmony_ci	default:
367062306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_BOOT,
367162306a36Sopenharmony_ci			   "received unknown tx pause action %d on vdev %i, ignoring\n",
367262306a36Sopenharmony_ci			    action, arvif->vdev_id);
367362306a36Sopenharmony_ci		break;
367462306a36Sopenharmony_ci	}
367562306a36Sopenharmony_ci}
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_cistruct ath10k_mac_tx_pause {
367862306a36Sopenharmony_ci	u32 vdev_id;
367962306a36Sopenharmony_ci	enum wmi_tlv_tx_pause_id pause_id;
368062306a36Sopenharmony_ci	enum wmi_tlv_tx_pause_action action;
368162306a36Sopenharmony_ci};
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_cistatic void ath10k_mac_handle_tx_pause_iter(void *data, u8 *mac,
368462306a36Sopenharmony_ci					    struct ieee80211_vif *vif)
368562306a36Sopenharmony_ci{
368662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
368762306a36Sopenharmony_ci	struct ath10k_mac_tx_pause *arg = data;
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	if (arvif->vdev_id != arg->vdev_id)
369062306a36Sopenharmony_ci		return;
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_ci	ath10k_mac_vif_handle_tx_pause(arvif, arg->pause_id, arg->action);
369362306a36Sopenharmony_ci}
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_civoid ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
369662306a36Sopenharmony_ci				     enum wmi_tlv_tx_pause_id pause_id,
369762306a36Sopenharmony_ci				     enum wmi_tlv_tx_pause_action action)
369862306a36Sopenharmony_ci{
369962306a36Sopenharmony_ci	struct ath10k_mac_tx_pause arg = {
370062306a36Sopenharmony_ci		.vdev_id = vdev_id,
370162306a36Sopenharmony_ci		.pause_id = pause_id,
370262306a36Sopenharmony_ci		.action = action,
370362306a36Sopenharmony_ci	};
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
370662306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(ar->hw,
370762306a36Sopenharmony_ci						   ATH10K_ITER_RESUME_FLAGS,
370862306a36Sopenharmony_ci						   ath10k_mac_handle_tx_pause_iter,
370962306a36Sopenharmony_ci						   &arg);
371062306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
371162306a36Sopenharmony_ci}
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_cistatic enum ath10k_hw_txrx_mode
371462306a36Sopenharmony_ciath10k_mac_tx_h_get_txmode(struct ath10k *ar,
371562306a36Sopenharmony_ci			   struct ieee80211_vif *vif,
371662306a36Sopenharmony_ci			   struct ieee80211_sta *sta,
371762306a36Sopenharmony_ci			   struct sk_buff *skb)
371862306a36Sopenharmony_ci{
371962306a36Sopenharmony_ci	const struct ieee80211_hdr *hdr = (void *)skb->data;
372062306a36Sopenharmony_ci	const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
372162306a36Sopenharmony_ci	__le16 fc = hdr->frame_control;
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
372462306a36Sopenharmony_ci		return ATH10K_HW_TXRX_ETHERNET;
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	if (!vif || vif->type == NL80211_IFTYPE_MONITOR)
372762306a36Sopenharmony_ci		return ATH10K_HW_TXRX_RAW;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	if (ieee80211_is_mgmt(fc))
373062306a36Sopenharmony_ci		return ATH10K_HW_TXRX_MGMT;
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	/* Workaround:
373362306a36Sopenharmony_ci	 *
373462306a36Sopenharmony_ci	 * NullFunc frames are mostly used to ping if a client or AP are still
373562306a36Sopenharmony_ci	 * reachable and responsive. This implies tx status reports must be
373662306a36Sopenharmony_ci	 * accurate - otherwise either mac80211 or userspace (e.g. hostapd) can
373762306a36Sopenharmony_ci	 * come to a conclusion that the other end disappeared and tear down
373862306a36Sopenharmony_ci	 * BSS connection or it can never disconnect from BSS/client (which is
373962306a36Sopenharmony_ci	 * the case).
374062306a36Sopenharmony_ci	 *
374162306a36Sopenharmony_ci	 * Firmware with HTT older than 3.0 delivers incorrect tx status for
374262306a36Sopenharmony_ci	 * NullFunc frames to driver. However there's a HTT Mgmt Tx command
374362306a36Sopenharmony_ci	 * which seems to deliver correct tx reports for NullFunc frames. The
374462306a36Sopenharmony_ci	 * downside of using it is it ignores client powersave state so it can
374562306a36Sopenharmony_ci	 * end up disconnecting sleeping clients in AP mode. It should fix STA
374662306a36Sopenharmony_ci	 * mode though because AP don't sleep.
374762306a36Sopenharmony_ci	 */
374862306a36Sopenharmony_ci	if (ar->htt.target_version_major < 3 &&
374962306a36Sopenharmony_ci	    (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
375062306a36Sopenharmony_ci	    !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
375162306a36Sopenharmony_ci		      ar->running_fw->fw_file.fw_features))
375262306a36Sopenharmony_ci		return ATH10K_HW_TXRX_MGMT;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	/* Workaround:
375562306a36Sopenharmony_ci	 *
375662306a36Sopenharmony_ci	 * Some wmi-tlv firmwares for qca6174 have broken Tx key selection for
375762306a36Sopenharmony_ci	 * NativeWifi txmode - it selects AP key instead of peer key. It seems
375862306a36Sopenharmony_ci	 * to work with Ethernet txmode so use it.
375962306a36Sopenharmony_ci	 *
376062306a36Sopenharmony_ci	 * FIXME: Check if raw mode works with TDLS.
376162306a36Sopenharmony_ci	 */
376262306a36Sopenharmony_ci	if (ieee80211_is_data_present(fc) && sta && sta->tdls)
376362306a36Sopenharmony_ci		return ATH10K_HW_TXRX_ETHERNET;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci	if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) ||
376662306a36Sopenharmony_ci	    skb_cb->flags & ATH10K_SKB_F_RAW_TX)
376762306a36Sopenharmony_ci		return ATH10K_HW_TXRX_RAW;
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci	return ATH10K_HW_TXRX_NATIVE_WIFI;
377062306a36Sopenharmony_ci}
377162306a36Sopenharmony_ci
377262306a36Sopenharmony_cistatic bool ath10k_tx_h_use_hwcrypto(struct ieee80211_vif *vif,
377362306a36Sopenharmony_ci				     struct sk_buff *skb)
377462306a36Sopenharmony_ci{
377562306a36Sopenharmony_ci	const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
377662306a36Sopenharmony_ci	const struct ieee80211_hdr *hdr = (void *)skb->data;
377762306a36Sopenharmony_ci	const u32 mask = IEEE80211_TX_INTFL_DONT_ENCRYPT |
377862306a36Sopenharmony_ci			 IEEE80211_TX_CTL_INJECTED;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	if (!ieee80211_has_protected(hdr->frame_control))
378162306a36Sopenharmony_ci		return false;
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	if ((info->flags & mask) == mask)
378462306a36Sopenharmony_ci		return false;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	if (vif)
378762306a36Sopenharmony_ci		return !((struct ath10k_vif *)vif->drv_priv)->nohwcrypt;
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	return true;
379062306a36Sopenharmony_ci}
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci/* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS
379362306a36Sopenharmony_ci * Control in the header.
379462306a36Sopenharmony_ci */
379562306a36Sopenharmony_cistatic void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
379662306a36Sopenharmony_ci{
379762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (void *)skb->data;
379862306a36Sopenharmony_ci	struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
379962306a36Sopenharmony_ci	u8 *qos_ctl;
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	if (!ieee80211_is_data_qos(hdr->frame_control))
380262306a36Sopenharmony_ci		return;
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci	qos_ctl = ieee80211_get_qos_ctl(hdr);
380562306a36Sopenharmony_ci	memmove(skb->data + IEEE80211_QOS_CTL_LEN,
380662306a36Sopenharmony_ci		skb->data, (void *)qos_ctl - (void *)skb->data);
380762306a36Sopenharmony_ci	skb_pull(skb, IEEE80211_QOS_CTL_LEN);
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	/* Some firmware revisions don't handle sending QoS NullFunc well.
381062306a36Sopenharmony_ci	 * These frames are mainly used for CQM purposes so it doesn't really
381162306a36Sopenharmony_ci	 * matter whether QoS NullFunc or NullFunc are sent.
381262306a36Sopenharmony_ci	 */
381362306a36Sopenharmony_ci	hdr = (void *)skb->data;
381462306a36Sopenharmony_ci	if (ieee80211_is_qos_nullfunc(hdr->frame_control))
381562306a36Sopenharmony_ci		cb->flags &= ~ATH10K_SKB_F_QOS;
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
381862306a36Sopenharmony_ci}
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_cistatic void ath10k_tx_h_8023(struct sk_buff *skb)
382162306a36Sopenharmony_ci{
382262306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
382362306a36Sopenharmony_ci	struct rfc1042_hdr *rfc1042;
382462306a36Sopenharmony_ci	struct ethhdr *eth;
382562306a36Sopenharmony_ci	size_t hdrlen;
382662306a36Sopenharmony_ci	u8 da[ETH_ALEN];
382762306a36Sopenharmony_ci	u8 sa[ETH_ALEN];
382862306a36Sopenharmony_ci	__be16 type;
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_ci	hdr = (void *)skb->data;
383162306a36Sopenharmony_ci	hdrlen = ieee80211_hdrlen(hdr->frame_control);
383262306a36Sopenharmony_ci	rfc1042 = (void *)skb->data + hdrlen;
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	ether_addr_copy(da, ieee80211_get_DA(hdr));
383562306a36Sopenharmony_ci	ether_addr_copy(sa, ieee80211_get_SA(hdr));
383662306a36Sopenharmony_ci	type = rfc1042->snap_type;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	skb_pull(skb, hdrlen + sizeof(*rfc1042));
383962306a36Sopenharmony_ci	skb_push(skb, sizeof(*eth));
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci	eth = (void *)skb->data;
384262306a36Sopenharmony_ci	ether_addr_copy(eth->h_dest, da);
384362306a36Sopenharmony_ci	ether_addr_copy(eth->h_source, sa);
384462306a36Sopenharmony_ci	eth->h_proto = type;
384562306a36Sopenharmony_ci}
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_cistatic void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
384862306a36Sopenharmony_ci				       struct ieee80211_vif *vif,
384962306a36Sopenharmony_ci				       struct sk_buff *skb)
385062306a36Sopenharmony_ci{
385162306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
385262306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	/* This is case only for P2P_GO */
385562306a36Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_AP || !vif->p2p)
385662306a36Sopenharmony_ci		return;
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci	if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
385962306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
386062306a36Sopenharmony_ci		if (arvif->u.ap.noa_data)
386162306a36Sopenharmony_ci			if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
386262306a36Sopenharmony_ci					      GFP_ATOMIC))
386362306a36Sopenharmony_ci				skb_put_data(skb, arvif->u.ap.noa_data,
386462306a36Sopenharmony_ci					     arvif->u.ap.noa_len);
386562306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
386662306a36Sopenharmony_ci	}
386762306a36Sopenharmony_ci}
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_cistatic void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
387062306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
387162306a36Sopenharmony_ci				    struct ieee80211_txq *txq,
387262306a36Sopenharmony_ci				    struct ieee80211_sta *sta,
387362306a36Sopenharmony_ci				    struct sk_buff *skb, u16 airtime)
387462306a36Sopenharmony_ci{
387562306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (void *)skb->data;
387662306a36Sopenharmony_ci	struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
387762306a36Sopenharmony_ci	const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
387862306a36Sopenharmony_ci	bool is_data = ieee80211_is_data(hdr->frame_control) ||
387962306a36Sopenharmony_ci			ieee80211_is_data_qos(hdr->frame_control);
388062306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
388162306a36Sopenharmony_ci	struct ath10k_sta *arsta;
388262306a36Sopenharmony_ci	u8 tid, *qos_ctl;
388362306a36Sopenharmony_ci	bool noack = false;
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	cb->flags = 0;
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
388862306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_QOS;	/* Assume data frames are QoS */
388962306a36Sopenharmony_ci		goto finish_cb_fill;
389062306a36Sopenharmony_ci	}
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	if (!ath10k_tx_h_use_hwcrypto(vif, skb))
389362306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci	if (ieee80211_is_mgmt(hdr->frame_control))
389662306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_MGMT;
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	if (ieee80211_is_data_qos(hdr->frame_control)) {
389962306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_QOS;
390062306a36Sopenharmony_ci		qos_ctl = ieee80211_get_qos_ctl(hdr);
390162306a36Sopenharmony_ci		tid = (*qos_ctl) & IEEE80211_QOS_CTL_TID_MASK;
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_ci		if (arvif->noack[tid] == WMI_PEER_TID_CONFIG_NOACK)
390462306a36Sopenharmony_ci			noack = true;
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci		if (sta) {
390762306a36Sopenharmony_ci			arsta = (struct ath10k_sta *)sta->drv_priv;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci			if (arsta->noack[tid] == WMI_PEER_TID_CONFIG_NOACK)
391062306a36Sopenharmony_ci				noack = true;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci			if (arsta->noack[tid] == WMI_PEER_TID_CONFIG_ACK)
391362306a36Sopenharmony_ci				noack = false;
391462306a36Sopenharmony_ci		}
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci		if (noack)
391762306a36Sopenharmony_ci			cb->flags |= ATH10K_SKB_F_NOACK_TID;
391862306a36Sopenharmony_ci	}
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci	/* Data frames encrypted in software will be posted to firmware
392162306a36Sopenharmony_ci	 * with tx encap mode set to RAW. Ex: Multicast traffic generated
392262306a36Sopenharmony_ci	 * for a specific VLAN group will always be encrypted in software.
392362306a36Sopenharmony_ci	 */
392462306a36Sopenharmony_ci	if (is_data && ieee80211_has_protected(hdr->frame_control) &&
392562306a36Sopenharmony_ci	    !info->control.hw_key) {
392662306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
392762306a36Sopenharmony_ci		cb->flags |= ATH10K_SKB_F_RAW_TX;
392862306a36Sopenharmony_ci	}
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_cifinish_cb_fill:
393162306a36Sopenharmony_ci	cb->vif = vif;
393262306a36Sopenharmony_ci	cb->txq = txq;
393362306a36Sopenharmony_ci	cb->airtime_est = airtime;
393462306a36Sopenharmony_ci	if (sta) {
393562306a36Sopenharmony_ci		arsta = (struct ath10k_sta *)sta->drv_priv;
393662306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
393762306a36Sopenharmony_ci		cb->ucast_cipher = arsta->ucast_cipher;
393862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
393962306a36Sopenharmony_ci	}
394062306a36Sopenharmony_ci}
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_cibool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
394362306a36Sopenharmony_ci{
394462306a36Sopenharmony_ci	/* FIXME: Not really sure since when the behaviour changed. At some
394562306a36Sopenharmony_ci	 * point new firmware stopped requiring creation of peer entries for
394662306a36Sopenharmony_ci	 * offchannel tx (and actually creating them causes issues with wmi-htc
394762306a36Sopenharmony_ci	 * tx credit replenishment and reliability). Assuming it's at least 3.4
394862306a36Sopenharmony_ci	 * because that's when the `freq` was introduced to TX_FRM HTT command.
394962306a36Sopenharmony_ci	 */
395062306a36Sopenharmony_ci	return (ar->htt.target_version_major >= 3 &&
395162306a36Sopenharmony_ci		ar->htt.target_version_minor >= 4 &&
395262306a36Sopenharmony_ci		ar->running_fw->fw_file.htt_op_version == ATH10K_FW_HTT_OP_VERSION_TLV);
395362306a36Sopenharmony_ci}
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_cistatic int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb)
395662306a36Sopenharmony_ci{
395762306a36Sopenharmony_ci	struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue;
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	if (skb_queue_len_lockless(q) >= ATH10K_MAX_NUM_MGMT_PENDING) {
396062306a36Sopenharmony_ci		ath10k_warn(ar, "wmi mgmt tx queue is full\n");
396162306a36Sopenharmony_ci		return -ENOSPC;
396262306a36Sopenharmony_ci	}
396362306a36Sopenharmony_ci
396462306a36Sopenharmony_ci	skb_queue_tail(q, skb);
396562306a36Sopenharmony_ci	ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work);
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	return 0;
396862306a36Sopenharmony_ci}
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_cistatic enum ath10k_mac_tx_path
397162306a36Sopenharmony_ciath10k_mac_tx_h_get_txpath(struct ath10k *ar,
397262306a36Sopenharmony_ci			   struct sk_buff *skb,
397362306a36Sopenharmony_ci			   enum ath10k_hw_txrx_mode txmode)
397462306a36Sopenharmony_ci{
397562306a36Sopenharmony_ci	switch (txmode) {
397662306a36Sopenharmony_ci	case ATH10K_HW_TXRX_RAW:
397762306a36Sopenharmony_ci	case ATH10K_HW_TXRX_NATIVE_WIFI:
397862306a36Sopenharmony_ci	case ATH10K_HW_TXRX_ETHERNET:
397962306a36Sopenharmony_ci		return ATH10K_MAC_TX_HTT;
398062306a36Sopenharmony_ci	case ATH10K_HW_TXRX_MGMT:
398162306a36Sopenharmony_ci		if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
398262306a36Sopenharmony_ci			     ar->running_fw->fw_file.fw_features) ||
398362306a36Sopenharmony_ci			     test_bit(WMI_SERVICE_MGMT_TX_WMI,
398462306a36Sopenharmony_ci				      ar->wmi.svc_map))
398562306a36Sopenharmony_ci			return ATH10K_MAC_TX_WMI_MGMT;
398662306a36Sopenharmony_ci		else if (ar->htt.target_version_major >= 3)
398762306a36Sopenharmony_ci			return ATH10K_MAC_TX_HTT;
398862306a36Sopenharmony_ci		else
398962306a36Sopenharmony_ci			return ATH10K_MAC_TX_HTT_MGMT;
399062306a36Sopenharmony_ci	}
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	return ATH10K_MAC_TX_UNKNOWN;
399362306a36Sopenharmony_ci}
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_cistatic int ath10k_mac_tx_submit(struct ath10k *ar,
399662306a36Sopenharmony_ci				enum ath10k_hw_txrx_mode txmode,
399762306a36Sopenharmony_ci				enum ath10k_mac_tx_path txpath,
399862306a36Sopenharmony_ci				struct sk_buff *skb)
399962306a36Sopenharmony_ci{
400062306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
400162306a36Sopenharmony_ci	int ret = -EINVAL;
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	switch (txpath) {
400462306a36Sopenharmony_ci	case ATH10K_MAC_TX_HTT:
400562306a36Sopenharmony_ci		ret = ath10k_htt_tx(htt, txmode, skb);
400662306a36Sopenharmony_ci		break;
400762306a36Sopenharmony_ci	case ATH10K_MAC_TX_HTT_MGMT:
400862306a36Sopenharmony_ci		ret = ath10k_htt_mgmt_tx(htt, skb);
400962306a36Sopenharmony_ci		break;
401062306a36Sopenharmony_ci	case ATH10K_MAC_TX_WMI_MGMT:
401162306a36Sopenharmony_ci		ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
401262306a36Sopenharmony_ci		break;
401362306a36Sopenharmony_ci	case ATH10K_MAC_TX_UNKNOWN:
401462306a36Sopenharmony_ci		WARN_ON_ONCE(1);
401562306a36Sopenharmony_ci		ret = -EINVAL;
401662306a36Sopenharmony_ci		break;
401762306a36Sopenharmony_ci	}
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci	if (ret) {
402062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
402162306a36Sopenharmony_ci			    ret);
402262306a36Sopenharmony_ci		ieee80211_free_txskb(ar->hw, skb);
402362306a36Sopenharmony_ci	}
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_ci	return ret;
402662306a36Sopenharmony_ci}
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci/* This function consumes the sk_buff regardless of return value as far as
402962306a36Sopenharmony_ci * caller is concerned so no freeing is necessary afterwards.
403062306a36Sopenharmony_ci */
403162306a36Sopenharmony_cistatic int ath10k_mac_tx(struct ath10k *ar,
403262306a36Sopenharmony_ci			 struct ieee80211_vif *vif,
403362306a36Sopenharmony_ci			 enum ath10k_hw_txrx_mode txmode,
403462306a36Sopenharmony_ci			 enum ath10k_mac_tx_path txpath,
403562306a36Sopenharmony_ci			 struct sk_buff *skb, bool noque_offchan)
403662306a36Sopenharmony_ci{
403762306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
403862306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
403962306a36Sopenharmony_ci	const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
404062306a36Sopenharmony_ci	int ret;
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	/* We should disable CCK RATE due to P2P */
404362306a36Sopenharmony_ci	if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
404462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	switch (txmode) {
404762306a36Sopenharmony_ci	case ATH10K_HW_TXRX_MGMT:
404862306a36Sopenharmony_ci	case ATH10K_HW_TXRX_NATIVE_WIFI:
404962306a36Sopenharmony_ci		ath10k_tx_h_nwifi(hw, skb);
405062306a36Sopenharmony_ci		ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
405162306a36Sopenharmony_ci		ath10k_tx_h_seq_no(vif, skb);
405262306a36Sopenharmony_ci		break;
405362306a36Sopenharmony_ci	case ATH10K_HW_TXRX_ETHERNET:
405462306a36Sopenharmony_ci		/* Convert 802.11->802.3 header only if the frame was earlier
405562306a36Sopenharmony_ci		 * encapsulated to 802.11 by mac80211. Otherwise pass it as is.
405662306a36Sopenharmony_ci		 */
405762306a36Sopenharmony_ci		if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
405862306a36Sopenharmony_ci			ath10k_tx_h_8023(skb);
405962306a36Sopenharmony_ci		break;
406062306a36Sopenharmony_ci	case ATH10K_HW_TXRX_RAW:
406162306a36Sopenharmony_ci		if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) &&
406262306a36Sopenharmony_ci		    !(skb_cb->flags & ATH10K_SKB_F_RAW_TX)) {
406362306a36Sopenharmony_ci			WARN_ON_ONCE(1);
406462306a36Sopenharmony_ci			ieee80211_free_txskb(hw, skb);
406562306a36Sopenharmony_ci			return -ENOTSUPP;
406662306a36Sopenharmony_ci		}
406762306a36Sopenharmony_ci	}
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci	if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
407062306a36Sopenharmony_ci		if (!ath10k_mac_tx_frm_has_freq(ar)) {
407162306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %pK len %d\n",
407262306a36Sopenharmony_ci				   skb, skb->len);
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci			skb_queue_tail(&ar->offchan_tx_queue, skb);
407562306a36Sopenharmony_ci			ieee80211_queue_work(hw, &ar->offchan_tx_work);
407662306a36Sopenharmony_ci			return 0;
407762306a36Sopenharmony_ci		}
407862306a36Sopenharmony_ci	}
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_ci	ret = ath10k_mac_tx_submit(ar, txmode, txpath, skb);
408162306a36Sopenharmony_ci	if (ret) {
408262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit frame: %d\n", ret);
408362306a36Sopenharmony_ci		return ret;
408462306a36Sopenharmony_ci	}
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci	return 0;
408762306a36Sopenharmony_ci}
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_civoid ath10k_offchan_tx_purge(struct ath10k *ar)
409062306a36Sopenharmony_ci{
409162306a36Sopenharmony_ci	struct sk_buff *skb;
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci	for (;;) {
409462306a36Sopenharmony_ci		skb = skb_dequeue(&ar->offchan_tx_queue);
409562306a36Sopenharmony_ci		if (!skb)
409662306a36Sopenharmony_ci			break;
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci		ieee80211_free_txskb(ar->hw, skb);
409962306a36Sopenharmony_ci	}
410062306a36Sopenharmony_ci}
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_civoid ath10k_offchan_tx_work(struct work_struct *work)
410362306a36Sopenharmony_ci{
410462306a36Sopenharmony_ci	struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
410562306a36Sopenharmony_ci	struct ath10k_peer *peer;
410662306a36Sopenharmony_ci	struct ath10k_vif *arvif;
410762306a36Sopenharmony_ci	enum ath10k_hw_txrx_mode txmode;
410862306a36Sopenharmony_ci	enum ath10k_mac_tx_path txpath;
410962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
411062306a36Sopenharmony_ci	struct ieee80211_vif *vif;
411162306a36Sopenharmony_ci	struct ieee80211_sta *sta;
411262306a36Sopenharmony_ci	struct sk_buff *skb;
411362306a36Sopenharmony_ci	const u8 *peer_addr;
411462306a36Sopenharmony_ci	int vdev_id;
411562306a36Sopenharmony_ci	int ret;
411662306a36Sopenharmony_ci	unsigned long time_left;
411762306a36Sopenharmony_ci	bool tmp_peer_created = false;
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ci	/* FW requirement: We must create a peer before FW will send out
412062306a36Sopenharmony_ci	 * an offchannel frame. Otherwise the frame will be stuck and
412162306a36Sopenharmony_ci	 * never transmitted. We delete the peer upon tx completion.
412262306a36Sopenharmony_ci	 * It is unlikely that a peer for offchannel tx will already be
412362306a36Sopenharmony_ci	 * present. However it may be in some rare cases so account for that.
412462306a36Sopenharmony_ci	 * Otherwise we might remove a legitimate peer and break stuff.
412562306a36Sopenharmony_ci	 */
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci	for (;;) {
412862306a36Sopenharmony_ci		skb = skb_dequeue(&ar->offchan_tx_queue);
412962306a36Sopenharmony_ci		if (!skb)
413062306a36Sopenharmony_ci			break;
413162306a36Sopenharmony_ci
413262306a36Sopenharmony_ci		mutex_lock(&ar->conf_mutex);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK len %d\n",
413562306a36Sopenharmony_ci			   skb, skb->len);
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci		hdr = (struct ieee80211_hdr *)skb->data;
413862306a36Sopenharmony_ci		peer_addr = ieee80211_get_DA(hdr);
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
414162306a36Sopenharmony_ci		vdev_id = ar->scan.vdev_id;
414262306a36Sopenharmony_ci		peer = ath10k_peer_find(ar, vdev_id, peer_addr);
414362306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci		if (peer) {
414662306a36Sopenharmony_ci			ath10k_warn(ar, "peer %pM on vdev %d already present\n",
414762306a36Sopenharmony_ci				    peer_addr, vdev_id);
414862306a36Sopenharmony_ci		} else {
414962306a36Sopenharmony_ci			ret = ath10k_peer_create(ar, NULL, NULL, vdev_id,
415062306a36Sopenharmony_ci						 peer_addr,
415162306a36Sopenharmony_ci						 WMI_PEER_TYPE_DEFAULT);
415262306a36Sopenharmony_ci			if (ret)
415362306a36Sopenharmony_ci				ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
415462306a36Sopenharmony_ci					    peer_addr, vdev_id, ret);
415562306a36Sopenharmony_ci			tmp_peer_created = (ret == 0);
415662306a36Sopenharmony_ci		}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
415962306a36Sopenharmony_ci		reinit_completion(&ar->offchan_tx_completed);
416062306a36Sopenharmony_ci		ar->offchan_tx_skb = skb;
416162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci		/* It's safe to access vif and sta - conf_mutex guarantees that
416462306a36Sopenharmony_ci		 * sta_state() and remove_interface() are locked exclusively
416562306a36Sopenharmony_ci		 * out wrt to this offchannel worker.
416662306a36Sopenharmony_ci		 */
416762306a36Sopenharmony_ci		arvif = ath10k_get_arvif(ar, vdev_id);
416862306a36Sopenharmony_ci		if (arvif) {
416962306a36Sopenharmony_ci			vif = arvif->vif;
417062306a36Sopenharmony_ci			sta = ieee80211_find_sta(vif, peer_addr);
417162306a36Sopenharmony_ci		} else {
417262306a36Sopenharmony_ci			vif = NULL;
417362306a36Sopenharmony_ci			sta = NULL;
417462306a36Sopenharmony_ci		}
417562306a36Sopenharmony_ci
417662306a36Sopenharmony_ci		txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
417762306a36Sopenharmony_ci		txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci		ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, true);
418062306a36Sopenharmony_ci		if (ret) {
418162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
418262306a36Sopenharmony_ci				    ret);
418362306a36Sopenharmony_ci			/* not serious */
418462306a36Sopenharmony_ci		}
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci		time_left =
418762306a36Sopenharmony_ci		wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
418862306a36Sopenharmony_ci		if (time_left == 0)
418962306a36Sopenharmony_ci			ath10k_warn(ar, "timed out waiting for offchannel skb %pK, len: %d\n",
419062306a36Sopenharmony_ci				    skb, skb->len);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci		if (!peer && tmp_peer_created) {
419362306a36Sopenharmony_ci			ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
419462306a36Sopenharmony_ci			if (ret)
419562306a36Sopenharmony_ci				ath10k_warn(ar, "failed to delete peer %pM on vdev %d: %d\n",
419662306a36Sopenharmony_ci					    peer_addr, vdev_id, ret);
419762306a36Sopenharmony_ci		}
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci		mutex_unlock(&ar->conf_mutex);
420062306a36Sopenharmony_ci	}
420162306a36Sopenharmony_ci}
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_civoid ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar)
420462306a36Sopenharmony_ci{
420562306a36Sopenharmony_ci	struct sk_buff *skb;
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	for (;;) {
420862306a36Sopenharmony_ci		skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
420962306a36Sopenharmony_ci		if (!skb)
421062306a36Sopenharmony_ci			break;
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_ci		ieee80211_free_txskb(ar->hw, skb);
421362306a36Sopenharmony_ci	}
421462306a36Sopenharmony_ci}
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_civoid ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
421762306a36Sopenharmony_ci{
421862306a36Sopenharmony_ci	struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
421962306a36Sopenharmony_ci	struct sk_buff *skb;
422062306a36Sopenharmony_ci	dma_addr_t paddr;
422162306a36Sopenharmony_ci	int ret;
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci	for (;;) {
422462306a36Sopenharmony_ci		skb = skb_dequeue(&ar->wmi_mgmt_tx_queue);
422562306a36Sopenharmony_ci		if (!skb)
422662306a36Sopenharmony_ci			break;
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci		if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF,
422962306a36Sopenharmony_ci			     ar->running_fw->fw_file.fw_features)) {
423062306a36Sopenharmony_ci			paddr = dma_map_single(ar->dev, skb->data,
423162306a36Sopenharmony_ci					       skb->len, DMA_TO_DEVICE);
423262306a36Sopenharmony_ci			if (dma_mapping_error(ar->dev, paddr)) {
423362306a36Sopenharmony_ci				ieee80211_free_txskb(ar->hw, skb);
423462306a36Sopenharmony_ci				continue;
423562306a36Sopenharmony_ci			}
423662306a36Sopenharmony_ci			ret = ath10k_wmi_mgmt_tx_send(ar, skb, paddr);
423762306a36Sopenharmony_ci			if (ret) {
423862306a36Sopenharmony_ci				ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n",
423962306a36Sopenharmony_ci					    ret);
424062306a36Sopenharmony_ci				/* remove this msdu from idr tracking */
424162306a36Sopenharmony_ci				ath10k_wmi_cleanup_mgmt_tx_send(ar, skb);
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_ci				dma_unmap_single(ar->dev, paddr, skb->len,
424462306a36Sopenharmony_ci						 DMA_TO_DEVICE);
424562306a36Sopenharmony_ci				ieee80211_free_txskb(ar->hw, skb);
424662306a36Sopenharmony_ci			}
424762306a36Sopenharmony_ci		} else {
424862306a36Sopenharmony_ci			ret = ath10k_wmi_mgmt_tx(ar, skb);
424962306a36Sopenharmony_ci			if (ret) {
425062306a36Sopenharmony_ci				ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n",
425162306a36Sopenharmony_ci					    ret);
425262306a36Sopenharmony_ci				ieee80211_free_txskb(ar->hw, skb);
425362306a36Sopenharmony_ci			}
425462306a36Sopenharmony_ci		}
425562306a36Sopenharmony_ci	}
425662306a36Sopenharmony_ci}
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_cistatic void ath10k_mac_txq_init(struct ieee80211_txq *txq)
425962306a36Sopenharmony_ci{
426062306a36Sopenharmony_ci	struct ath10k_txq *artxq;
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	if (!txq)
426362306a36Sopenharmony_ci		return;
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci	artxq = (void *)txq->drv_priv;
426662306a36Sopenharmony_ci	INIT_LIST_HEAD(&artxq->list);
426762306a36Sopenharmony_ci}
426862306a36Sopenharmony_ci
426962306a36Sopenharmony_cistatic void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
427062306a36Sopenharmony_ci{
427162306a36Sopenharmony_ci	struct ath10k_skb_cb *cb;
427262306a36Sopenharmony_ci	struct sk_buff *msdu;
427362306a36Sopenharmony_ci	int msdu_id;
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci	if (!txq)
427662306a36Sopenharmony_ci		return;
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
427962306a36Sopenharmony_ci	idr_for_each_entry(&ar->htt.pending_tx, msdu, msdu_id) {
428062306a36Sopenharmony_ci		cb = ATH10K_SKB_CB(msdu);
428162306a36Sopenharmony_ci		if (cb->txq == txq)
428262306a36Sopenharmony_ci			cb->txq = NULL;
428362306a36Sopenharmony_ci	}
428462306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
428562306a36Sopenharmony_ci}
428662306a36Sopenharmony_ci
428762306a36Sopenharmony_cistruct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
428862306a36Sopenharmony_ci					    u16 peer_id,
428962306a36Sopenharmony_ci					    u8 tid)
429062306a36Sopenharmony_ci{
429162306a36Sopenharmony_ci	struct ath10k_peer *peer;
429262306a36Sopenharmony_ci
429362306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	peer = ar->peer_map[peer_id];
429662306a36Sopenharmony_ci	if (!peer)
429762306a36Sopenharmony_ci		return NULL;
429862306a36Sopenharmony_ci
429962306a36Sopenharmony_ci	if (peer->removed)
430062306a36Sopenharmony_ci		return NULL;
430162306a36Sopenharmony_ci
430262306a36Sopenharmony_ci	if (peer->sta)
430362306a36Sopenharmony_ci		return peer->sta->txq[tid];
430462306a36Sopenharmony_ci	else if (peer->vif)
430562306a36Sopenharmony_ci		return peer->vif->txq;
430662306a36Sopenharmony_ci	else
430762306a36Sopenharmony_ci		return NULL;
430862306a36Sopenharmony_ci}
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_cistatic bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
431162306a36Sopenharmony_ci				   struct ieee80211_txq *txq)
431262306a36Sopenharmony_ci{
431362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
431462306a36Sopenharmony_ci	struct ath10k_txq *artxq = (void *)txq->drv_priv;
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	/* No need to get locks */
431762306a36Sopenharmony_ci	if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
431862306a36Sopenharmony_ci		return true;
431962306a36Sopenharmony_ci
432062306a36Sopenharmony_ci	if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
432162306a36Sopenharmony_ci		return true;
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci	if (artxq->num_fw_queued < artxq->num_push_allowed)
432462306a36Sopenharmony_ci		return true;
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ci	return false;
432762306a36Sopenharmony_ci}
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_ci/* Return estimated airtime in microsecond, which is calculated using last
433062306a36Sopenharmony_ci * reported TX rate. This is just a rough estimation because host driver has no
433162306a36Sopenharmony_ci * knowledge of the actual transmit rate, retries or aggregation. If actual
433262306a36Sopenharmony_ci * airtime can be reported by firmware, then delta between estimated and actual
433362306a36Sopenharmony_ci * airtime can be adjusted from deficit.
433462306a36Sopenharmony_ci */
433562306a36Sopenharmony_ci#define IEEE80211_ATF_OVERHEAD		100	/* IFS + some slot time */
433662306a36Sopenharmony_ci#define IEEE80211_ATF_OVERHEAD_IFS	16	/* IFS only */
433762306a36Sopenharmony_cistatic u16 ath10k_mac_update_airtime(struct ath10k *ar,
433862306a36Sopenharmony_ci				     struct ieee80211_txq *txq,
433962306a36Sopenharmony_ci				     struct sk_buff *skb)
434062306a36Sopenharmony_ci{
434162306a36Sopenharmony_ci	struct ath10k_sta *arsta;
434262306a36Sopenharmony_ci	u32 pktlen;
434362306a36Sopenharmony_ci	u16 airtime = 0;
434462306a36Sopenharmony_ci
434562306a36Sopenharmony_ci	if (!txq || !txq->sta)
434662306a36Sopenharmony_ci		return airtime;
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_REPORT_AIRTIME, ar->wmi.svc_map))
434962306a36Sopenharmony_ci		return airtime;
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
435262306a36Sopenharmony_ci	arsta = (struct ath10k_sta *)txq->sta->drv_priv;
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci	pktlen = skb->len + 38; /* Assume MAC header 30, SNAP 8 for most case */
435562306a36Sopenharmony_ci	if (arsta->last_tx_bitrate) {
435662306a36Sopenharmony_ci		/* airtime in us, last_tx_bitrate in 100kbps */
435762306a36Sopenharmony_ci		airtime = (pktlen * 8 * (1000 / 100))
435862306a36Sopenharmony_ci				/ arsta->last_tx_bitrate;
435962306a36Sopenharmony_ci		/* overhead for media access time and IFS */
436062306a36Sopenharmony_ci		airtime += IEEE80211_ATF_OVERHEAD_IFS;
436162306a36Sopenharmony_ci	} else {
436262306a36Sopenharmony_ci		/* This is mostly for throttle excessive BC/MC frames, and the
436362306a36Sopenharmony_ci		 * airtime/rate doesn't need be exact. Airtime of BC/MC frames
436462306a36Sopenharmony_ci		 * in 2G get some discount, which helps prevent very low rate
436562306a36Sopenharmony_ci		 * frames from being blocked for too long.
436662306a36Sopenharmony_ci		 */
436762306a36Sopenharmony_ci		airtime = (pktlen * 8 * (1000 / 100)) / 60; /* 6M */
436862306a36Sopenharmony_ci		airtime += IEEE80211_ATF_OVERHEAD;
436962306a36Sopenharmony_ci	}
437062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci	return airtime;
437362306a36Sopenharmony_ci}
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ciint ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
437662306a36Sopenharmony_ci			   struct ieee80211_txq *txq)
437762306a36Sopenharmony_ci{
437862306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
437962306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
438062306a36Sopenharmony_ci	struct ath10k_txq *artxq = (void *)txq->drv_priv;
438162306a36Sopenharmony_ci	struct ieee80211_vif *vif = txq->vif;
438262306a36Sopenharmony_ci	struct ieee80211_sta *sta = txq->sta;
438362306a36Sopenharmony_ci	enum ath10k_hw_txrx_mode txmode;
438462306a36Sopenharmony_ci	enum ath10k_mac_tx_path txpath;
438562306a36Sopenharmony_ci	struct sk_buff *skb;
438662306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
438762306a36Sopenharmony_ci	size_t skb_len;
438862306a36Sopenharmony_ci	bool is_mgmt, is_presp;
438962306a36Sopenharmony_ci	int ret;
439062306a36Sopenharmony_ci	u16 airtime;
439162306a36Sopenharmony_ci
439262306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
439362306a36Sopenharmony_ci	ret = ath10k_htt_tx_inc_pending(htt);
439462306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci	if (ret)
439762306a36Sopenharmony_ci		return ret;
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci	skb = ieee80211_tx_dequeue_ni(hw, txq);
440062306a36Sopenharmony_ci	if (!skb) {
440162306a36Sopenharmony_ci		spin_lock_bh(&ar->htt.tx_lock);
440262306a36Sopenharmony_ci		ath10k_htt_tx_dec_pending(htt);
440362306a36Sopenharmony_ci		spin_unlock_bh(&ar->htt.tx_lock);
440462306a36Sopenharmony_ci
440562306a36Sopenharmony_ci		return -ENOENT;
440662306a36Sopenharmony_ci	}
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_ci	airtime = ath10k_mac_update_airtime(ar, txq, skb);
440962306a36Sopenharmony_ci	ath10k_mac_tx_h_fill_cb(ar, vif, txq, sta, skb, airtime);
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	skb_len = skb->len;
441262306a36Sopenharmony_ci	txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
441362306a36Sopenharmony_ci	txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
441462306a36Sopenharmony_ci	is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT);
441562306a36Sopenharmony_ci
441662306a36Sopenharmony_ci	if (is_mgmt) {
441762306a36Sopenharmony_ci		hdr = (struct ieee80211_hdr *)skb->data;
441862306a36Sopenharmony_ci		is_presp = ieee80211_is_probe_resp(hdr->frame_control);
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci		spin_lock_bh(&ar->htt.tx_lock);
442162306a36Sopenharmony_ci		ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp);
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci		if (ret) {
442462306a36Sopenharmony_ci			ath10k_htt_tx_dec_pending(htt);
442562306a36Sopenharmony_ci			spin_unlock_bh(&ar->htt.tx_lock);
442662306a36Sopenharmony_ci			return ret;
442762306a36Sopenharmony_ci		}
442862306a36Sopenharmony_ci		spin_unlock_bh(&ar->htt.tx_lock);
442962306a36Sopenharmony_ci	}
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci	ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, false);
443262306a36Sopenharmony_ci	if (unlikely(ret)) {
443362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to push frame: %d\n", ret);
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci		spin_lock_bh(&ar->htt.tx_lock);
443662306a36Sopenharmony_ci		ath10k_htt_tx_dec_pending(htt);
443762306a36Sopenharmony_ci		if (is_mgmt)
443862306a36Sopenharmony_ci			ath10k_htt_tx_mgmt_dec_pending(htt);
443962306a36Sopenharmony_ci		spin_unlock_bh(&ar->htt.tx_lock);
444062306a36Sopenharmony_ci
444162306a36Sopenharmony_ci		return ret;
444262306a36Sopenharmony_ci	}
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
444562306a36Sopenharmony_ci	artxq->num_fw_queued++;
444662306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
444762306a36Sopenharmony_ci
444862306a36Sopenharmony_ci	return skb_len;
444962306a36Sopenharmony_ci}
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_cistatic int ath10k_mac_schedule_txq(struct ieee80211_hw *hw, u32 ac)
445262306a36Sopenharmony_ci{
445362306a36Sopenharmony_ci	struct ieee80211_txq *txq;
445462306a36Sopenharmony_ci	int ret = 0;
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_ci	ieee80211_txq_schedule_start(hw, ac);
445762306a36Sopenharmony_ci	while ((txq = ieee80211_next_txq(hw, ac))) {
445862306a36Sopenharmony_ci		while (ath10k_mac_tx_can_push(hw, txq)) {
445962306a36Sopenharmony_ci			ret = ath10k_mac_tx_push_txq(hw, txq);
446062306a36Sopenharmony_ci			if (ret < 0)
446162306a36Sopenharmony_ci				break;
446262306a36Sopenharmony_ci		}
446362306a36Sopenharmony_ci		ieee80211_return_txq(hw, txq, false);
446462306a36Sopenharmony_ci		ath10k_htt_tx_txq_update(hw, txq);
446562306a36Sopenharmony_ci		if (ret == -EBUSY)
446662306a36Sopenharmony_ci			break;
446762306a36Sopenharmony_ci	}
446862306a36Sopenharmony_ci	ieee80211_txq_schedule_end(hw, ac);
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ci	return ret;
447162306a36Sopenharmony_ci}
447262306a36Sopenharmony_ci
447362306a36Sopenharmony_civoid ath10k_mac_tx_push_pending(struct ath10k *ar)
447462306a36Sopenharmony_ci{
447562306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
447662306a36Sopenharmony_ci	u32 ac;
447762306a36Sopenharmony_ci
447862306a36Sopenharmony_ci	if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH)
447962306a36Sopenharmony_ci		return;
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_ci	if (ar->htt.num_pending_tx >= (ar->htt.max_num_pending_tx / 2))
448262306a36Sopenharmony_ci		return;
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	rcu_read_lock();
448562306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
448662306a36Sopenharmony_ci		if (ath10k_mac_schedule_txq(hw, ac) == -EBUSY)
448762306a36Sopenharmony_ci			break;
448862306a36Sopenharmony_ci	}
448962306a36Sopenharmony_ci	rcu_read_unlock();
449062306a36Sopenharmony_ci}
449162306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_mac_tx_push_pending);
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci/************/
449462306a36Sopenharmony_ci/* Scanning */
449562306a36Sopenharmony_ci/************/
449662306a36Sopenharmony_ci
449762306a36Sopenharmony_civoid __ath10k_scan_finish(struct ath10k *ar)
449862306a36Sopenharmony_ci{
449962306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci	switch (ar->scan.state) {
450262306a36Sopenharmony_ci	case ATH10K_SCAN_IDLE:
450362306a36Sopenharmony_ci		break;
450462306a36Sopenharmony_ci	case ATH10K_SCAN_RUNNING:
450562306a36Sopenharmony_ci	case ATH10K_SCAN_ABORTING:
450662306a36Sopenharmony_ci		if (!ar->scan.is_roc) {
450762306a36Sopenharmony_ci			struct cfg80211_scan_info info = {
450862306a36Sopenharmony_ci				.aborted = (ar->scan.state ==
450962306a36Sopenharmony_ci					    ATH10K_SCAN_ABORTING),
451062306a36Sopenharmony_ci			};
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_ci			ieee80211_scan_completed(ar->hw, &info);
451362306a36Sopenharmony_ci		} else if (ar->scan.roc_notify) {
451462306a36Sopenharmony_ci			ieee80211_remain_on_channel_expired(ar->hw);
451562306a36Sopenharmony_ci		}
451662306a36Sopenharmony_ci		fallthrough;
451762306a36Sopenharmony_ci	case ATH10K_SCAN_STARTING:
451862306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_IDLE;
451962306a36Sopenharmony_ci		ar->scan_channel = NULL;
452062306a36Sopenharmony_ci		ar->scan.roc_freq = 0;
452162306a36Sopenharmony_ci		ath10k_offchan_tx_purge(ar);
452262306a36Sopenharmony_ci		cancel_delayed_work(&ar->scan.timeout);
452362306a36Sopenharmony_ci		complete(&ar->scan.completed);
452462306a36Sopenharmony_ci		break;
452562306a36Sopenharmony_ci	}
452662306a36Sopenharmony_ci}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_civoid ath10k_scan_finish(struct ath10k *ar)
452962306a36Sopenharmony_ci{
453062306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
453162306a36Sopenharmony_ci	__ath10k_scan_finish(ar);
453262306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
453362306a36Sopenharmony_ci}
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_cistatic int ath10k_scan_stop(struct ath10k *ar)
453662306a36Sopenharmony_ci{
453762306a36Sopenharmony_ci	struct wmi_stop_scan_arg arg = {
453862306a36Sopenharmony_ci		.req_id = 1, /* FIXME */
453962306a36Sopenharmony_ci		.req_type = WMI_SCAN_STOP_ONE,
454062306a36Sopenharmony_ci		.u.scan_id = ATH10K_SCAN_ID,
454162306a36Sopenharmony_ci	};
454262306a36Sopenharmony_ci	int ret;
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_ci	ret = ath10k_wmi_stop_scan(ar, &arg);
454762306a36Sopenharmony_ci	if (ret) {
454862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop wmi scan: %d\n", ret);
454962306a36Sopenharmony_ci		goto out;
455062306a36Sopenharmony_ci	}
455162306a36Sopenharmony_ci
455262306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ);
455362306a36Sopenharmony_ci	if (ret == 0) {
455462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to receive scan abortion completion: timed out\n");
455562306a36Sopenharmony_ci		ret = -ETIMEDOUT;
455662306a36Sopenharmony_ci	} else if (ret > 0) {
455762306a36Sopenharmony_ci		ret = 0;
455862306a36Sopenharmony_ci	}
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ciout:
456162306a36Sopenharmony_ci	/* Scan state should be updated upon scan completion but in case
456262306a36Sopenharmony_ci	 * firmware fails to deliver the event (for whatever reason) it is
456362306a36Sopenharmony_ci	 * desired to clean up scan state anyway. Firmware may have just
456462306a36Sopenharmony_ci	 * dropped the scan completion event delivery due to transport pipe
456562306a36Sopenharmony_ci	 * being overflown with data and/or it can recover on its own before
456662306a36Sopenharmony_ci	 * next scan request is submitted.
456762306a36Sopenharmony_ci	 */
456862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
456962306a36Sopenharmony_ci	if (ar->scan.state != ATH10K_SCAN_IDLE)
457062306a36Sopenharmony_ci		__ath10k_scan_finish(ar);
457162306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci	return ret;
457462306a36Sopenharmony_ci}
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_cistatic void ath10k_scan_abort(struct ath10k *ar)
457762306a36Sopenharmony_ci{
457862306a36Sopenharmony_ci	int ret;
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
458162306a36Sopenharmony_ci
458262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	switch (ar->scan.state) {
458562306a36Sopenharmony_ci	case ATH10K_SCAN_IDLE:
458662306a36Sopenharmony_ci		/* This can happen if timeout worker kicked in and called
458762306a36Sopenharmony_ci		 * abortion while scan completion was being processed.
458862306a36Sopenharmony_ci		 */
458962306a36Sopenharmony_ci		break;
459062306a36Sopenharmony_ci	case ATH10K_SCAN_STARTING:
459162306a36Sopenharmony_ci	case ATH10K_SCAN_ABORTING:
459262306a36Sopenharmony_ci		ath10k_warn(ar, "refusing scan abortion due to invalid scan state: %s (%d)\n",
459362306a36Sopenharmony_ci			    ath10k_scan_state_str(ar->scan.state),
459462306a36Sopenharmony_ci			    ar->scan.state);
459562306a36Sopenharmony_ci		break;
459662306a36Sopenharmony_ci	case ATH10K_SCAN_RUNNING:
459762306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_ABORTING;
459862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci		ret = ath10k_scan_stop(ar);
460162306a36Sopenharmony_ci		if (ret)
460262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to abort scan: %d\n", ret);
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
460562306a36Sopenharmony_ci		break;
460662306a36Sopenharmony_ci	}
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
460962306a36Sopenharmony_ci}
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_civoid ath10k_scan_timeout_work(struct work_struct *work)
461262306a36Sopenharmony_ci{
461362306a36Sopenharmony_ci	struct ath10k *ar = container_of(work, struct ath10k,
461462306a36Sopenharmony_ci					 scan.timeout.work);
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
461762306a36Sopenharmony_ci	ath10k_scan_abort(ar);
461862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
461962306a36Sopenharmony_ci}
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_cistatic int ath10k_start_scan(struct ath10k *ar,
462262306a36Sopenharmony_ci			     const struct wmi_start_scan_arg *arg)
462362306a36Sopenharmony_ci{
462462306a36Sopenharmony_ci	int ret;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci	ret = ath10k_wmi_start_scan(ar, arg);
462962306a36Sopenharmony_ci	if (ret)
463062306a36Sopenharmony_ci		return ret;
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ);
463362306a36Sopenharmony_ci	if (ret == 0) {
463462306a36Sopenharmony_ci		ret = ath10k_scan_stop(ar);
463562306a36Sopenharmony_ci		if (ret)
463662306a36Sopenharmony_ci			ath10k_warn(ar, "failed to stop scan: %d\n", ret);
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci		return -ETIMEDOUT;
463962306a36Sopenharmony_ci	}
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_ci	/* If we failed to start the scan, return error code at
464262306a36Sopenharmony_ci	 * this point.  This is probably due to some issue in the
464362306a36Sopenharmony_ci	 * firmware, but no need to wedge the driver due to that...
464462306a36Sopenharmony_ci	 */
464562306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
464662306a36Sopenharmony_ci	if (ar->scan.state == ATH10K_SCAN_IDLE) {
464762306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
464862306a36Sopenharmony_ci		return -EINVAL;
464962306a36Sopenharmony_ci	}
465062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci	return 0;
465362306a36Sopenharmony_ci}
465462306a36Sopenharmony_ci
465562306a36Sopenharmony_ci/**********************/
465662306a36Sopenharmony_ci/* mac80211 callbacks */
465762306a36Sopenharmony_ci/**********************/
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_cistatic void ath10k_mac_op_tx(struct ieee80211_hw *hw,
466062306a36Sopenharmony_ci			     struct ieee80211_tx_control *control,
466162306a36Sopenharmony_ci			     struct sk_buff *skb)
466262306a36Sopenharmony_ci{
466362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
466462306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
466562306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
466662306a36Sopenharmony_ci	struct ieee80211_vif *vif = info->control.vif;
466762306a36Sopenharmony_ci	struct ieee80211_sta *sta = control->sta;
466862306a36Sopenharmony_ci	struct ieee80211_txq *txq = NULL;
466962306a36Sopenharmony_ci	enum ath10k_hw_txrx_mode txmode;
467062306a36Sopenharmony_ci	enum ath10k_mac_tx_path txpath;
467162306a36Sopenharmony_ci	bool is_htt;
467262306a36Sopenharmony_ci	bool is_mgmt;
467362306a36Sopenharmony_ci	int ret;
467462306a36Sopenharmony_ci	u16 airtime;
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	airtime = ath10k_mac_update_airtime(ar, txq, skb);
467762306a36Sopenharmony_ci	ath10k_mac_tx_h_fill_cb(ar, vif, txq, sta, skb, airtime);
467862306a36Sopenharmony_ci
467962306a36Sopenharmony_ci	txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
468062306a36Sopenharmony_ci	txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
468162306a36Sopenharmony_ci	is_htt = (txpath == ATH10K_MAC_TX_HTT ||
468262306a36Sopenharmony_ci		  txpath == ATH10K_MAC_TX_HTT_MGMT);
468362306a36Sopenharmony_ci	is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT);
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_ci	if (is_htt) {
468662306a36Sopenharmony_ci		bool is_presp = false;
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci		spin_lock_bh(&ar->htt.tx_lock);
468962306a36Sopenharmony_ci		if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
469062306a36Sopenharmony_ci			struct ieee80211_hdr *hdr = (void *)skb->data;
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_ci			is_presp = ieee80211_is_probe_resp(hdr->frame_control);
469362306a36Sopenharmony_ci		}
469462306a36Sopenharmony_ci
469562306a36Sopenharmony_ci		ret = ath10k_htt_tx_inc_pending(htt);
469662306a36Sopenharmony_ci		if (ret) {
469762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
469862306a36Sopenharmony_ci				    ret);
469962306a36Sopenharmony_ci			spin_unlock_bh(&ar->htt.tx_lock);
470062306a36Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
470162306a36Sopenharmony_ci			return;
470262306a36Sopenharmony_ci		}
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci		ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp);
470562306a36Sopenharmony_ci		if (ret) {
470662306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
470762306a36Sopenharmony_ci				   ret);
470862306a36Sopenharmony_ci			ath10k_htt_tx_dec_pending(htt);
470962306a36Sopenharmony_ci			spin_unlock_bh(&ar->htt.tx_lock);
471062306a36Sopenharmony_ci			ieee80211_free_txskb(ar->hw, skb);
471162306a36Sopenharmony_ci			return;
471262306a36Sopenharmony_ci		}
471362306a36Sopenharmony_ci		spin_unlock_bh(&ar->htt.tx_lock);
471462306a36Sopenharmony_ci	}
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_ci	ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, false);
471762306a36Sopenharmony_ci	if (ret) {
471862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
471962306a36Sopenharmony_ci		if (is_htt) {
472062306a36Sopenharmony_ci			spin_lock_bh(&ar->htt.tx_lock);
472162306a36Sopenharmony_ci			ath10k_htt_tx_dec_pending(htt);
472262306a36Sopenharmony_ci			if (is_mgmt)
472362306a36Sopenharmony_ci				ath10k_htt_tx_mgmt_dec_pending(htt);
472462306a36Sopenharmony_ci			spin_unlock_bh(&ar->htt.tx_lock);
472562306a36Sopenharmony_ci		}
472662306a36Sopenharmony_ci		return;
472762306a36Sopenharmony_ci	}
472862306a36Sopenharmony_ci}
472962306a36Sopenharmony_ci
473062306a36Sopenharmony_cistatic void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
473162306a36Sopenharmony_ci					struct ieee80211_txq *txq)
473262306a36Sopenharmony_ci{
473362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
473462306a36Sopenharmony_ci	int ret;
473562306a36Sopenharmony_ci	u8 ac = txq->ac;
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci	ath10k_htt_tx_txq_update(hw, txq);
473862306a36Sopenharmony_ci	if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH)
473962306a36Sopenharmony_ci		return;
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	spin_lock_bh(&ar->queue_lock[ac]);
474262306a36Sopenharmony_ci
474362306a36Sopenharmony_ci	ieee80211_txq_schedule_start(hw, ac);
474462306a36Sopenharmony_ci	txq = ieee80211_next_txq(hw, ac);
474562306a36Sopenharmony_ci	if (!txq)
474662306a36Sopenharmony_ci		goto out;
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_ci	while (ath10k_mac_tx_can_push(hw, txq)) {
474962306a36Sopenharmony_ci		ret = ath10k_mac_tx_push_txq(hw, txq);
475062306a36Sopenharmony_ci		if (ret < 0)
475162306a36Sopenharmony_ci			break;
475262306a36Sopenharmony_ci	}
475362306a36Sopenharmony_ci	ieee80211_return_txq(hw, txq, false);
475462306a36Sopenharmony_ci	ath10k_htt_tx_txq_update(hw, txq);
475562306a36Sopenharmony_ciout:
475662306a36Sopenharmony_ci	ieee80211_txq_schedule_end(hw, ac);
475762306a36Sopenharmony_ci	spin_unlock_bh(&ar->queue_lock[ac]);
475862306a36Sopenharmony_ci}
475962306a36Sopenharmony_ci
476062306a36Sopenharmony_ci/* Must not be called with conf_mutex held as workers can use that also. */
476162306a36Sopenharmony_civoid ath10k_drain_tx(struct ath10k *ar)
476262306a36Sopenharmony_ci{
476362306a36Sopenharmony_ci	lockdep_assert_not_held(&ar->conf_mutex);
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	/* make sure rcu-protected mac80211 tx path itself is drained */
476662306a36Sopenharmony_ci	synchronize_net();
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci	ath10k_offchan_tx_purge(ar);
476962306a36Sopenharmony_ci	ath10k_mgmt_over_wmi_tx_purge(ar);
477062306a36Sopenharmony_ci
477162306a36Sopenharmony_ci	cancel_work_sync(&ar->offchan_tx_work);
477262306a36Sopenharmony_ci	cancel_work_sync(&ar->wmi_mgmt_tx_work);
477362306a36Sopenharmony_ci}
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_civoid ath10k_halt(struct ath10k *ar)
477662306a36Sopenharmony_ci{
477762306a36Sopenharmony_ci	struct ath10k_vif *arvif;
477862306a36Sopenharmony_ci
477962306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci	clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
478262306a36Sopenharmony_ci	ar->filter_flags = 0;
478362306a36Sopenharmony_ci	ar->monitor = false;
478462306a36Sopenharmony_ci	ar->monitor_arvif = NULL;
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_ci	if (ar->monitor_started)
478762306a36Sopenharmony_ci		ath10k_monitor_stop(ar);
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci	ar->monitor_started = false;
479062306a36Sopenharmony_ci	ar->tx_paused = 0;
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	ath10k_scan_finish(ar);
479362306a36Sopenharmony_ci	ath10k_peer_cleanup_all(ar);
479462306a36Sopenharmony_ci	ath10k_stop_radar_confirmation(ar);
479562306a36Sopenharmony_ci	ath10k_core_stop(ar);
479662306a36Sopenharmony_ci	ath10k_hif_power_down(ar);
479762306a36Sopenharmony_ci
479862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
479962306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list)
480062306a36Sopenharmony_ci		ath10k_mac_vif_beacon_cleanup(arvif);
480162306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
480262306a36Sopenharmony_ci}
480362306a36Sopenharmony_ci
480462306a36Sopenharmony_cistatic int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
480562306a36Sopenharmony_ci{
480662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
480762306a36Sopenharmony_ci
480862306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
480962306a36Sopenharmony_ci
481062306a36Sopenharmony_ci	*tx_ant = ar->cfg_tx_chainmask;
481162306a36Sopenharmony_ci	*rx_ant = ar->cfg_rx_chainmask;
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
481462306a36Sopenharmony_ci
481562306a36Sopenharmony_ci	return 0;
481662306a36Sopenharmony_ci}
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_cistatic bool ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
481962306a36Sopenharmony_ci{
482062306a36Sopenharmony_ci	/* It is not clear that allowing gaps in chainmask
482162306a36Sopenharmony_ci	 * is helpful.  Probably it will not do what user
482262306a36Sopenharmony_ci	 * is hoping for, so warn in that case.
482362306a36Sopenharmony_ci	 */
482462306a36Sopenharmony_ci	if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
482562306a36Sopenharmony_ci		return true;
482662306a36Sopenharmony_ci
482762306a36Sopenharmony_ci	ath10k_warn(ar, "mac %s antenna chainmask is invalid: 0x%x.  Suggested values: 15, 7, 3, 1 or 0.\n",
482862306a36Sopenharmony_ci		    dbg, cm);
482962306a36Sopenharmony_ci	return false;
483062306a36Sopenharmony_ci}
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_cistatic int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar)
483362306a36Sopenharmony_ci{
483462306a36Sopenharmony_ci	int nsts = ar->vht_cap_info;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci	nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
483762306a36Sopenharmony_ci	nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci	/* If firmware does not deliver to host number of space-time
484062306a36Sopenharmony_ci	 * streams supported, assume it support up to 4 BF STS and return
484162306a36Sopenharmony_ci	 * the value for VHT CAP: nsts-1)
484262306a36Sopenharmony_ci	 */
484362306a36Sopenharmony_ci	if (nsts == 0)
484462306a36Sopenharmony_ci		return 3;
484562306a36Sopenharmony_ci
484662306a36Sopenharmony_ci	return nsts;
484762306a36Sopenharmony_ci}
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_cistatic int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar)
485062306a36Sopenharmony_ci{
485162306a36Sopenharmony_ci	int sound_dim = ar->vht_cap_info;
485262306a36Sopenharmony_ci
485362306a36Sopenharmony_ci	sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
485462306a36Sopenharmony_ci	sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci	/* If the sounding dimension is not advertised by the firmware,
485762306a36Sopenharmony_ci	 * let's use a default value of 1
485862306a36Sopenharmony_ci	 */
485962306a36Sopenharmony_ci	if (sound_dim == 0)
486062306a36Sopenharmony_ci		return 1;
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci	return sound_dim;
486362306a36Sopenharmony_ci}
486462306a36Sopenharmony_ci
486562306a36Sopenharmony_cistatic struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
486662306a36Sopenharmony_ci{
486762306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap vht_cap = {0};
486862306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
486962306a36Sopenharmony_ci	u16 mcs_map;
487062306a36Sopenharmony_ci	u32 val;
487162306a36Sopenharmony_ci	int i;
487262306a36Sopenharmony_ci
487362306a36Sopenharmony_ci	vht_cap.vht_supported = 1;
487462306a36Sopenharmony_ci	vht_cap.cap = ar->vht_cap_info;
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
487762306a36Sopenharmony_ci				IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
487862306a36Sopenharmony_ci		val = ath10k_mac_get_vht_cap_bf_sts(ar);
487962306a36Sopenharmony_ci		val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
488062306a36Sopenharmony_ci		val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci		vht_cap.cap |= val;
488362306a36Sopenharmony_ci	}
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
488662306a36Sopenharmony_ci				IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
488762306a36Sopenharmony_ci		val = ath10k_mac_get_vht_cap_bf_sound_dim(ar);
488862306a36Sopenharmony_ci		val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
488962306a36Sopenharmony_ci		val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
489062306a36Sopenharmony_ci
489162306a36Sopenharmony_ci		vht_cap.cap |= val;
489262306a36Sopenharmony_ci	}
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_ci	mcs_map = 0;
489562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
489662306a36Sopenharmony_ci		if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i)))
489762306a36Sopenharmony_ci			mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
489862306a36Sopenharmony_ci		else
489962306a36Sopenharmony_ci			mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
490062306a36Sopenharmony_ci	}
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	if (ar->cfg_tx_chainmask <= 1)
490362306a36Sopenharmony_ci		vht_cap.cap &= ~IEEE80211_VHT_CAP_TXSTBC;
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci	vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
490662306a36Sopenharmony_ci	vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
490762306a36Sopenharmony_ci
490862306a36Sopenharmony_ci	/* If we are supporting 160Mhz or 80+80, then the NIC may be able to do
490962306a36Sopenharmony_ci	 * a restricted NSS for 160 or 80+80 vs what it can do for 80Mhz.  Give
491062306a36Sopenharmony_ci	 * user-space a clue if that is the case.
491162306a36Sopenharmony_ci	 */
491262306a36Sopenharmony_ci	if ((vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) &&
491362306a36Sopenharmony_ci	    (hw->vht160_mcs_rx_highest != 0 ||
491462306a36Sopenharmony_ci	     hw->vht160_mcs_tx_highest != 0)) {
491562306a36Sopenharmony_ci		vht_cap.vht_mcs.rx_highest = cpu_to_le16(hw->vht160_mcs_rx_highest);
491662306a36Sopenharmony_ci		vht_cap.vht_mcs.tx_highest = cpu_to_le16(hw->vht160_mcs_tx_highest);
491762306a36Sopenharmony_ci	}
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	return vht_cap;
492062306a36Sopenharmony_ci}
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_cistatic struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
492362306a36Sopenharmony_ci{
492462306a36Sopenharmony_ci	int i;
492562306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap ht_cap = {0};
492662306a36Sopenharmony_ci
492762306a36Sopenharmony_ci	if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
492862306a36Sopenharmony_ci		return ht_cap;
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	ht_cap.ht_supported = 1;
493162306a36Sopenharmony_ci	ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
493262306a36Sopenharmony_ci	ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
493362306a36Sopenharmony_ci	ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
493462306a36Sopenharmony_ci	ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
493562306a36Sopenharmony_ci	ht_cap.cap |=
493662306a36Sopenharmony_ci		WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT;
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
493962306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
494062306a36Sopenharmony_ci
494162306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
494262306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
494562306a36Sopenharmony_ci		u32 smps;
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci		smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
494862306a36Sopenharmony_ci		smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_ci		ht_cap.cap |= smps;
495162306a36Sopenharmony_ci	}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC && (ar->cfg_tx_chainmask > 1))
495462306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
495562306a36Sopenharmony_ci
495662306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
495762306a36Sopenharmony_ci		u32 stbc;
495862306a36Sopenharmony_ci
495962306a36Sopenharmony_ci		stbc   = ar->ht_cap_info;
496062306a36Sopenharmony_ci		stbc  &= WMI_HT_CAP_RX_STBC;
496162306a36Sopenharmony_ci		stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
496262306a36Sopenharmony_ci		stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
496362306a36Sopenharmony_ci		stbc  &= IEEE80211_HT_CAP_RX_STBC;
496462306a36Sopenharmony_ci
496562306a36Sopenharmony_ci		ht_cap.cap |= stbc;
496662306a36Sopenharmony_ci	}
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_LDPC || (ar->ht_cap_info &
496962306a36Sopenharmony_ci	    WMI_HT_CAP_RX_LDPC && (ar->ht_cap_info & WMI_HT_CAP_TX_LDPC)))
497062306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
497362306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci	/* max AMSDU is implicitly taken from vht_cap_info */
497662306a36Sopenharmony_ci	if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
497762306a36Sopenharmony_ci		ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	for (i = 0; i < ar->num_rf_chains; i++) {
498062306a36Sopenharmony_ci		if (ar->cfg_rx_chainmask & BIT(i))
498162306a36Sopenharmony_ci			ht_cap.mcs.rx_mask[i] = 0xFF;
498262306a36Sopenharmony_ci	}
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_ci	ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
498562306a36Sopenharmony_ci
498662306a36Sopenharmony_ci	return ht_cap;
498762306a36Sopenharmony_ci}
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_cistatic void ath10k_mac_setup_ht_vht_cap(struct ath10k *ar)
499062306a36Sopenharmony_ci{
499162306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
499262306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap vht_cap;
499362306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap ht_cap;
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ci	ht_cap = ath10k_get_ht_cap(ar);
499662306a36Sopenharmony_ci	vht_cap = ath10k_create_vht_cap(ar);
499762306a36Sopenharmony_ci
499862306a36Sopenharmony_ci	if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
499962306a36Sopenharmony_ci		band = &ar->mac.sbands[NL80211_BAND_2GHZ];
500062306a36Sopenharmony_ci		band->ht_cap = ht_cap;
500162306a36Sopenharmony_ci	}
500262306a36Sopenharmony_ci	if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
500362306a36Sopenharmony_ci		band = &ar->mac.sbands[NL80211_BAND_5GHZ];
500462306a36Sopenharmony_ci		band->ht_cap = ht_cap;
500562306a36Sopenharmony_ci		band->vht_cap = vht_cap;
500662306a36Sopenharmony_ci	}
500762306a36Sopenharmony_ci}
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_cistatic int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
501062306a36Sopenharmony_ci{
501162306a36Sopenharmony_ci	int ret;
501262306a36Sopenharmony_ci	bool is_valid_tx_chain_mask, is_valid_rx_chain_mask;
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
501562306a36Sopenharmony_ci
501662306a36Sopenharmony_ci	is_valid_tx_chain_mask = ath10k_check_chain_mask(ar, tx_ant, "tx");
501762306a36Sopenharmony_ci	is_valid_rx_chain_mask = ath10k_check_chain_mask(ar, rx_ant, "rx");
501862306a36Sopenharmony_ci
501962306a36Sopenharmony_ci	if (!is_valid_tx_chain_mask || !is_valid_rx_chain_mask)
502062306a36Sopenharmony_ci		return -EINVAL;
502162306a36Sopenharmony_ci
502262306a36Sopenharmony_ci	ar->cfg_tx_chainmask = tx_ant;
502362306a36Sopenharmony_ci	ar->cfg_rx_chainmask = rx_ant;
502462306a36Sopenharmony_ci
502562306a36Sopenharmony_ci	if ((ar->state != ATH10K_STATE_ON) &&
502662306a36Sopenharmony_ci	    (ar->state != ATH10K_STATE_RESTARTED))
502762306a36Sopenharmony_ci		return 0;
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask,
503062306a36Sopenharmony_ci					tx_ant);
503162306a36Sopenharmony_ci	if (ret) {
503262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set tx-chainmask: %d, req 0x%x\n",
503362306a36Sopenharmony_ci			    ret, tx_ant);
503462306a36Sopenharmony_ci		return ret;
503562306a36Sopenharmony_ci	}
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask,
503862306a36Sopenharmony_ci					rx_ant);
503962306a36Sopenharmony_ci	if (ret) {
504062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set rx-chainmask: %d, req 0x%x\n",
504162306a36Sopenharmony_ci			    ret, rx_ant);
504262306a36Sopenharmony_ci		return ret;
504362306a36Sopenharmony_ci	}
504462306a36Sopenharmony_ci
504562306a36Sopenharmony_ci	/* Reload HT/VHT capability */
504662306a36Sopenharmony_ci	ath10k_mac_setup_ht_vht_cap(ar);
504762306a36Sopenharmony_ci
504862306a36Sopenharmony_ci	return 0;
504962306a36Sopenharmony_ci}
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_cistatic int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
505262306a36Sopenharmony_ci{
505362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
505462306a36Sopenharmony_ci	int ret;
505562306a36Sopenharmony_ci
505662306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
505762306a36Sopenharmony_ci	ret = __ath10k_set_antenna(ar, tx_ant, rx_ant);
505862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
505962306a36Sopenharmony_ci	return ret;
506062306a36Sopenharmony_ci}
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_cistatic int __ath10k_fetch_bb_timing_dt(struct ath10k *ar,
506362306a36Sopenharmony_ci				       struct wmi_bb_timing_cfg_arg *bb_timing)
506462306a36Sopenharmony_ci{
506562306a36Sopenharmony_ci	struct device_node *node;
506662306a36Sopenharmony_ci	const char *fem_name;
506762306a36Sopenharmony_ci	int ret;
506862306a36Sopenharmony_ci
506962306a36Sopenharmony_ci	node = ar->dev->of_node;
507062306a36Sopenharmony_ci	if (!node)
507162306a36Sopenharmony_ci		return -ENOENT;
507262306a36Sopenharmony_ci
507362306a36Sopenharmony_ci	ret = of_property_read_string_index(node, "ext-fem-name", 0, &fem_name);
507462306a36Sopenharmony_ci	if (ret)
507562306a36Sopenharmony_ci		return -ENOENT;
507662306a36Sopenharmony_ci
507762306a36Sopenharmony_ci	/*
507862306a36Sopenharmony_ci	 * If external Front End module used in hardware, then default base band timing
507962306a36Sopenharmony_ci	 * parameter cannot be used since they were fine tuned for reference hardware,
508062306a36Sopenharmony_ci	 * so choosing different value suitable for that external FEM.
508162306a36Sopenharmony_ci	 */
508262306a36Sopenharmony_ci	if (!strcmp("microsemi-lx5586", fem_name)) {
508362306a36Sopenharmony_ci		bb_timing->bb_tx_timing = 0x00;
508462306a36Sopenharmony_ci		bb_timing->bb_xpa_timing = 0x0101;
508562306a36Sopenharmony_ci	} else {
508662306a36Sopenharmony_ci		return -ENOENT;
508762306a36Sopenharmony_ci	}
508862306a36Sopenharmony_ci
508962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot bb_tx_timing 0x%x bb_xpa_timing 0x%x\n",
509062306a36Sopenharmony_ci		   bb_timing->bb_tx_timing, bb_timing->bb_xpa_timing);
509162306a36Sopenharmony_ci	return 0;
509262306a36Sopenharmony_ci}
509362306a36Sopenharmony_ci
509462306a36Sopenharmony_cistatic int ath10k_mac_rfkill_config(struct ath10k *ar)
509562306a36Sopenharmony_ci{
509662306a36Sopenharmony_ci	u32 param;
509762306a36Sopenharmony_ci	int ret;
509862306a36Sopenharmony_ci
509962306a36Sopenharmony_ci	if (ar->hw_values->rfkill_pin == 0) {
510062306a36Sopenharmony_ci		ath10k_warn(ar, "ath10k does not support hardware rfkill with this device\n");
510162306a36Sopenharmony_ci		return -EOPNOTSUPP;
510262306a36Sopenharmony_ci	}
510362306a36Sopenharmony_ci
510462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
510562306a36Sopenharmony_ci		   "mac rfkill_pin %d rfkill_cfg %d rfkill_on_level %d",
510662306a36Sopenharmony_ci		   ar->hw_values->rfkill_pin, ar->hw_values->rfkill_cfg,
510762306a36Sopenharmony_ci		   ar->hw_values->rfkill_on_level);
510862306a36Sopenharmony_ci
510962306a36Sopenharmony_ci	param = FIELD_PREP(WMI_TLV_RFKILL_CFG_RADIO_LEVEL,
511062306a36Sopenharmony_ci			   ar->hw_values->rfkill_on_level) |
511162306a36Sopenharmony_ci		FIELD_PREP(WMI_TLV_RFKILL_CFG_GPIO_PIN_NUM,
511262306a36Sopenharmony_ci			   ar->hw_values->rfkill_pin) |
511362306a36Sopenharmony_ci		FIELD_PREP(WMI_TLV_RFKILL_CFG_PIN_AS_GPIO,
511462306a36Sopenharmony_ci			   ar->hw_values->rfkill_cfg);
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar,
511762306a36Sopenharmony_ci					ar->wmi.pdev_param->rfkill_config,
511862306a36Sopenharmony_ci					param);
511962306a36Sopenharmony_ci	if (ret) {
512062306a36Sopenharmony_ci		ath10k_warn(ar,
512162306a36Sopenharmony_ci			    "failed to set rfkill config 0x%x: %d\n",
512262306a36Sopenharmony_ci			    param, ret);
512362306a36Sopenharmony_ci		return ret;
512462306a36Sopenharmony_ci	}
512562306a36Sopenharmony_ci	return 0;
512662306a36Sopenharmony_ci}
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ciint ath10k_mac_rfkill_enable_radio(struct ath10k *ar, bool enable)
512962306a36Sopenharmony_ci{
513062306a36Sopenharmony_ci	enum wmi_tlv_rfkill_enable_radio param;
513162306a36Sopenharmony_ci	int ret;
513262306a36Sopenharmony_ci
513362306a36Sopenharmony_ci	if (enable)
513462306a36Sopenharmony_ci		param = WMI_TLV_RFKILL_ENABLE_RADIO_ON;
513562306a36Sopenharmony_ci	else
513662306a36Sopenharmony_ci		param = WMI_TLV_RFKILL_ENABLE_RADIO_OFF;
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac rfkill enable %d", param);
513962306a36Sopenharmony_ci
514062306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rfkill_enable,
514162306a36Sopenharmony_ci					param);
514262306a36Sopenharmony_ci	if (ret) {
514362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set rfkill enable param %d: %d\n",
514462306a36Sopenharmony_ci			    param, ret);
514562306a36Sopenharmony_ci		return ret;
514662306a36Sopenharmony_ci	}
514762306a36Sopenharmony_ci
514862306a36Sopenharmony_ci	return 0;
514962306a36Sopenharmony_ci}
515062306a36Sopenharmony_ci
515162306a36Sopenharmony_cistatic int ath10k_start(struct ieee80211_hw *hw)
515262306a36Sopenharmony_ci{
515362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
515462306a36Sopenharmony_ci	u32 param;
515562306a36Sopenharmony_ci	int ret = 0;
515662306a36Sopenharmony_ci	struct wmi_bb_timing_cfg_arg bb_timing = {0};
515762306a36Sopenharmony_ci
515862306a36Sopenharmony_ci	/*
515962306a36Sopenharmony_ci	 * This makes sense only when restarting hw. It is harmless to call
516062306a36Sopenharmony_ci	 * unconditionally. This is necessary to make sure no HTT/WMI tx
516162306a36Sopenharmony_ci	 * commands will be submitted while restarting.
516262306a36Sopenharmony_ci	 */
516362306a36Sopenharmony_ci	ath10k_drain_tx(ar);
516462306a36Sopenharmony_ci
516562306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
516662306a36Sopenharmony_ci
516762306a36Sopenharmony_ci	switch (ar->state) {
516862306a36Sopenharmony_ci	case ATH10K_STATE_OFF:
516962306a36Sopenharmony_ci		ar->state = ATH10K_STATE_ON;
517062306a36Sopenharmony_ci		break;
517162306a36Sopenharmony_ci	case ATH10K_STATE_RESTARTING:
517262306a36Sopenharmony_ci		ar->state = ATH10K_STATE_RESTARTED;
517362306a36Sopenharmony_ci		break;
517462306a36Sopenharmony_ci	case ATH10K_STATE_ON:
517562306a36Sopenharmony_ci	case ATH10K_STATE_RESTARTED:
517662306a36Sopenharmony_ci	case ATH10K_STATE_WEDGED:
517762306a36Sopenharmony_ci		WARN_ON(1);
517862306a36Sopenharmony_ci		ret = -EINVAL;
517962306a36Sopenharmony_ci		goto err;
518062306a36Sopenharmony_ci	case ATH10K_STATE_UTF:
518162306a36Sopenharmony_ci		ret = -EBUSY;
518262306a36Sopenharmony_ci		goto err;
518362306a36Sopenharmony_ci	}
518462306a36Sopenharmony_ci
518562306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
518662306a36Sopenharmony_ci
518762306a36Sopenharmony_ci	if (ar->hw_rfkill_on) {
518862306a36Sopenharmony_ci		ar->hw_rfkill_on = false;
518962306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
519062306a36Sopenharmony_ci		goto err;
519162306a36Sopenharmony_ci	}
519262306a36Sopenharmony_ci
519362306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
519462306a36Sopenharmony_ci
519562306a36Sopenharmony_ci	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL);
519662306a36Sopenharmony_ci	if (ret) {
519762306a36Sopenharmony_ci		ath10k_err(ar, "Could not init hif: %d\n", ret);
519862306a36Sopenharmony_ci		goto err_off;
519962306a36Sopenharmony_ci	}
520062306a36Sopenharmony_ci
520162306a36Sopenharmony_ci	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL,
520262306a36Sopenharmony_ci				&ar->normal_mode_fw);
520362306a36Sopenharmony_ci	if (ret) {
520462306a36Sopenharmony_ci		ath10k_err(ar, "Could not init core: %d\n", ret);
520562306a36Sopenharmony_ci		goto err_power_down;
520662306a36Sopenharmony_ci	}
520762306a36Sopenharmony_ci
520862306a36Sopenharmony_ci	if (ar->sys_cap_info & WMI_TLV_SYS_CAP_INFO_RFKILL) {
520962306a36Sopenharmony_ci		ret = ath10k_mac_rfkill_config(ar);
521062306a36Sopenharmony_ci		if (ret && ret != -EOPNOTSUPP) {
521162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to configure rfkill: %d", ret);
521262306a36Sopenharmony_ci			goto err_core_stop;
521362306a36Sopenharmony_ci		}
521462306a36Sopenharmony_ci	}
521562306a36Sopenharmony_ci
521662306a36Sopenharmony_ci	param = ar->wmi.pdev_param->pmf_qos;
521762306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, 1);
521862306a36Sopenharmony_ci	if (ret) {
521962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret);
522062306a36Sopenharmony_ci		goto err_core_stop;
522162306a36Sopenharmony_ci	}
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	param = ar->wmi.pdev_param->dynamic_bw;
522462306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, 1);
522562306a36Sopenharmony_ci	if (ret) {
522662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret);
522762306a36Sopenharmony_ci		goto err_core_stop;
522862306a36Sopenharmony_ci	}
522962306a36Sopenharmony_ci
523062306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
523162306a36Sopenharmony_ci		ret = ath10k_wmi_scan_prob_req_oui(ar, ar->mac_addr);
523262306a36Sopenharmony_ci		if (ret) {
523362306a36Sopenharmony_ci			ath10k_err(ar, "failed to set prob req oui: %i\n", ret);
523462306a36Sopenharmony_ci			goto err_core_stop;
523562306a36Sopenharmony_ci		}
523662306a36Sopenharmony_ci	}
523762306a36Sopenharmony_ci
523862306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
523962306a36Sopenharmony_ci		ret = ath10k_wmi_adaptive_qcs(ar, true);
524062306a36Sopenharmony_ci		if (ret) {
524162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to enable adaptive qcs: %d\n",
524262306a36Sopenharmony_ci				    ret);
524362306a36Sopenharmony_ci			goto err_core_stop;
524462306a36Sopenharmony_ci		}
524562306a36Sopenharmony_ci	}
524662306a36Sopenharmony_ci
524762306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_BURST, ar->wmi.svc_map)) {
524862306a36Sopenharmony_ci		param = ar->wmi.pdev_param->burst_enable;
524962306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_set_param(ar, param, 0);
525062306a36Sopenharmony_ci		if (ret) {
525162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to disable burst: %d\n", ret);
525262306a36Sopenharmony_ci			goto err_core_stop;
525362306a36Sopenharmony_ci		}
525462306a36Sopenharmony_ci	}
525562306a36Sopenharmony_ci
525662306a36Sopenharmony_ci	param = ar->wmi.pdev_param->idle_ps_config;
525762306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, 1);
525862306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP) {
525962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret);
526062306a36Sopenharmony_ci		goto err_core_stop;
526162306a36Sopenharmony_ci	}
526262306a36Sopenharmony_ci
526362306a36Sopenharmony_ci	__ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);
526462306a36Sopenharmony_ci
526562306a36Sopenharmony_ci	/*
526662306a36Sopenharmony_ci	 * By default FW set ARP frames ac to voice (6). In that case ARP
526762306a36Sopenharmony_ci	 * exchange is not working properly for UAPSD enabled AP. ARP requests
526862306a36Sopenharmony_ci	 * which arrives with access category 0 are processed by network stack
526962306a36Sopenharmony_ci	 * and send back with access category 0, but FW changes access category
527062306a36Sopenharmony_ci	 * to 6. Set ARP frames access category to best effort (0) solves
527162306a36Sopenharmony_ci	 * this problem.
527262306a36Sopenharmony_ci	 */
527362306a36Sopenharmony_ci
527462306a36Sopenharmony_ci	param = ar->wmi.pdev_param->arp_ac_override;
527562306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, 0);
527662306a36Sopenharmony_ci	if (ret) {
527762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set arp ac override parameter: %d\n",
527862306a36Sopenharmony_ci			    ret);
527962306a36Sopenharmony_ci		goto err_core_stop;
528062306a36Sopenharmony_ci	}
528162306a36Sopenharmony_ci
528262306a36Sopenharmony_ci	if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA,
528362306a36Sopenharmony_ci		     ar->running_fw->fw_file.fw_features)) {
528462306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_enable_adaptive_cca(ar, 1,
528562306a36Sopenharmony_ci							  WMI_CCA_DETECT_LEVEL_AUTO,
528662306a36Sopenharmony_ci							  WMI_CCA_DETECT_MARGIN_AUTO);
528762306a36Sopenharmony_ci		if (ret) {
528862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to enable adaptive cca: %d\n",
528962306a36Sopenharmony_ci				    ret);
529062306a36Sopenharmony_ci			goto err_core_stop;
529162306a36Sopenharmony_ci		}
529262306a36Sopenharmony_ci	}
529362306a36Sopenharmony_ci
529462306a36Sopenharmony_ci	param = ar->wmi.pdev_param->ani_enable;
529562306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_set_param(ar, param, 1);
529662306a36Sopenharmony_ci	if (ret) {
529762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable ani by default: %d\n",
529862306a36Sopenharmony_ci			    ret);
529962306a36Sopenharmony_ci		goto err_core_stop;
530062306a36Sopenharmony_ci	}
530162306a36Sopenharmony_ci
530262306a36Sopenharmony_ci	ar->ani_enabled = true;
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ci	if (ath10k_peer_stats_enabled(ar)) {
530562306a36Sopenharmony_ci		param = ar->wmi.pdev_param->peer_stats_update_period;
530662306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_set_param(ar, param,
530762306a36Sopenharmony_ci						PEER_DEFAULT_STATS_UPDATE_PERIOD);
530862306a36Sopenharmony_ci		if (ret) {
530962306a36Sopenharmony_ci			ath10k_warn(ar,
531062306a36Sopenharmony_ci				    "failed to set peer stats period : %d\n",
531162306a36Sopenharmony_ci				    ret);
531262306a36Sopenharmony_ci			goto err_core_stop;
531362306a36Sopenharmony_ci		}
531462306a36Sopenharmony_ci	}
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	param = ar->wmi.pdev_param->enable_btcoex;
531762306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map) &&
531862306a36Sopenharmony_ci	    test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,
531962306a36Sopenharmony_ci		     ar->running_fw->fw_file.fw_features) &&
532062306a36Sopenharmony_ci	    ar->coex_support) {
532162306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_set_param(ar, param, 0);
532262306a36Sopenharmony_ci		if (ret) {
532362306a36Sopenharmony_ci			ath10k_warn(ar,
532462306a36Sopenharmony_ci				    "failed to set btcoex param: %d\n", ret);
532562306a36Sopenharmony_ci			goto err_core_stop;
532662306a36Sopenharmony_ci		}
532762306a36Sopenharmony_ci		clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
532862306a36Sopenharmony_ci	}
532962306a36Sopenharmony_ci
533062306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_BB_TIMING_CONFIG_SUPPORT, ar->wmi.svc_map)) {
533162306a36Sopenharmony_ci		ret = __ath10k_fetch_bb_timing_dt(ar, &bb_timing);
533262306a36Sopenharmony_ci		if (!ret) {
533362306a36Sopenharmony_ci			ret = ath10k_wmi_pdev_bb_timing(ar, &bb_timing);
533462306a36Sopenharmony_ci			if (ret) {
533562306a36Sopenharmony_ci				ath10k_warn(ar,
533662306a36Sopenharmony_ci					    "failed to set bb timings: %d\n",
533762306a36Sopenharmony_ci					    ret);
533862306a36Sopenharmony_ci				goto err_core_stop;
533962306a36Sopenharmony_ci			}
534062306a36Sopenharmony_ci		}
534162306a36Sopenharmony_ci	}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_ci	ar->num_started_vdevs = 0;
534462306a36Sopenharmony_ci	ath10k_regd_update(ar);
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_ci	ath10k_spectral_start(ar);
534762306a36Sopenharmony_ci	ath10k_thermal_set_throttling(ar);
534862306a36Sopenharmony_ci
534962306a36Sopenharmony_ci	ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_IDLE;
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
535262306a36Sopenharmony_ci	return 0;
535362306a36Sopenharmony_ci
535462306a36Sopenharmony_cierr_core_stop:
535562306a36Sopenharmony_ci	ath10k_core_stop(ar);
535662306a36Sopenharmony_ci
535762306a36Sopenharmony_cierr_power_down:
535862306a36Sopenharmony_ci	ath10k_hif_power_down(ar);
535962306a36Sopenharmony_ci
536062306a36Sopenharmony_cierr_off:
536162306a36Sopenharmony_ci	ar->state = ATH10K_STATE_OFF;
536262306a36Sopenharmony_ci
536362306a36Sopenharmony_cierr:
536462306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
536562306a36Sopenharmony_ci	return ret;
536662306a36Sopenharmony_ci}
536762306a36Sopenharmony_ci
536862306a36Sopenharmony_cistatic void ath10k_stop(struct ieee80211_hw *hw)
536962306a36Sopenharmony_ci{
537062306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
537162306a36Sopenharmony_ci	u32 opt;
537262306a36Sopenharmony_ci
537362306a36Sopenharmony_ci	ath10k_drain_tx(ar);
537462306a36Sopenharmony_ci
537562306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
537662306a36Sopenharmony_ci	if (ar->state != ATH10K_STATE_OFF) {
537762306a36Sopenharmony_ci		if (!ar->hw_rfkill_on) {
537862306a36Sopenharmony_ci			/* If the current driver state is RESTARTING but not yet
537962306a36Sopenharmony_ci			 * fully RESTARTED because of incoming suspend event,
538062306a36Sopenharmony_ci			 * then ath10k_halt() is already called via
538162306a36Sopenharmony_ci			 * ath10k_core_restart() and should not be called here.
538262306a36Sopenharmony_ci			 */
538362306a36Sopenharmony_ci			if (ar->state != ATH10K_STATE_RESTARTING) {
538462306a36Sopenharmony_ci				ath10k_halt(ar);
538562306a36Sopenharmony_ci			} else {
538662306a36Sopenharmony_ci				/* Suspending here, because when in RESTARTING
538762306a36Sopenharmony_ci				 * state, ath10k_core_stop() skips
538862306a36Sopenharmony_ci				 * ath10k_wait_for_suspend().
538962306a36Sopenharmony_ci				 */
539062306a36Sopenharmony_ci				opt = WMI_PDEV_SUSPEND_AND_DISABLE_INTR;
539162306a36Sopenharmony_ci				ath10k_wait_for_suspend(ar, opt);
539262306a36Sopenharmony_ci			}
539362306a36Sopenharmony_ci		}
539462306a36Sopenharmony_ci		ar->state = ATH10K_STATE_OFF;
539562306a36Sopenharmony_ci	}
539662306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
539762306a36Sopenharmony_ci
539862306a36Sopenharmony_ci	cancel_work_sync(&ar->set_coverage_class_work);
539962306a36Sopenharmony_ci	cancel_delayed_work_sync(&ar->scan.timeout);
540062306a36Sopenharmony_ci	cancel_work_sync(&ar->restart_work);
540162306a36Sopenharmony_ci}
540262306a36Sopenharmony_ci
540362306a36Sopenharmony_cistatic int ath10k_config_ps(struct ath10k *ar)
540462306a36Sopenharmony_ci{
540562306a36Sopenharmony_ci	struct ath10k_vif *arvif;
540662306a36Sopenharmony_ci	int ret = 0;
540762306a36Sopenharmony_ci
540862306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
540962306a36Sopenharmony_ci
541062306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list) {
541162306a36Sopenharmony_ci		ret = ath10k_mac_vif_setup_ps(arvif);
541262306a36Sopenharmony_ci		if (ret) {
541362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to setup powersave: %d\n", ret);
541462306a36Sopenharmony_ci			break;
541562306a36Sopenharmony_ci		}
541662306a36Sopenharmony_ci	}
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	return ret;
541962306a36Sopenharmony_ci}
542062306a36Sopenharmony_ci
542162306a36Sopenharmony_cistatic int ath10k_config(struct ieee80211_hw *hw, u32 changed)
542262306a36Sopenharmony_ci{
542362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
542462306a36Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
542562306a36Sopenharmony_ci	int ret = 0;
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
542862306a36Sopenharmony_ci
542962306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_PS)
543062306a36Sopenharmony_ci		ath10k_config_ps(ar);
543162306a36Sopenharmony_ci
543262306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
543362306a36Sopenharmony_ci		ar->monitor = conf->flags & IEEE80211_CONF_MONITOR;
543462306a36Sopenharmony_ci		ret = ath10k_monitor_recalc(ar);
543562306a36Sopenharmony_ci		if (ret)
543662306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
543762306a36Sopenharmony_ci	}
543862306a36Sopenharmony_ci
543962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
544062306a36Sopenharmony_ci	return ret;
544162306a36Sopenharmony_ci}
544262306a36Sopenharmony_ci
544362306a36Sopenharmony_cistatic u32 get_nss_from_chainmask(u16 chain_mask)
544462306a36Sopenharmony_ci{
544562306a36Sopenharmony_ci	if ((chain_mask & 0xf) == 0xf)
544662306a36Sopenharmony_ci		return 4;
544762306a36Sopenharmony_ci	else if ((chain_mask & 0x7) == 0x7)
544862306a36Sopenharmony_ci		return 3;
544962306a36Sopenharmony_ci	else if ((chain_mask & 0x3) == 0x3)
545062306a36Sopenharmony_ci		return 2;
545162306a36Sopenharmony_ci	return 1;
545262306a36Sopenharmony_ci}
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_cistatic int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif)
545562306a36Sopenharmony_ci{
545662306a36Sopenharmony_ci	u32 value = 0;
545762306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
545862306a36Sopenharmony_ci	int nsts;
545962306a36Sopenharmony_ci	int sound_dim;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci	if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC)
546262306a36Sopenharmony_ci		return 0;
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ci	nsts = ath10k_mac_get_vht_cap_bf_sts(ar);
546562306a36Sopenharmony_ci	if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
546662306a36Sopenharmony_ci				IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE))
546762306a36Sopenharmony_ci		value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET);
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_ci	sound_dim = ath10k_mac_get_vht_cap_bf_sound_dim(ar);
547062306a36Sopenharmony_ci	if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
547162306a36Sopenharmony_ci				IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))
547262306a36Sopenharmony_ci		value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET);
547362306a36Sopenharmony_ci
547462306a36Sopenharmony_ci	if (!value)
547562306a36Sopenharmony_ci		return 0;
547662306a36Sopenharmony_ci
547762306a36Sopenharmony_ci	if (ar->vht_cap_info & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
547862306a36Sopenharmony_ci		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
547962306a36Sopenharmony_ci
548062306a36Sopenharmony_ci	if (ar->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
548162306a36Sopenharmony_ci		value |= (WMI_VDEV_PARAM_TXBF_MU_TX_BFER |
548262306a36Sopenharmony_ci			  WMI_VDEV_PARAM_TXBF_SU_TX_BFER);
548362306a36Sopenharmony_ci
548462306a36Sopenharmony_ci	if (ar->vht_cap_info & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
548562306a36Sopenharmony_ci		value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
548662306a36Sopenharmony_ci
548762306a36Sopenharmony_ci	if (ar->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
548862306a36Sopenharmony_ci		value |= (WMI_VDEV_PARAM_TXBF_MU_TX_BFEE |
548962306a36Sopenharmony_ci			  WMI_VDEV_PARAM_TXBF_SU_TX_BFEE);
549062306a36Sopenharmony_ci
549162306a36Sopenharmony_ci	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
549262306a36Sopenharmony_ci					 ar->wmi.vdev_param->txbf, value);
549362306a36Sopenharmony_ci}
549462306a36Sopenharmony_ci
549562306a36Sopenharmony_cistatic void ath10k_update_vif_offload(struct ieee80211_hw *hw,
549662306a36Sopenharmony_ci				      struct ieee80211_vif *vif)
549762306a36Sopenharmony_ci{
549862306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
549962306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
550062306a36Sopenharmony_ci	u32 vdev_param;
550162306a36Sopenharmony_ci	int ret;
550262306a36Sopenharmony_ci
550362306a36Sopenharmony_ci	if (ath10k_frame_mode != ATH10K_HW_TXRX_ETHERNET ||
550462306a36Sopenharmony_ci	    ar->wmi.vdev_param->tx_encap_type == WMI_VDEV_PARAM_UNSUPPORTED ||
550562306a36Sopenharmony_ci	     (vif->type != NL80211_IFTYPE_STATION &&
550662306a36Sopenharmony_ci	      vif->type != NL80211_IFTYPE_AP))
550762306a36Sopenharmony_ci		vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
550862306a36Sopenharmony_ci
550962306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->tx_encap_type;
551062306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
551162306a36Sopenharmony_ci					ATH10K_HW_TXRX_NATIVE_WIFI);
551262306a36Sopenharmony_ci	/* 10.X firmware does not support this VDEV parameter. Do not warn */
551362306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP) {
551462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
551562306a36Sopenharmony_ci			    arvif->vdev_id, ret);
551662306a36Sopenharmony_ci	}
551762306a36Sopenharmony_ci}
551862306a36Sopenharmony_ci
551962306a36Sopenharmony_ci/*
552062306a36Sopenharmony_ci * TODO:
552162306a36Sopenharmony_ci * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
552262306a36Sopenharmony_ci * because we will send mgmt frames without CCK. This requirement
552362306a36Sopenharmony_ci * for P2P_FIND/GO_NEG should be handled by checking CCK flag
552462306a36Sopenharmony_ci * in the TX packet.
552562306a36Sopenharmony_ci */
552662306a36Sopenharmony_cistatic int ath10k_add_interface(struct ieee80211_hw *hw,
552762306a36Sopenharmony_ci				struct ieee80211_vif *vif)
552862306a36Sopenharmony_ci{
552962306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
553062306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
553162306a36Sopenharmony_ci	struct ath10k_peer *peer;
553262306a36Sopenharmony_ci	enum wmi_sta_powersave_param param;
553362306a36Sopenharmony_ci	int ret = 0;
553462306a36Sopenharmony_ci	u32 value;
553562306a36Sopenharmony_ci	int bit;
553662306a36Sopenharmony_ci	int i;
553762306a36Sopenharmony_ci	u32 vdev_param;
553862306a36Sopenharmony_ci
553962306a36Sopenharmony_ci	vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
554062306a36Sopenharmony_ci
554162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci	memset(arvif, 0, sizeof(*arvif));
554462306a36Sopenharmony_ci	ath10k_mac_txq_init(vif->txq);
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_ci	arvif->ar = ar;
554762306a36Sopenharmony_ci	arvif->vif = vif;
554862306a36Sopenharmony_ci
554962306a36Sopenharmony_ci	INIT_LIST_HEAD(&arvif->list);
555062306a36Sopenharmony_ci	INIT_WORK(&arvif->ap_csa_work, ath10k_mac_vif_ap_csa_work);
555162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&arvif->connection_loss_work,
555262306a36Sopenharmony_ci			  ath10k_mac_vif_sta_connection_loss_work);
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
555562306a36Sopenharmony_ci		arvif->bitrate_mask.control[i].legacy = 0xffffffff;
555662306a36Sopenharmony_ci		memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff,
555762306a36Sopenharmony_ci		       sizeof(arvif->bitrate_mask.control[i].ht_mcs));
555862306a36Sopenharmony_ci		memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff,
555962306a36Sopenharmony_ci		       sizeof(arvif->bitrate_mask.control[i].vht_mcs));
556062306a36Sopenharmony_ci	}
556162306a36Sopenharmony_ci
556262306a36Sopenharmony_ci	if (ar->num_peers >= ar->max_num_peers) {
556362306a36Sopenharmony_ci		ath10k_warn(ar, "refusing vdev creation due to insufficient peer entry resources in firmware\n");
556462306a36Sopenharmony_ci		ret = -ENOBUFS;
556562306a36Sopenharmony_ci		goto err;
556662306a36Sopenharmony_ci	}
556762306a36Sopenharmony_ci
556862306a36Sopenharmony_ci	if (ar->free_vdev_map == 0) {
556962306a36Sopenharmony_ci		ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n");
557062306a36Sopenharmony_ci		ret = -EBUSY;
557162306a36Sopenharmony_ci		goto err;
557262306a36Sopenharmony_ci	}
557362306a36Sopenharmony_ci	bit = __ffs64(ar->free_vdev_map);
557462306a36Sopenharmony_ci
557562306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac create vdev %i map %llx\n",
557662306a36Sopenharmony_ci		   bit, ar->free_vdev_map);
557762306a36Sopenharmony_ci
557862306a36Sopenharmony_ci	arvif->vdev_id = bit;
557962306a36Sopenharmony_ci	arvif->vdev_subtype =
558062306a36Sopenharmony_ci		ath10k_wmi_get_vdev_subtype(ar, WMI_VDEV_SUBTYPE_NONE);
558162306a36Sopenharmony_ci
558262306a36Sopenharmony_ci	switch (vif->type) {
558362306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
558462306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_STA;
558562306a36Sopenharmony_ci		arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
558662306a36Sopenharmony_ci					(ar, WMI_VDEV_SUBTYPE_P2P_DEVICE);
558762306a36Sopenharmony_ci		break;
558862306a36Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
558962306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
559062306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_STA;
559162306a36Sopenharmony_ci		if (vif->p2p)
559262306a36Sopenharmony_ci			arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
559362306a36Sopenharmony_ci					(ar, WMI_VDEV_SUBTYPE_P2P_CLIENT);
559462306a36Sopenharmony_ci		break;
559562306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
559662306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
559762306a36Sopenharmony_ci		break;
559862306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
559962306a36Sopenharmony_ci		if (test_bit(WMI_SERVICE_MESH_11S, ar->wmi.svc_map)) {
560062306a36Sopenharmony_ci			arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
560162306a36Sopenharmony_ci						(ar, WMI_VDEV_SUBTYPE_MESH_11S);
560262306a36Sopenharmony_ci		} else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
560362306a36Sopenharmony_ci			ret = -EINVAL;
560462306a36Sopenharmony_ci			ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n");
560562306a36Sopenharmony_ci			goto err;
560662306a36Sopenharmony_ci		}
560762306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_AP;
560862306a36Sopenharmony_ci		break;
560962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
561062306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_AP;
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_ci		if (vif->p2p)
561362306a36Sopenharmony_ci			arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
561462306a36Sopenharmony_ci						(ar, WMI_VDEV_SUBTYPE_P2P_GO);
561562306a36Sopenharmony_ci		break;
561662306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
561762306a36Sopenharmony_ci		arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
561862306a36Sopenharmony_ci		break;
561962306a36Sopenharmony_ci	default:
562062306a36Sopenharmony_ci		WARN_ON(1);
562162306a36Sopenharmony_ci		break;
562262306a36Sopenharmony_ci	}
562362306a36Sopenharmony_ci
562462306a36Sopenharmony_ci	/* Using vdev_id as queue number will make it very easy to do per-vif
562562306a36Sopenharmony_ci	 * tx queue locking. This shouldn't wrap due to interface combinations
562662306a36Sopenharmony_ci	 * but do a modulo for correctness sake and prevent using offchannel tx
562762306a36Sopenharmony_ci	 * queues for regular vif tx.
562862306a36Sopenharmony_ci	 */
562962306a36Sopenharmony_ci	vif->cab_queue = arvif->vdev_id % (IEEE80211_MAX_QUEUES - 1);
563062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++)
563162306a36Sopenharmony_ci		vif->hw_queue[i] = arvif->vdev_id % (IEEE80211_MAX_QUEUES - 1);
563262306a36Sopenharmony_ci
563362306a36Sopenharmony_ci	/* Some firmware revisions don't wait for beacon tx completion before
563462306a36Sopenharmony_ci	 * sending another SWBA event. This could lead to hardware using old
563562306a36Sopenharmony_ci	 * (freed) beacon data in some cases, e.g. tx credit starvation
563662306a36Sopenharmony_ci	 * combined with missed TBTT. This is very rare.
563762306a36Sopenharmony_ci	 *
563862306a36Sopenharmony_ci	 * On non-IOMMU-enabled hosts this could be a possible security issue
563962306a36Sopenharmony_ci	 * because hw could beacon some random data on the air.  On
564062306a36Sopenharmony_ci	 * IOMMU-enabled hosts DMAR faults would occur in most cases and target
564162306a36Sopenharmony_ci	 * device would crash.
564262306a36Sopenharmony_ci	 *
564362306a36Sopenharmony_ci	 * Since there are no beacon tx completions (implicit nor explicit)
564462306a36Sopenharmony_ci	 * propagated to host the only workaround for this is to allocate a
564562306a36Sopenharmony_ci	 * DMA-coherent buffer for a lifetime of a vif and use it for all
564662306a36Sopenharmony_ci	 * beacon tx commands. Worst case for this approach is some beacons may
564762306a36Sopenharmony_ci	 * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
564862306a36Sopenharmony_ci	 */
564962306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_ADHOC ||
565062306a36Sopenharmony_ci	    vif->type == NL80211_IFTYPE_MESH_POINT ||
565162306a36Sopenharmony_ci	    vif->type == NL80211_IFTYPE_AP) {
565262306a36Sopenharmony_ci		if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
565362306a36Sopenharmony_ci			arvif->beacon_buf = kmalloc(IEEE80211_MAX_FRAME_LEN,
565462306a36Sopenharmony_ci						    GFP_KERNEL);
565562306a36Sopenharmony_ci
565662306a36Sopenharmony_ci			/* Using a kernel pointer in place of a dma_addr_t
565762306a36Sopenharmony_ci			 * token can lead to undefined behavior if that
565862306a36Sopenharmony_ci			 * makes it into cache management functions. Use a
565962306a36Sopenharmony_ci			 * known-invalid address token instead, which
566062306a36Sopenharmony_ci			 * avoids the warning and makes it easier to catch
566162306a36Sopenharmony_ci			 * bugs if it does end up getting used.
566262306a36Sopenharmony_ci			 */
566362306a36Sopenharmony_ci			arvif->beacon_paddr = DMA_MAPPING_ERROR;
566462306a36Sopenharmony_ci		} else {
566562306a36Sopenharmony_ci			arvif->beacon_buf =
566662306a36Sopenharmony_ci				dma_alloc_coherent(ar->dev,
566762306a36Sopenharmony_ci						   IEEE80211_MAX_FRAME_LEN,
566862306a36Sopenharmony_ci						   &arvif->beacon_paddr,
566962306a36Sopenharmony_ci						   GFP_ATOMIC);
567062306a36Sopenharmony_ci		}
567162306a36Sopenharmony_ci		if (!arvif->beacon_buf) {
567262306a36Sopenharmony_ci			ret = -ENOMEM;
567362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to allocate beacon buffer: %d\n",
567462306a36Sopenharmony_ci				    ret);
567562306a36Sopenharmony_ci			goto err;
567662306a36Sopenharmony_ci		}
567762306a36Sopenharmony_ci	}
567862306a36Sopenharmony_ci	if (test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags))
567962306a36Sopenharmony_ci		arvif->nohwcrypt = true;
568062306a36Sopenharmony_ci
568162306a36Sopenharmony_ci	if (arvif->nohwcrypt &&
568262306a36Sopenharmony_ci	    !test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
568362306a36Sopenharmony_ci		ret = -EINVAL;
568462306a36Sopenharmony_ci		ath10k_warn(ar, "cryptmode module param needed for sw crypto\n");
568562306a36Sopenharmony_ci		goto err;
568662306a36Sopenharmony_ci	}
568762306a36Sopenharmony_ci
568862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d bcnmode %s\n",
568962306a36Sopenharmony_ci		   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
569062306a36Sopenharmony_ci		   arvif->beacon_buf ? "single-buf" : "per-skb");
569162306a36Sopenharmony_ci
569262306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
569362306a36Sopenharmony_ci				     arvif->vdev_subtype, vif->addr);
569462306a36Sopenharmony_ci	if (ret) {
569562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to create WMI vdev %i: %d\n",
569662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
569762306a36Sopenharmony_ci		goto err;
569862306a36Sopenharmony_ci	}
569962306a36Sopenharmony_ci
570062306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_VDEV_DISABLE_4_ADDR_SRC_LRN_SUPPORT,
570162306a36Sopenharmony_ci		     ar->wmi.svc_map)) {
570262306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->disable_4addr_src_lrn;
570362306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
570462306a36Sopenharmony_ci						WMI_VDEV_DISABLE_4_ADDR_SRC_LRN);
570562306a36Sopenharmony_ci		if (ret && ret != -EOPNOTSUPP) {
570662306a36Sopenharmony_ci			ath10k_warn(ar, "failed to disable 4addr src lrn vdev %i: %d\n",
570762306a36Sopenharmony_ci				    arvif->vdev_id, ret);
570862306a36Sopenharmony_ci		}
570962306a36Sopenharmony_ci	}
571062306a36Sopenharmony_ci
571162306a36Sopenharmony_ci	ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
571262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
571362306a36Sopenharmony_ci	list_add(&arvif->list, &ar->arvifs);
571462306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
571562306a36Sopenharmony_ci
571662306a36Sopenharmony_ci	/* It makes no sense to have firmware do keepalives. mac80211 already
571762306a36Sopenharmony_ci	 * takes care of this with idle connection polling.
571862306a36Sopenharmony_ci	 */
571962306a36Sopenharmony_ci	ret = ath10k_mac_vif_disable_keepalive(arvif);
572062306a36Sopenharmony_ci	if (ret) {
572162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
572262306a36Sopenharmony_ci			    arvif->vdev_id, ret);
572362306a36Sopenharmony_ci		goto err_vdev_delete;
572462306a36Sopenharmony_ci	}
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	arvif->def_wep_key_idx = -1;
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_ci	ath10k_update_vif_offload(hw, vif);
572962306a36Sopenharmony_ci
573062306a36Sopenharmony_ci	/* Configuring number of spatial stream for monitor interface is causing
573162306a36Sopenharmony_ci	 * target assert in qca9888 and qca6174.
573262306a36Sopenharmony_ci	 */
573362306a36Sopenharmony_ci	if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) {
573462306a36Sopenharmony_ci		u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
573562306a36Sopenharmony_ci
573662306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->nss;
573762306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
573862306a36Sopenharmony_ci						nss);
573962306a36Sopenharmony_ci		if (ret) {
574062306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
574162306a36Sopenharmony_ci				    arvif->vdev_id, ar->cfg_tx_chainmask, nss,
574262306a36Sopenharmony_ci				    ret);
574362306a36Sopenharmony_ci			goto err_vdev_delete;
574462306a36Sopenharmony_ci		}
574562306a36Sopenharmony_ci	}
574662306a36Sopenharmony_ci
574762306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
574862306a36Sopenharmony_ci	    arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
574962306a36Sopenharmony_ci		ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
575062306a36Sopenharmony_ci					 vif->addr, WMI_PEER_TYPE_DEFAULT);
575162306a36Sopenharmony_ci		if (ret) {
575262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
575362306a36Sopenharmony_ci				    arvif->vdev_id, ret);
575462306a36Sopenharmony_ci			goto err_vdev_delete;
575562306a36Sopenharmony_ci		}
575662306a36Sopenharmony_ci
575762306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
575862306a36Sopenharmony_ci
575962306a36Sopenharmony_ci		peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr);
576062306a36Sopenharmony_ci		if (!peer) {
576162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
576262306a36Sopenharmony_ci				    vif->addr, arvif->vdev_id);
576362306a36Sopenharmony_ci			spin_unlock_bh(&ar->data_lock);
576462306a36Sopenharmony_ci			ret = -ENOENT;
576562306a36Sopenharmony_ci			goto err_peer_delete;
576662306a36Sopenharmony_ci		}
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_ci		arvif->peer_id = find_first_bit(peer->peer_ids,
576962306a36Sopenharmony_ci						ATH10K_MAX_NUM_PEER_IDS);
577062306a36Sopenharmony_ci
577162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
577262306a36Sopenharmony_ci	} else {
577362306a36Sopenharmony_ci		arvif->peer_id = HTT_INVALID_PEERID;
577462306a36Sopenharmony_ci	}
577562306a36Sopenharmony_ci
577662306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
577762306a36Sopenharmony_ci		ret = ath10k_mac_set_kickout(arvif);
577862306a36Sopenharmony_ci		if (ret) {
577962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set vdev %i kickout parameters: %d\n",
578062306a36Sopenharmony_ci				    arvif->vdev_id, ret);
578162306a36Sopenharmony_ci			goto err_peer_delete;
578262306a36Sopenharmony_ci		}
578362306a36Sopenharmony_ci	}
578462306a36Sopenharmony_ci
578562306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
578662306a36Sopenharmony_ci		param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
578762306a36Sopenharmony_ci		value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
578862306a36Sopenharmony_ci		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
578962306a36Sopenharmony_ci						  param, value);
579062306a36Sopenharmony_ci		if (ret) {
579162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set vdev %i RX wake policy: %d\n",
579262306a36Sopenharmony_ci				    arvif->vdev_id, ret);
579362306a36Sopenharmony_ci			goto err_peer_delete;
579462306a36Sopenharmony_ci		}
579562306a36Sopenharmony_ci
579662306a36Sopenharmony_ci		ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
579762306a36Sopenharmony_ci		if (ret) {
579862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
579962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
580062306a36Sopenharmony_ci			goto err_peer_delete;
580162306a36Sopenharmony_ci		}
580262306a36Sopenharmony_ci
580362306a36Sopenharmony_ci		ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
580462306a36Sopenharmony_ci		if (ret) {
580562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
580662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
580762306a36Sopenharmony_ci			goto err_peer_delete;
580862306a36Sopenharmony_ci		}
580962306a36Sopenharmony_ci	}
581062306a36Sopenharmony_ci
581162306a36Sopenharmony_ci	ret = ath10k_mac_set_txbf_conf(arvif);
581262306a36Sopenharmony_ci	if (ret) {
581362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set txbf for vdev %d: %d\n",
581462306a36Sopenharmony_ci			    arvif->vdev_id, ret);
581562306a36Sopenharmony_ci		goto err_peer_delete;
581662306a36Sopenharmony_ci	}
581762306a36Sopenharmony_ci
581862306a36Sopenharmony_ci	ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
581962306a36Sopenharmony_ci	if (ret) {
582062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
582162306a36Sopenharmony_ci			    arvif->vdev_id, ret);
582262306a36Sopenharmony_ci		goto err_peer_delete;
582362306a36Sopenharmony_ci	}
582462306a36Sopenharmony_ci
582562306a36Sopenharmony_ci	arvif->txpower = vif->bss_conf.txpower;
582662306a36Sopenharmony_ci	ret = ath10k_mac_txpower_recalc(ar);
582762306a36Sopenharmony_ci	if (ret) {
582862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
582962306a36Sopenharmony_ci		goto err_peer_delete;
583062306a36Sopenharmony_ci	}
583162306a36Sopenharmony_ci
583262306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_RTT_RESPONDER_ROLE, ar->wmi.svc_map)) {
583362306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->rtt_responder_role;
583462306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
583562306a36Sopenharmony_ci						arvif->ftm_responder);
583662306a36Sopenharmony_ci
583762306a36Sopenharmony_ci		/* It is harmless to not set FTM role. Do not warn */
583862306a36Sopenharmony_ci		if (ret && ret != -EOPNOTSUPP)
583962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set vdev %i FTM Responder: %d\n",
584062306a36Sopenharmony_ci				    arvif->vdev_id, ret);
584162306a36Sopenharmony_ci	}
584262306a36Sopenharmony_ci
584362306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR) {
584462306a36Sopenharmony_ci		ar->monitor_arvif = arvif;
584562306a36Sopenharmony_ci		ret = ath10k_monitor_recalc(ar);
584662306a36Sopenharmony_ci		if (ret) {
584762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
584862306a36Sopenharmony_ci			goto err_peer_delete;
584962306a36Sopenharmony_ci		}
585062306a36Sopenharmony_ci	}
585162306a36Sopenharmony_ci
585262306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
585362306a36Sopenharmony_ci	if (!ar->tx_paused)
585462306a36Sopenharmony_ci		ieee80211_wake_queue(ar->hw, arvif->vdev_id);
585562306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
585662306a36Sopenharmony_ci
585762306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
585862306a36Sopenharmony_ci	return 0;
585962306a36Sopenharmony_ci
586062306a36Sopenharmony_cierr_peer_delete:
586162306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
586262306a36Sopenharmony_ci	    arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
586362306a36Sopenharmony_ci		ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr);
586462306a36Sopenharmony_ci		ath10k_wait_for_peer_delete_done(ar, arvif->vdev_id,
586562306a36Sopenharmony_ci						 vif->addr);
586662306a36Sopenharmony_ci	}
586762306a36Sopenharmony_ci
586862306a36Sopenharmony_cierr_vdev_delete:
586962306a36Sopenharmony_ci	ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
587062306a36Sopenharmony_ci	ar->free_vdev_map |= 1LL << arvif->vdev_id;
587162306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
587262306a36Sopenharmony_ci	list_del(&arvif->list);
587362306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
587462306a36Sopenharmony_ci
587562306a36Sopenharmony_cierr:
587662306a36Sopenharmony_ci	if (arvif->beacon_buf) {
587762306a36Sopenharmony_ci		if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
587862306a36Sopenharmony_ci			kfree(arvif->beacon_buf);
587962306a36Sopenharmony_ci		else
588062306a36Sopenharmony_ci			dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
588162306a36Sopenharmony_ci					  arvif->beacon_buf,
588262306a36Sopenharmony_ci					  arvif->beacon_paddr);
588362306a36Sopenharmony_ci		arvif->beacon_buf = NULL;
588462306a36Sopenharmony_ci	}
588562306a36Sopenharmony_ci
588662306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
588762306a36Sopenharmony_ci
588862306a36Sopenharmony_ci	return ret;
588962306a36Sopenharmony_ci}
589062306a36Sopenharmony_ci
589162306a36Sopenharmony_cistatic void ath10k_mac_vif_tx_unlock_all(struct ath10k_vif *arvif)
589262306a36Sopenharmony_ci{
589362306a36Sopenharmony_ci	int i;
589462306a36Sopenharmony_ci
589562306a36Sopenharmony_ci	for (i = 0; i < BITS_PER_LONG; i++)
589662306a36Sopenharmony_ci		ath10k_mac_vif_tx_unlock(arvif, i);
589762306a36Sopenharmony_ci}
589862306a36Sopenharmony_ci
589962306a36Sopenharmony_cistatic void ath10k_remove_interface(struct ieee80211_hw *hw,
590062306a36Sopenharmony_ci				    struct ieee80211_vif *vif)
590162306a36Sopenharmony_ci{
590262306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
590362306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
590462306a36Sopenharmony_ci	struct ath10k_peer *peer;
590562306a36Sopenharmony_ci	unsigned long time_left;
590662306a36Sopenharmony_ci	int ret;
590762306a36Sopenharmony_ci	int i;
590862306a36Sopenharmony_ci
590962306a36Sopenharmony_ci	cancel_work_sync(&arvif->ap_csa_work);
591062306a36Sopenharmony_ci	cancel_delayed_work_sync(&arvif->connection_loss_work);
591162306a36Sopenharmony_ci
591262306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
591362306a36Sopenharmony_ci
591462306a36Sopenharmony_ci	ret = ath10k_spectral_vif_stop(arvif);
591562306a36Sopenharmony_ci	if (ret)
591662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
591762306a36Sopenharmony_ci			    arvif->vdev_id, ret);
591862306a36Sopenharmony_ci
591962306a36Sopenharmony_ci	ar->free_vdev_map |= 1LL << arvif->vdev_id;
592062306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
592162306a36Sopenharmony_ci	list_del(&arvif->list);
592262306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
592362306a36Sopenharmony_ci
592462306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
592562306a36Sopenharmony_ci	    arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
592662306a36Sopenharmony_ci		ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id,
592762306a36Sopenharmony_ci					     vif->addr);
592862306a36Sopenharmony_ci		if (ret)
592962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to submit AP/IBSS self-peer removal on vdev %i: %d\n",
593062306a36Sopenharmony_ci				    arvif->vdev_id, ret);
593162306a36Sopenharmony_ci
593262306a36Sopenharmony_ci		ath10k_wait_for_peer_delete_done(ar, arvif->vdev_id,
593362306a36Sopenharmony_ci						 vif->addr);
593462306a36Sopenharmony_ci		kfree(arvif->u.ap.noa_data);
593562306a36Sopenharmony_ci	}
593662306a36Sopenharmony_ci
593762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n",
593862306a36Sopenharmony_ci		   arvif->vdev_id);
593962306a36Sopenharmony_ci
594062306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
594162306a36Sopenharmony_ci	if (ret)
594262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
594362306a36Sopenharmony_ci			    arvif->vdev_id, ret);
594462306a36Sopenharmony_ci
594562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
594662306a36Sopenharmony_ci		time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
594762306a36Sopenharmony_ci							ATH10K_VDEV_DELETE_TIMEOUT_HZ);
594862306a36Sopenharmony_ci		if (time_left == 0) {
594962306a36Sopenharmony_ci			ath10k_warn(ar, "Timeout in receiving vdev delete response\n");
595062306a36Sopenharmony_ci			goto out;
595162306a36Sopenharmony_ci		}
595262306a36Sopenharmony_ci	}
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci	/* Some firmware revisions don't notify host about self-peer removal
595562306a36Sopenharmony_ci	 * until after associated vdev is deleted.
595662306a36Sopenharmony_ci	 */
595762306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
595862306a36Sopenharmony_ci	    arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
595962306a36Sopenharmony_ci		ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id,
596062306a36Sopenharmony_ci						   vif->addr);
596162306a36Sopenharmony_ci		if (ret)
596262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n",
596362306a36Sopenharmony_ci				    arvif->vdev_id, ret);
596462306a36Sopenharmony_ci
596562306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
596662306a36Sopenharmony_ci		ar->num_peers--;
596762306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
596862306a36Sopenharmony_ci	}
596962306a36Sopenharmony_ci
597062306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
597162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
597262306a36Sopenharmony_ci		peer = ar->peer_map[i];
597362306a36Sopenharmony_ci		if (!peer)
597462306a36Sopenharmony_ci			continue;
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci		if (peer->vif == vif) {
597762306a36Sopenharmony_ci			ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
597862306a36Sopenharmony_ci				    vif->addr, arvif->vdev_id);
597962306a36Sopenharmony_ci			peer->vif = NULL;
598062306a36Sopenharmony_ci		}
598162306a36Sopenharmony_ci	}
598262306a36Sopenharmony_ci
598362306a36Sopenharmony_ci	/* Clean this up late, less opportunity for firmware to access
598462306a36Sopenharmony_ci	 * DMA memory we have deleted.
598562306a36Sopenharmony_ci	 */
598662306a36Sopenharmony_ci	ath10k_mac_vif_beacon_cleanup(arvif);
598762306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
598862306a36Sopenharmony_ci
598962306a36Sopenharmony_ci	ath10k_peer_cleanup(ar, arvif->vdev_id);
599062306a36Sopenharmony_ci	ath10k_mac_txq_unref(ar, vif->txq);
599162306a36Sopenharmony_ci
599262306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR) {
599362306a36Sopenharmony_ci		ar->monitor_arvif = NULL;
599462306a36Sopenharmony_ci		ret = ath10k_monitor_recalc(ar);
599562306a36Sopenharmony_ci		if (ret)
599662306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
599762306a36Sopenharmony_ci	}
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_ci	ret = ath10k_mac_txpower_recalc(ar);
600062306a36Sopenharmony_ci	if (ret)
600162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
600262306a36Sopenharmony_ci
600362306a36Sopenharmony_ci	spin_lock_bh(&ar->htt.tx_lock);
600462306a36Sopenharmony_ci	ath10k_mac_vif_tx_unlock_all(arvif);
600562306a36Sopenharmony_ci	spin_unlock_bh(&ar->htt.tx_lock);
600662306a36Sopenharmony_ci
600762306a36Sopenharmony_ci	ath10k_mac_txq_unref(ar, vif->txq);
600862306a36Sopenharmony_ci
600962306a36Sopenharmony_ciout:
601062306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
601162306a36Sopenharmony_ci}
601262306a36Sopenharmony_ci
601362306a36Sopenharmony_ci/*
601462306a36Sopenharmony_ci * FIXME: Has to be verified.
601562306a36Sopenharmony_ci */
601662306a36Sopenharmony_ci#define SUPPORTED_FILTERS			\
601762306a36Sopenharmony_ci	(FIF_ALLMULTI |				\
601862306a36Sopenharmony_ci	FIF_CONTROL |				\
601962306a36Sopenharmony_ci	FIF_PSPOLL |				\
602062306a36Sopenharmony_ci	FIF_OTHER_BSS |				\
602162306a36Sopenharmony_ci	FIF_BCN_PRBRESP_PROMISC |		\
602262306a36Sopenharmony_ci	FIF_PROBE_REQ |				\
602362306a36Sopenharmony_ci	FIF_FCSFAIL)
602462306a36Sopenharmony_ci
602562306a36Sopenharmony_cistatic void ath10k_configure_filter(struct ieee80211_hw *hw,
602662306a36Sopenharmony_ci				    unsigned int changed_flags,
602762306a36Sopenharmony_ci				    unsigned int *total_flags,
602862306a36Sopenharmony_ci				    u64 multicast)
602962306a36Sopenharmony_ci{
603062306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
603162306a36Sopenharmony_ci	int ret;
603262306a36Sopenharmony_ci
603362306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
603462306a36Sopenharmony_ci
603562306a36Sopenharmony_ci	*total_flags &= SUPPORTED_FILTERS;
603662306a36Sopenharmony_ci	ar->filter_flags = *total_flags;
603762306a36Sopenharmony_ci
603862306a36Sopenharmony_ci	ret = ath10k_monitor_recalc(ar);
603962306a36Sopenharmony_ci	if (ret)
604062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
604162306a36Sopenharmony_ci
604262306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
604362306a36Sopenharmony_ci}
604462306a36Sopenharmony_ci
604562306a36Sopenharmony_cistatic void ath10k_recalculate_mgmt_rate(struct ath10k *ar,
604662306a36Sopenharmony_ci					 struct ieee80211_vif *vif,
604762306a36Sopenharmony_ci					 struct cfg80211_chan_def *def)
604862306a36Sopenharmony_ci{
604962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
605062306a36Sopenharmony_ci	const struct ieee80211_supported_band *sband;
605162306a36Sopenharmony_ci	u8 basic_rate_idx;
605262306a36Sopenharmony_ci	int hw_rate_code;
605362306a36Sopenharmony_ci	u32 vdev_param;
605462306a36Sopenharmony_ci	u16 bitrate;
605562306a36Sopenharmony_ci	int ret;
605662306a36Sopenharmony_ci
605762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
605862306a36Sopenharmony_ci
605962306a36Sopenharmony_ci	sband = ar->hw->wiphy->bands[def->chan->band];
606062306a36Sopenharmony_ci	basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1;
606162306a36Sopenharmony_ci	bitrate = sband->bitrates[basic_rate_idx].bitrate;
606262306a36Sopenharmony_ci
606362306a36Sopenharmony_ci	hw_rate_code = ath10k_mac_get_rate_hw_value(bitrate);
606462306a36Sopenharmony_ci	if (hw_rate_code < 0) {
606562306a36Sopenharmony_ci		ath10k_warn(ar, "bitrate not supported %d\n", bitrate);
606662306a36Sopenharmony_ci		return;
606762306a36Sopenharmony_ci	}
606862306a36Sopenharmony_ci
606962306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->mgmt_rate;
607062306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
607162306a36Sopenharmony_ci					hw_rate_code);
607262306a36Sopenharmony_ci	if (ret)
607362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set mgmt tx rate %d\n", ret);
607462306a36Sopenharmony_ci}
607562306a36Sopenharmony_ci
607662306a36Sopenharmony_cistatic void ath10k_bss_info_changed(struct ieee80211_hw *hw,
607762306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
607862306a36Sopenharmony_ci				    struct ieee80211_bss_conf *info,
607962306a36Sopenharmony_ci				    u64 changed)
608062306a36Sopenharmony_ci{
608162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
608262306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
608362306a36Sopenharmony_ci	struct cfg80211_chan_def def;
608462306a36Sopenharmony_ci	u32 vdev_param, pdev_param, slottime, preamble;
608562306a36Sopenharmony_ci	u16 bitrate, hw_value;
608662306a36Sopenharmony_ci	u8 rate, rateidx;
608762306a36Sopenharmony_ci	int ret = 0, mcast_rate;
608862306a36Sopenharmony_ci	enum nl80211_band band;
608962306a36Sopenharmony_ci
609062306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
609162306a36Sopenharmony_ci
609262306a36Sopenharmony_ci	if (changed & BSS_CHANGED_IBSS)
609362306a36Sopenharmony_ci		ath10k_control_ibss(arvif, vif);
609462306a36Sopenharmony_ci
609562306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BEACON_INT) {
609662306a36Sopenharmony_ci		arvif->beacon_interval = info->beacon_int;
609762306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->beacon_interval;
609862306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
609962306a36Sopenharmony_ci						arvif->beacon_interval);
610062306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
610162306a36Sopenharmony_ci			   "mac vdev %d beacon_interval %d\n",
610262306a36Sopenharmony_ci			   arvif->vdev_id, arvif->beacon_interval);
610362306a36Sopenharmony_ci
610462306a36Sopenharmony_ci		if (ret)
610562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set beacon interval for vdev %d: %i\n",
610662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
610762306a36Sopenharmony_ci	}
610862306a36Sopenharmony_ci
610962306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BEACON) {
611062306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
611162306a36Sopenharmony_ci			   "vdev %d set beacon tx mode to staggered\n",
611262306a36Sopenharmony_ci			   arvif->vdev_id);
611362306a36Sopenharmony_ci
611462306a36Sopenharmony_ci		pdev_param = ar->wmi.pdev_param->beacon_tx_mode;
611562306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
611662306a36Sopenharmony_ci						WMI_BEACON_STAGGERED_MODE);
611762306a36Sopenharmony_ci		if (ret)
611862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
611962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
612062306a36Sopenharmony_ci
612162306a36Sopenharmony_ci		ret = ath10k_mac_setup_bcn_tmpl(arvif);
612262306a36Sopenharmony_ci		if (ret)
612362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update beacon template: %d\n",
612462306a36Sopenharmony_ci				    ret);
612562306a36Sopenharmony_ci
612662306a36Sopenharmony_ci		if (ieee80211_vif_is_mesh(vif)) {
612762306a36Sopenharmony_ci			/* mesh doesn't use SSID but firmware needs it */
612862306a36Sopenharmony_ci			strncpy(arvif->u.ap.ssid, "mesh",
612962306a36Sopenharmony_ci				sizeof(arvif->u.ap.ssid));
613062306a36Sopenharmony_ci			arvif->u.ap.ssid_len = 4;
613162306a36Sopenharmony_ci		}
613262306a36Sopenharmony_ci	}
613362306a36Sopenharmony_ci
613462306a36Sopenharmony_ci	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
613562306a36Sopenharmony_ci		ret = ath10k_mac_setup_prb_tmpl(arvif);
613662306a36Sopenharmony_ci		if (ret)
613762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
613862306a36Sopenharmony_ci				    arvif->vdev_id, ret);
613962306a36Sopenharmony_ci	}
614062306a36Sopenharmony_ci
614162306a36Sopenharmony_ci	if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
614262306a36Sopenharmony_ci		arvif->dtim_period = info->dtim_period;
614362306a36Sopenharmony_ci
614462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
614562306a36Sopenharmony_ci			   "mac vdev %d dtim_period %d\n",
614662306a36Sopenharmony_ci			   arvif->vdev_id, arvif->dtim_period);
614762306a36Sopenharmony_ci
614862306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->dtim_period;
614962306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
615062306a36Sopenharmony_ci						arvif->dtim_period);
615162306a36Sopenharmony_ci		if (ret)
615262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set dtim period for vdev %d: %i\n",
615362306a36Sopenharmony_ci				    arvif->vdev_id, ret);
615462306a36Sopenharmony_ci	}
615562306a36Sopenharmony_ci
615662306a36Sopenharmony_ci	if (changed & BSS_CHANGED_SSID &&
615762306a36Sopenharmony_ci	    vif->type == NL80211_IFTYPE_AP) {
615862306a36Sopenharmony_ci		arvif->u.ap.ssid_len = vif->cfg.ssid_len;
615962306a36Sopenharmony_ci		if (vif->cfg.ssid_len)
616062306a36Sopenharmony_ci			memcpy(arvif->u.ap.ssid, vif->cfg.ssid,
616162306a36Sopenharmony_ci			       vif->cfg.ssid_len);
616262306a36Sopenharmony_ci		arvif->u.ap.hidden_ssid = info->hidden_ssid;
616362306a36Sopenharmony_ci	}
616462306a36Sopenharmony_ci
616562306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid))
616662306a36Sopenharmony_ci		ether_addr_copy(arvif->bssid, info->bssid);
616762306a36Sopenharmony_ci
616862306a36Sopenharmony_ci	if (changed & BSS_CHANGED_FTM_RESPONDER &&
616962306a36Sopenharmony_ci	    arvif->ftm_responder != info->ftm_responder &&
617062306a36Sopenharmony_ci	    test_bit(WMI_SERVICE_RTT_RESPONDER_ROLE, ar->wmi.svc_map)) {
617162306a36Sopenharmony_ci		arvif->ftm_responder = info->ftm_responder;
617262306a36Sopenharmony_ci
617362306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->rtt_responder_role;
617462306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
617562306a36Sopenharmony_ci						arvif->ftm_responder);
617662306a36Sopenharmony_ci
617762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
617862306a36Sopenharmony_ci			   "mac vdev %d ftm_responder %d:ret %d\n",
617962306a36Sopenharmony_ci			   arvif->vdev_id, arvif->ftm_responder, ret);
618062306a36Sopenharmony_ci	}
618162306a36Sopenharmony_ci
618262306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BEACON_ENABLED)
618362306a36Sopenharmony_ci		ath10k_control_beaconing(arvif, info);
618462306a36Sopenharmony_ci
618562306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
618662306a36Sopenharmony_ci		arvif->use_cts_prot = info->use_cts_prot;
618762306a36Sopenharmony_ci
618862306a36Sopenharmony_ci		ret = ath10k_recalc_rtscts_prot(arvif);
618962306a36Sopenharmony_ci		if (ret)
619062306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
619162306a36Sopenharmony_ci				    arvif->vdev_id, ret);
619262306a36Sopenharmony_ci
619362306a36Sopenharmony_ci		if (ath10k_mac_can_set_cts_prot(arvif)) {
619462306a36Sopenharmony_ci			ret = ath10k_mac_set_cts_prot(arvif);
619562306a36Sopenharmony_ci			if (ret)
619662306a36Sopenharmony_ci				ath10k_warn(ar, "failed to set cts protection for vdev %d: %d\n",
619762306a36Sopenharmony_ci					    arvif->vdev_id, ret);
619862306a36Sopenharmony_ci		}
619962306a36Sopenharmony_ci	}
620062306a36Sopenharmony_ci
620162306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_SLOT) {
620262306a36Sopenharmony_ci		if (info->use_short_slot)
620362306a36Sopenharmony_ci			slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
620462306a36Sopenharmony_ci
620562306a36Sopenharmony_ci		else
620662306a36Sopenharmony_ci			slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
620762306a36Sopenharmony_ci
620862306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
620962306a36Sopenharmony_ci			   arvif->vdev_id, slottime);
621062306a36Sopenharmony_ci
621162306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->slot_time;
621262306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
621362306a36Sopenharmony_ci						slottime);
621462306a36Sopenharmony_ci		if (ret)
621562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set erp slot for vdev %d: %i\n",
621662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
621762306a36Sopenharmony_ci	}
621862306a36Sopenharmony_ci
621962306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
622062306a36Sopenharmony_ci		if (info->use_short_preamble)
622162306a36Sopenharmony_ci			preamble = WMI_VDEV_PREAMBLE_SHORT;
622262306a36Sopenharmony_ci		else
622362306a36Sopenharmony_ci			preamble = WMI_VDEV_PREAMBLE_LONG;
622462306a36Sopenharmony_ci
622562306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
622662306a36Sopenharmony_ci			   "mac vdev %d preamble %dn",
622762306a36Sopenharmony_ci			   arvif->vdev_id, preamble);
622862306a36Sopenharmony_ci
622962306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->preamble;
623062306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
623162306a36Sopenharmony_ci						preamble);
623262306a36Sopenharmony_ci		if (ret)
623362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set preamble for vdev %d: %i\n",
623462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
623562306a36Sopenharmony_ci	}
623662306a36Sopenharmony_ci
623762306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ASSOC) {
623862306a36Sopenharmony_ci		if (vif->cfg.assoc) {
623962306a36Sopenharmony_ci			/* Workaround: Make sure monitor vdev is not running
624062306a36Sopenharmony_ci			 * when associating to prevent some firmware revisions
624162306a36Sopenharmony_ci			 * (e.g. 10.1 and 10.2) from crashing.
624262306a36Sopenharmony_ci			 */
624362306a36Sopenharmony_ci			if (ar->monitor_started)
624462306a36Sopenharmony_ci				ath10k_monitor_stop(ar);
624562306a36Sopenharmony_ci			ath10k_bss_assoc(hw, vif, info);
624662306a36Sopenharmony_ci			ath10k_monitor_recalc(ar);
624762306a36Sopenharmony_ci		} else {
624862306a36Sopenharmony_ci			ath10k_bss_disassoc(hw, vif);
624962306a36Sopenharmony_ci		}
625062306a36Sopenharmony_ci	}
625162306a36Sopenharmony_ci
625262306a36Sopenharmony_ci	if (changed & BSS_CHANGED_TXPOWER) {
625362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev_id %i txpower %d\n",
625462306a36Sopenharmony_ci			   arvif->vdev_id, info->txpower);
625562306a36Sopenharmony_ci
625662306a36Sopenharmony_ci		arvif->txpower = info->txpower;
625762306a36Sopenharmony_ci		ret = ath10k_mac_txpower_recalc(ar);
625862306a36Sopenharmony_ci		if (ret)
625962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
626062306a36Sopenharmony_ci	}
626162306a36Sopenharmony_ci
626262306a36Sopenharmony_ci	if (changed & BSS_CHANGED_PS) {
626362306a36Sopenharmony_ci		arvif->ps = vif->cfg.ps;
626462306a36Sopenharmony_ci
626562306a36Sopenharmony_ci		ret = ath10k_config_ps(ar);
626662306a36Sopenharmony_ci		if (ret)
626762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
626862306a36Sopenharmony_ci				    arvif->vdev_id, ret);
626962306a36Sopenharmony_ci	}
627062306a36Sopenharmony_ci
627162306a36Sopenharmony_ci	if (changed & BSS_CHANGED_MCAST_RATE &&
627262306a36Sopenharmony_ci	    !ath10k_mac_vif_chan(arvif->vif, &def)) {
627362306a36Sopenharmony_ci		band = def.chan->band;
627462306a36Sopenharmony_ci		mcast_rate = vif->bss_conf.mcast_rate[band];
627562306a36Sopenharmony_ci		if (mcast_rate > 0)
627662306a36Sopenharmony_ci			rateidx = mcast_rate - 1;
627762306a36Sopenharmony_ci		else
627862306a36Sopenharmony_ci			rateidx = ffs(vif->bss_conf.basic_rates) - 1;
627962306a36Sopenharmony_ci
628062306a36Sopenharmony_ci		if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY)
628162306a36Sopenharmony_ci			rateidx += ATH10K_MAC_FIRST_OFDM_RATE_IDX;
628262306a36Sopenharmony_ci
628362306a36Sopenharmony_ci		bitrate = ath10k_wmi_legacy_rates[rateidx].bitrate;
628462306a36Sopenharmony_ci		hw_value = ath10k_wmi_legacy_rates[rateidx].hw_value;
628562306a36Sopenharmony_ci		if (ath10k_mac_bitrate_is_cck(bitrate))
628662306a36Sopenharmony_ci			preamble = WMI_RATE_PREAMBLE_CCK;
628762306a36Sopenharmony_ci		else
628862306a36Sopenharmony_ci			preamble = WMI_RATE_PREAMBLE_OFDM;
628962306a36Sopenharmony_ci
629062306a36Sopenharmony_ci		rate = ATH10K_HW_RATECODE(hw_value, 0, preamble);
629162306a36Sopenharmony_ci
629262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
629362306a36Sopenharmony_ci			   "mac vdev %d mcast_rate %x\n",
629462306a36Sopenharmony_ci			   arvif->vdev_id, rate);
629562306a36Sopenharmony_ci
629662306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->mcast_data_rate;
629762306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
629862306a36Sopenharmony_ci						vdev_param, rate);
629962306a36Sopenharmony_ci		if (ret)
630062306a36Sopenharmony_ci			ath10k_warn(ar,
630162306a36Sopenharmony_ci				    "failed to set mcast rate on vdev %i: %d\n",
630262306a36Sopenharmony_ci				    arvif->vdev_id,  ret);
630362306a36Sopenharmony_ci
630462306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->bcast_data_rate;
630562306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
630662306a36Sopenharmony_ci						vdev_param, rate);
630762306a36Sopenharmony_ci		if (ret)
630862306a36Sopenharmony_ci			ath10k_warn(ar,
630962306a36Sopenharmony_ci				    "failed to set bcast rate on vdev %i: %d\n",
631062306a36Sopenharmony_ci				    arvif->vdev_id,  ret);
631162306a36Sopenharmony_ci	}
631262306a36Sopenharmony_ci
631362306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BASIC_RATES &&
631462306a36Sopenharmony_ci	    !ath10k_mac_vif_chan(arvif->vif, &def))
631562306a36Sopenharmony_ci		ath10k_recalculate_mgmt_rate(ar, vif, &def);
631662306a36Sopenharmony_ci
631762306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
631862306a36Sopenharmony_ci}
631962306a36Sopenharmony_ci
632062306a36Sopenharmony_cistatic void ath10k_mac_op_set_coverage_class(struct ieee80211_hw *hw, s16 value)
632162306a36Sopenharmony_ci{
632262306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
632362306a36Sopenharmony_ci
632462306a36Sopenharmony_ci	/* This function should never be called if setting the coverage class
632562306a36Sopenharmony_ci	 * is not supported on this hardware.
632662306a36Sopenharmony_ci	 */
632762306a36Sopenharmony_ci	if (!ar->hw_params.hw_ops->set_coverage_class) {
632862306a36Sopenharmony_ci		WARN_ON_ONCE(1);
632962306a36Sopenharmony_ci		return;
633062306a36Sopenharmony_ci	}
633162306a36Sopenharmony_ci	ar->hw_params.hw_ops->set_coverage_class(ar, value);
633262306a36Sopenharmony_ci}
633362306a36Sopenharmony_ci
633462306a36Sopenharmony_cistruct ath10k_mac_tdls_iter_data {
633562306a36Sopenharmony_ci	u32 num_tdls_stations;
633662306a36Sopenharmony_ci	struct ieee80211_vif *curr_vif;
633762306a36Sopenharmony_ci};
633862306a36Sopenharmony_ci
633962306a36Sopenharmony_cistatic void ath10k_mac_tdls_vif_stations_count_iter(void *data,
634062306a36Sopenharmony_ci						    struct ieee80211_sta *sta)
634162306a36Sopenharmony_ci{
634262306a36Sopenharmony_ci	struct ath10k_mac_tdls_iter_data *iter_data = data;
634362306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
634462306a36Sopenharmony_ci	struct ieee80211_vif *sta_vif = arsta->arvif->vif;
634562306a36Sopenharmony_ci
634662306a36Sopenharmony_ci	if (sta->tdls && sta_vif == iter_data->curr_vif)
634762306a36Sopenharmony_ci		iter_data->num_tdls_stations++;
634862306a36Sopenharmony_ci}
634962306a36Sopenharmony_ci
635062306a36Sopenharmony_cistatic int ath10k_mac_tdls_vif_stations_count(struct ieee80211_hw *hw,
635162306a36Sopenharmony_ci					      struct ieee80211_vif *vif)
635262306a36Sopenharmony_ci{
635362306a36Sopenharmony_ci	struct ath10k_mac_tdls_iter_data data = {};
635462306a36Sopenharmony_ci
635562306a36Sopenharmony_ci	data.curr_vif = vif;
635662306a36Sopenharmony_ci
635762306a36Sopenharmony_ci	ieee80211_iterate_stations_atomic(hw,
635862306a36Sopenharmony_ci					  ath10k_mac_tdls_vif_stations_count_iter,
635962306a36Sopenharmony_ci					  &data);
636062306a36Sopenharmony_ci	return data.num_tdls_stations;
636162306a36Sopenharmony_ci}
636262306a36Sopenharmony_ci
636362306a36Sopenharmony_cistatic int ath10k_hw_scan(struct ieee80211_hw *hw,
636462306a36Sopenharmony_ci			  struct ieee80211_vif *vif,
636562306a36Sopenharmony_ci			  struct ieee80211_scan_request *hw_req)
636662306a36Sopenharmony_ci{
636762306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
636862306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
636962306a36Sopenharmony_ci	struct cfg80211_scan_request *req = &hw_req->req;
637062306a36Sopenharmony_ci	struct wmi_start_scan_arg arg;
637162306a36Sopenharmony_ci	int ret = 0;
637262306a36Sopenharmony_ci	int i;
637362306a36Sopenharmony_ci	u32 scan_timeout;
637462306a36Sopenharmony_ci
637562306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
637662306a36Sopenharmony_ci
637762306a36Sopenharmony_ci	if (ath10k_mac_tdls_vif_stations_count(hw, vif) > 0) {
637862306a36Sopenharmony_ci		ret = -EBUSY;
637962306a36Sopenharmony_ci		goto exit;
638062306a36Sopenharmony_ci	}
638162306a36Sopenharmony_ci
638262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
638362306a36Sopenharmony_ci	switch (ar->scan.state) {
638462306a36Sopenharmony_ci	case ATH10K_SCAN_IDLE:
638562306a36Sopenharmony_ci		reinit_completion(&ar->scan.started);
638662306a36Sopenharmony_ci		reinit_completion(&ar->scan.completed);
638762306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_STARTING;
638862306a36Sopenharmony_ci		ar->scan.is_roc = false;
638962306a36Sopenharmony_ci		ar->scan.vdev_id = arvif->vdev_id;
639062306a36Sopenharmony_ci		ret = 0;
639162306a36Sopenharmony_ci		break;
639262306a36Sopenharmony_ci	case ATH10K_SCAN_STARTING:
639362306a36Sopenharmony_ci	case ATH10K_SCAN_RUNNING:
639462306a36Sopenharmony_ci	case ATH10K_SCAN_ABORTING:
639562306a36Sopenharmony_ci		ret = -EBUSY;
639662306a36Sopenharmony_ci		break;
639762306a36Sopenharmony_ci	}
639862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
639962306a36Sopenharmony_ci
640062306a36Sopenharmony_ci	if (ret)
640162306a36Sopenharmony_ci		goto exit;
640262306a36Sopenharmony_ci
640362306a36Sopenharmony_ci	memset(&arg, 0, sizeof(arg));
640462306a36Sopenharmony_ci	ath10k_wmi_start_scan_init(ar, &arg);
640562306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
640662306a36Sopenharmony_ci	arg.scan_id = ATH10K_SCAN_ID;
640762306a36Sopenharmony_ci
640862306a36Sopenharmony_ci	if (req->ie_len) {
640962306a36Sopenharmony_ci		arg.ie_len = req->ie_len;
641062306a36Sopenharmony_ci		memcpy(arg.ie, req->ie, arg.ie_len);
641162306a36Sopenharmony_ci	}
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_ci	if (req->n_ssids) {
641462306a36Sopenharmony_ci		arg.n_ssids = req->n_ssids;
641562306a36Sopenharmony_ci		for (i = 0; i < arg.n_ssids; i++) {
641662306a36Sopenharmony_ci			arg.ssids[i].len  = req->ssids[i].ssid_len;
641762306a36Sopenharmony_ci			arg.ssids[i].ssid = req->ssids[i].ssid;
641862306a36Sopenharmony_ci		}
641962306a36Sopenharmony_ci	} else {
642062306a36Sopenharmony_ci		arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
642162306a36Sopenharmony_ci	}
642262306a36Sopenharmony_ci
642362306a36Sopenharmony_ci	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
642462306a36Sopenharmony_ci		arg.scan_ctrl_flags |=  WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ;
642562306a36Sopenharmony_ci		ether_addr_copy(arg.mac_addr.addr, req->mac_addr);
642662306a36Sopenharmony_ci		ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask);
642762306a36Sopenharmony_ci	}
642862306a36Sopenharmony_ci
642962306a36Sopenharmony_ci	if (req->n_channels) {
643062306a36Sopenharmony_ci		arg.n_channels = req->n_channels;
643162306a36Sopenharmony_ci		for (i = 0; i < arg.n_channels; i++)
643262306a36Sopenharmony_ci			arg.channels[i] = req->channels[i]->center_freq;
643362306a36Sopenharmony_ci	}
643462306a36Sopenharmony_ci
643562306a36Sopenharmony_ci	/* if duration is set, default dwell times will be overwritten */
643662306a36Sopenharmony_ci	if (req->duration) {
643762306a36Sopenharmony_ci		arg.dwell_time_active = req->duration;
643862306a36Sopenharmony_ci		arg.dwell_time_passive = req->duration;
643962306a36Sopenharmony_ci		arg.burst_duration_ms = req->duration;
644062306a36Sopenharmony_ci
644162306a36Sopenharmony_ci		scan_timeout = min_t(u32, arg.max_rest_time *
644262306a36Sopenharmony_ci				(arg.n_channels - 1) + (req->duration +
644362306a36Sopenharmony_ci				ATH10K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD) *
644462306a36Sopenharmony_ci				arg.n_channels, arg.max_scan_time);
644562306a36Sopenharmony_ci	} else {
644662306a36Sopenharmony_ci		scan_timeout = arg.max_scan_time;
644762306a36Sopenharmony_ci	}
644862306a36Sopenharmony_ci
644962306a36Sopenharmony_ci	/* Add a 200ms margin to account for event/command processing */
645062306a36Sopenharmony_ci	scan_timeout += 200;
645162306a36Sopenharmony_ci
645262306a36Sopenharmony_ci	ret = ath10k_start_scan(ar, &arg);
645362306a36Sopenharmony_ci	if (ret) {
645462306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start hw scan: %d\n", ret);
645562306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
645662306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_IDLE;
645762306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
645862306a36Sopenharmony_ci	}
645962306a36Sopenharmony_ci
646062306a36Sopenharmony_ci	ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
646162306a36Sopenharmony_ci				     msecs_to_jiffies(scan_timeout));
646262306a36Sopenharmony_ci
646362306a36Sopenharmony_ciexit:
646462306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
646562306a36Sopenharmony_ci	return ret;
646662306a36Sopenharmony_ci}
646762306a36Sopenharmony_ci
646862306a36Sopenharmony_cistatic void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
646962306a36Sopenharmony_ci				  struct ieee80211_vif *vif)
647062306a36Sopenharmony_ci{
647162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
647462306a36Sopenharmony_ci	ath10k_scan_abort(ar);
647562306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
647662306a36Sopenharmony_ci
647762306a36Sopenharmony_ci	cancel_delayed_work_sync(&ar->scan.timeout);
647862306a36Sopenharmony_ci}
647962306a36Sopenharmony_ci
648062306a36Sopenharmony_cistatic void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
648162306a36Sopenharmony_ci					struct ath10k_vif *arvif,
648262306a36Sopenharmony_ci					enum set_key_cmd cmd,
648362306a36Sopenharmony_ci					struct ieee80211_key_conf *key)
648462306a36Sopenharmony_ci{
648562306a36Sopenharmony_ci	u32 vdev_param = arvif->ar->wmi.vdev_param->def_keyid;
648662306a36Sopenharmony_ci	int ret;
648762306a36Sopenharmony_ci
648862306a36Sopenharmony_ci	/* 10.1 firmware branch requires default key index to be set to group
648962306a36Sopenharmony_ci	 * key index after installing it. Otherwise FW/HW Txes corrupted
649062306a36Sopenharmony_ci	 * frames with multi-vif APs. This is not required for main firmware
649162306a36Sopenharmony_ci	 * branch (e.g. 636).
649262306a36Sopenharmony_ci	 *
649362306a36Sopenharmony_ci	 * This is also needed for 636 fw for IBSS-RSN to work more reliably.
649462306a36Sopenharmony_ci	 *
649562306a36Sopenharmony_ci	 * FIXME: It remains unknown if this is required for multi-vif STA
649662306a36Sopenharmony_ci	 * interfaces on 10.1.
649762306a36Sopenharmony_ci	 */
649862306a36Sopenharmony_ci
649962306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
650062306a36Sopenharmony_ci	    arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
650162306a36Sopenharmony_ci		return;
650262306a36Sopenharmony_ci
650362306a36Sopenharmony_ci	if (key->cipher == WLAN_CIPHER_SUITE_WEP40)
650462306a36Sopenharmony_ci		return;
650562306a36Sopenharmony_ci
650662306a36Sopenharmony_ci	if (key->cipher == WLAN_CIPHER_SUITE_WEP104)
650762306a36Sopenharmony_ci		return;
650862306a36Sopenharmony_ci
650962306a36Sopenharmony_ci	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
651062306a36Sopenharmony_ci		return;
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_ci	if (cmd != SET_KEY)
651362306a36Sopenharmony_ci		return;
651462306a36Sopenharmony_ci
651562306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
651662306a36Sopenharmony_ci					key->keyidx);
651762306a36Sopenharmony_ci	if (ret)
651862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set vdev %i group key as default key: %d\n",
651962306a36Sopenharmony_ci			    arvif->vdev_id, ret);
652062306a36Sopenharmony_ci}
652162306a36Sopenharmony_ci
652262306a36Sopenharmony_cistatic int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
652362306a36Sopenharmony_ci			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
652462306a36Sopenharmony_ci			  struct ieee80211_key_conf *key)
652562306a36Sopenharmony_ci{
652662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
652762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
652862306a36Sopenharmony_ci	struct ath10k_sta *arsta;
652962306a36Sopenharmony_ci	struct ath10k_peer *peer;
653062306a36Sopenharmony_ci	const u8 *peer_addr;
653162306a36Sopenharmony_ci	bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
653262306a36Sopenharmony_ci		      key->cipher == WLAN_CIPHER_SUITE_WEP104;
653362306a36Sopenharmony_ci	int ret = 0;
653462306a36Sopenharmony_ci	int ret2;
653562306a36Sopenharmony_ci	u32 flags = 0;
653662306a36Sopenharmony_ci	u32 flags2;
653762306a36Sopenharmony_ci
653862306a36Sopenharmony_ci	/* this one needs to be done in software */
653962306a36Sopenharmony_ci	if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
654062306a36Sopenharmony_ci	    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
654162306a36Sopenharmony_ci	    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
654262306a36Sopenharmony_ci	    key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
654362306a36Sopenharmony_ci		return 1;
654462306a36Sopenharmony_ci
654562306a36Sopenharmony_ci	if (arvif->nohwcrypt)
654662306a36Sopenharmony_ci		return 1;
654762306a36Sopenharmony_ci
654862306a36Sopenharmony_ci	if (key->keyidx > WMI_MAX_KEY_INDEX)
654962306a36Sopenharmony_ci		return -ENOSPC;
655062306a36Sopenharmony_ci
655162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
655262306a36Sopenharmony_ci
655362306a36Sopenharmony_ci	if (sta) {
655462306a36Sopenharmony_ci		arsta = (struct ath10k_sta *)sta->drv_priv;
655562306a36Sopenharmony_ci		peer_addr = sta->addr;
655662306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
655762306a36Sopenharmony_ci		arsta->ucast_cipher = key->cipher;
655862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
655962306a36Sopenharmony_ci	} else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
656062306a36Sopenharmony_ci		peer_addr = vif->bss_conf.bssid;
656162306a36Sopenharmony_ci	} else {
656262306a36Sopenharmony_ci		peer_addr = vif->addr;
656362306a36Sopenharmony_ci	}
656462306a36Sopenharmony_ci
656562306a36Sopenharmony_ci	key->hw_key_idx = key->keyidx;
656662306a36Sopenharmony_ci
656762306a36Sopenharmony_ci	if (is_wep) {
656862306a36Sopenharmony_ci		if (cmd == SET_KEY)
656962306a36Sopenharmony_ci			arvif->wep_keys[key->keyidx] = key;
657062306a36Sopenharmony_ci		else
657162306a36Sopenharmony_ci			arvif->wep_keys[key->keyidx] = NULL;
657262306a36Sopenharmony_ci	}
657362306a36Sopenharmony_ci
657462306a36Sopenharmony_ci	/* the peer should not disappear in mid-way (unless FW goes awry) since
657562306a36Sopenharmony_ci	 * we already hold conf_mutex. we just make sure its there now.
657662306a36Sopenharmony_ci	 */
657762306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
657862306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
657962306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
658062306a36Sopenharmony_ci
658162306a36Sopenharmony_ci	if (!peer) {
658262306a36Sopenharmony_ci		if (cmd == SET_KEY) {
658362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to install key for non-existent peer %pM\n",
658462306a36Sopenharmony_ci				    peer_addr);
658562306a36Sopenharmony_ci			ret = -EOPNOTSUPP;
658662306a36Sopenharmony_ci			goto exit;
658762306a36Sopenharmony_ci		} else {
658862306a36Sopenharmony_ci			/* if the peer doesn't exist there is no key to disable anymore */
658962306a36Sopenharmony_ci			goto exit;
659062306a36Sopenharmony_ci		}
659162306a36Sopenharmony_ci	}
659262306a36Sopenharmony_ci
659362306a36Sopenharmony_ci	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
659462306a36Sopenharmony_ci		flags |= WMI_KEY_PAIRWISE;
659562306a36Sopenharmony_ci	else
659662306a36Sopenharmony_ci		flags |= WMI_KEY_GROUP;
659762306a36Sopenharmony_ci
659862306a36Sopenharmony_ci	if (is_wep) {
659962306a36Sopenharmony_ci		if (cmd == DISABLE_KEY)
660062306a36Sopenharmony_ci			ath10k_clear_vdev_key(arvif, key);
660162306a36Sopenharmony_ci
660262306a36Sopenharmony_ci		/* When WEP keys are uploaded it's possible that there are
660362306a36Sopenharmony_ci		 * stations associated already (e.g. when merging) without any
660462306a36Sopenharmony_ci		 * keys. Static WEP needs an explicit per-peer key upload.
660562306a36Sopenharmony_ci		 */
660662306a36Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_ADHOC &&
660762306a36Sopenharmony_ci		    cmd == SET_KEY)
660862306a36Sopenharmony_ci			ath10k_mac_vif_update_wep_key(arvif, key);
660962306a36Sopenharmony_ci
661062306a36Sopenharmony_ci		/* 802.1x never sets the def_wep_key_idx so each set_key()
661162306a36Sopenharmony_ci		 * call changes default tx key.
661262306a36Sopenharmony_ci		 *
661362306a36Sopenharmony_ci		 * Static WEP sets def_wep_key_idx via .set_default_unicast_key
661462306a36Sopenharmony_ci		 * after first set_key().
661562306a36Sopenharmony_ci		 */
661662306a36Sopenharmony_ci		if (cmd == SET_KEY && arvif->def_wep_key_idx == -1)
661762306a36Sopenharmony_ci			flags |= WMI_KEY_TX_USAGE;
661862306a36Sopenharmony_ci	}
661962306a36Sopenharmony_ci
662062306a36Sopenharmony_ci	ret = ath10k_install_key(arvif, key, cmd, peer_addr, flags);
662162306a36Sopenharmony_ci	if (ret) {
662262306a36Sopenharmony_ci		WARN_ON(ret > 0);
662362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
662462306a36Sopenharmony_ci			    arvif->vdev_id, peer_addr, ret);
662562306a36Sopenharmony_ci		goto exit;
662662306a36Sopenharmony_ci	}
662762306a36Sopenharmony_ci
662862306a36Sopenharmony_ci	/* mac80211 sets static WEP keys as groupwise while firmware requires
662962306a36Sopenharmony_ci	 * them to be installed twice as both pairwise and groupwise.
663062306a36Sopenharmony_ci	 */
663162306a36Sopenharmony_ci	if (is_wep && !sta && vif->type == NL80211_IFTYPE_STATION) {
663262306a36Sopenharmony_ci		flags2 = flags;
663362306a36Sopenharmony_ci		flags2 &= ~WMI_KEY_GROUP;
663462306a36Sopenharmony_ci		flags2 |= WMI_KEY_PAIRWISE;
663562306a36Sopenharmony_ci
663662306a36Sopenharmony_ci		ret = ath10k_install_key(arvif, key, cmd, peer_addr, flags2);
663762306a36Sopenharmony_ci		if (ret) {
663862306a36Sopenharmony_ci			WARN_ON(ret > 0);
663962306a36Sopenharmony_ci			ath10k_warn(ar, "failed to install (ucast) key for vdev %i peer %pM: %d\n",
664062306a36Sopenharmony_ci				    arvif->vdev_id, peer_addr, ret);
664162306a36Sopenharmony_ci			ret2 = ath10k_install_key(arvif, key, DISABLE_KEY,
664262306a36Sopenharmony_ci						  peer_addr, flags);
664362306a36Sopenharmony_ci			if (ret2) {
664462306a36Sopenharmony_ci				WARN_ON(ret2 > 0);
664562306a36Sopenharmony_ci				ath10k_warn(ar, "failed to disable (mcast) key for vdev %i peer %pM: %d\n",
664662306a36Sopenharmony_ci					    arvif->vdev_id, peer_addr, ret2);
664762306a36Sopenharmony_ci			}
664862306a36Sopenharmony_ci			goto exit;
664962306a36Sopenharmony_ci		}
665062306a36Sopenharmony_ci	}
665162306a36Sopenharmony_ci
665262306a36Sopenharmony_ci	ath10k_set_key_h_def_keyidx(ar, arvif, cmd, key);
665362306a36Sopenharmony_ci
665462306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
665562306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
665662306a36Sopenharmony_ci	if (peer && cmd == SET_KEY)
665762306a36Sopenharmony_ci		peer->keys[key->keyidx] = key;
665862306a36Sopenharmony_ci	else if (peer && cmd == DISABLE_KEY)
665962306a36Sopenharmony_ci		peer->keys[key->keyidx] = NULL;
666062306a36Sopenharmony_ci	else if (peer == NULL)
666162306a36Sopenharmony_ci		/* impossible unless FW goes crazy */
666262306a36Sopenharmony_ci		ath10k_warn(ar, "Peer %pM disappeared!\n", peer_addr);
666362306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
666462306a36Sopenharmony_ci
666562306a36Sopenharmony_ci	if (sta && sta->tdls)
666662306a36Sopenharmony_ci		ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
666762306a36Sopenharmony_ci					  ar->wmi.peer_param->authorize, 1);
666862306a36Sopenharmony_ci	else if (sta && cmd == SET_KEY && (key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
666962306a36Sopenharmony_ci		ath10k_wmi_peer_set_param(ar, arvif->vdev_id, peer_addr,
667062306a36Sopenharmony_ci					  ar->wmi.peer_param->authorize, 1);
667162306a36Sopenharmony_ci
667262306a36Sopenharmony_ciexit:
667362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
667462306a36Sopenharmony_ci	return ret;
667562306a36Sopenharmony_ci}
667662306a36Sopenharmony_ci
667762306a36Sopenharmony_cistatic void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
667862306a36Sopenharmony_ci					   struct ieee80211_vif *vif,
667962306a36Sopenharmony_ci					   int keyidx)
668062306a36Sopenharmony_ci{
668162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
668262306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
668362306a36Sopenharmony_ci	int ret;
668462306a36Sopenharmony_ci
668562306a36Sopenharmony_ci	mutex_lock(&arvif->ar->conf_mutex);
668662306a36Sopenharmony_ci
668762306a36Sopenharmony_ci	if (arvif->ar->state != ATH10K_STATE_ON)
668862306a36Sopenharmony_ci		goto unlock;
668962306a36Sopenharmony_ci
669062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
669162306a36Sopenharmony_ci		   arvif->vdev_id, keyidx);
669262306a36Sopenharmony_ci
669362306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(arvif->ar,
669462306a36Sopenharmony_ci					arvif->vdev_id,
669562306a36Sopenharmony_ci					arvif->ar->wmi.vdev_param->def_keyid,
669662306a36Sopenharmony_ci					keyidx);
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_ci	if (ret) {
669962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
670062306a36Sopenharmony_ci			    arvif->vdev_id,
670162306a36Sopenharmony_ci			    ret);
670262306a36Sopenharmony_ci		goto unlock;
670362306a36Sopenharmony_ci	}
670462306a36Sopenharmony_ci
670562306a36Sopenharmony_ci	arvif->def_wep_key_idx = keyidx;
670662306a36Sopenharmony_ci
670762306a36Sopenharmony_ciunlock:
670862306a36Sopenharmony_ci	mutex_unlock(&arvif->ar->conf_mutex);
670962306a36Sopenharmony_ci}
671062306a36Sopenharmony_ci
671162306a36Sopenharmony_cistatic void ath10k_sta_rc_update_wk(struct work_struct *wk)
671262306a36Sopenharmony_ci{
671362306a36Sopenharmony_ci	struct ath10k *ar;
671462306a36Sopenharmony_ci	struct ath10k_vif *arvif;
671562306a36Sopenharmony_ci	struct ath10k_sta *arsta;
671662306a36Sopenharmony_ci	struct ieee80211_sta *sta;
671762306a36Sopenharmony_ci	struct cfg80211_chan_def def;
671862306a36Sopenharmony_ci	enum nl80211_band band;
671962306a36Sopenharmony_ci	const u8 *ht_mcs_mask;
672062306a36Sopenharmony_ci	const u16 *vht_mcs_mask;
672162306a36Sopenharmony_ci	u32 changed, bw, nss, smps;
672262306a36Sopenharmony_ci	int err;
672362306a36Sopenharmony_ci
672462306a36Sopenharmony_ci	arsta = container_of(wk, struct ath10k_sta, update_wk);
672562306a36Sopenharmony_ci	sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
672662306a36Sopenharmony_ci	arvif = arsta->arvif;
672762306a36Sopenharmony_ci	ar = arvif->ar;
672862306a36Sopenharmony_ci
672962306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(arvif->vif, &def)))
673062306a36Sopenharmony_ci		return;
673162306a36Sopenharmony_ci
673262306a36Sopenharmony_ci	band = def.chan->band;
673362306a36Sopenharmony_ci	ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
673462306a36Sopenharmony_ci	vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
673562306a36Sopenharmony_ci
673662306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
673762306a36Sopenharmony_ci
673862306a36Sopenharmony_ci	changed = arsta->changed;
673962306a36Sopenharmony_ci	arsta->changed = 0;
674062306a36Sopenharmony_ci
674162306a36Sopenharmony_ci	bw = arsta->bw;
674262306a36Sopenharmony_ci	nss = arsta->nss;
674362306a36Sopenharmony_ci	smps = arsta->smps;
674462306a36Sopenharmony_ci
674562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
674662306a36Sopenharmony_ci
674762306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
674862306a36Sopenharmony_ci
674962306a36Sopenharmony_ci	nss = max_t(u32, 1, nss);
675062306a36Sopenharmony_ci	nss = min(nss, max(ath10k_mac_max_ht_nss(ht_mcs_mask),
675162306a36Sopenharmony_ci			   ath10k_mac_max_vht_nss(vht_mcs_mask)));
675262306a36Sopenharmony_ci
675362306a36Sopenharmony_ci	if (changed & IEEE80211_RC_BW_CHANGED) {
675462306a36Sopenharmony_ci		enum wmi_phy_mode mode;
675562306a36Sopenharmony_ci
675662306a36Sopenharmony_ci		mode = chan_to_phymode(&def);
675762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM peer bw %d phymode %d\n",
675862306a36Sopenharmony_ci			   sta->addr, bw, mode);
675962306a36Sopenharmony_ci
676062306a36Sopenharmony_ci		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
676162306a36Sopenharmony_ci						ar->wmi.peer_param->phymode, mode);
676262306a36Sopenharmony_ci		if (err) {
676362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update STA %pM peer phymode %d: %d\n",
676462306a36Sopenharmony_ci				    sta->addr, mode, err);
676562306a36Sopenharmony_ci			goto exit;
676662306a36Sopenharmony_ci		}
676762306a36Sopenharmony_ci
676862306a36Sopenharmony_ci		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
676962306a36Sopenharmony_ci						ar->wmi.peer_param->chan_width, bw);
677062306a36Sopenharmony_ci		if (err)
677162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update STA %pM peer bw %d: %d\n",
677262306a36Sopenharmony_ci				    sta->addr, bw, err);
677362306a36Sopenharmony_ci	}
677462306a36Sopenharmony_ci
677562306a36Sopenharmony_ci	if (changed & IEEE80211_RC_NSS_CHANGED) {
677662306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM nss %d\n",
677762306a36Sopenharmony_ci			   sta->addr, nss);
677862306a36Sopenharmony_ci
677962306a36Sopenharmony_ci		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
678062306a36Sopenharmony_ci						ar->wmi.peer_param->nss, nss);
678162306a36Sopenharmony_ci		if (err)
678262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update STA %pM nss %d: %d\n",
678362306a36Sopenharmony_ci				    sta->addr, nss, err);
678462306a36Sopenharmony_ci	}
678562306a36Sopenharmony_ci
678662306a36Sopenharmony_ci	if (changed & IEEE80211_RC_SMPS_CHANGED) {
678762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM smps %d\n",
678862306a36Sopenharmony_ci			   sta->addr, smps);
678962306a36Sopenharmony_ci
679062306a36Sopenharmony_ci		err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
679162306a36Sopenharmony_ci						ar->wmi.peer_param->smps_state, smps);
679262306a36Sopenharmony_ci		if (err)
679362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update STA %pM smps %d: %d\n",
679462306a36Sopenharmony_ci				    sta->addr, smps, err);
679562306a36Sopenharmony_ci	}
679662306a36Sopenharmony_ci
679762306a36Sopenharmony_ci	if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
679862306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM supp rates\n",
679962306a36Sopenharmony_ci			   sta->addr);
680062306a36Sopenharmony_ci
680162306a36Sopenharmony_ci		err = ath10k_station_assoc(ar, arvif->vif, sta, true);
680262306a36Sopenharmony_ci		if (err)
680362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to reassociate station: %pM\n",
680462306a36Sopenharmony_ci				    sta->addr);
680562306a36Sopenharmony_ci	}
680662306a36Sopenharmony_ci
680762306a36Sopenharmony_ciexit:
680862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
680962306a36Sopenharmony_ci}
681062306a36Sopenharmony_ci
681162306a36Sopenharmony_cistatic int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif,
681262306a36Sopenharmony_ci				       struct ieee80211_sta *sta)
681362306a36Sopenharmony_ci{
681462306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
681562306a36Sopenharmony_ci
681662306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
681762306a36Sopenharmony_ci
681862306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls)
681962306a36Sopenharmony_ci		return 0;
682062306a36Sopenharmony_ci
682162306a36Sopenharmony_ci	if (ar->num_stations >= ar->max_num_stations)
682262306a36Sopenharmony_ci		return -ENOBUFS;
682362306a36Sopenharmony_ci
682462306a36Sopenharmony_ci	ar->num_stations++;
682562306a36Sopenharmony_ci
682662306a36Sopenharmony_ci	return 0;
682762306a36Sopenharmony_ci}
682862306a36Sopenharmony_ci
682962306a36Sopenharmony_cistatic void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif,
683062306a36Sopenharmony_ci					struct ieee80211_sta *sta)
683162306a36Sopenharmony_ci{
683262306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
683362306a36Sopenharmony_ci
683462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
683562306a36Sopenharmony_ci
683662306a36Sopenharmony_ci	if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls)
683762306a36Sopenharmony_ci		return;
683862306a36Sopenharmony_ci
683962306a36Sopenharmony_ci	ar->num_stations--;
684062306a36Sopenharmony_ci}
684162306a36Sopenharmony_ci
684262306a36Sopenharmony_cistatic int ath10k_sta_set_txpwr(struct ieee80211_hw *hw,
684362306a36Sopenharmony_ci				struct ieee80211_vif *vif,
684462306a36Sopenharmony_ci				struct ieee80211_sta *sta)
684562306a36Sopenharmony_ci{
684662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
684762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
684862306a36Sopenharmony_ci	int ret = 0;
684962306a36Sopenharmony_ci	s16 txpwr;
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_ci	if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) {
685262306a36Sopenharmony_ci		txpwr = 0;
685362306a36Sopenharmony_ci	} else {
685462306a36Sopenharmony_ci		txpwr = sta->deflink.txpwr.power;
685562306a36Sopenharmony_ci		if (!txpwr)
685662306a36Sopenharmony_ci			return -EINVAL;
685762306a36Sopenharmony_ci	}
685862306a36Sopenharmony_ci
685962306a36Sopenharmony_ci	if (txpwr > ATH10K_TX_POWER_MAX_VAL || txpwr < ATH10K_TX_POWER_MIN_VAL)
686062306a36Sopenharmony_ci		return -EINVAL;
686162306a36Sopenharmony_ci
686262306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
686362306a36Sopenharmony_ci
686462306a36Sopenharmony_ci	ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
686562306a36Sopenharmony_ci					ar->wmi.peer_param->use_fixed_power, txpwr);
686662306a36Sopenharmony_ci	if (ret) {
686762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set tx power for station ret: %d\n",
686862306a36Sopenharmony_ci			    ret);
686962306a36Sopenharmony_ci		goto out;
687062306a36Sopenharmony_ci	}
687162306a36Sopenharmony_ci
687262306a36Sopenharmony_ciout:
687362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
687462306a36Sopenharmony_ci	return ret;
687562306a36Sopenharmony_ci}
687662306a36Sopenharmony_ci
687762306a36Sopenharmony_cistruct ath10k_mac_iter_tid_conf_data {
687862306a36Sopenharmony_ci	struct ieee80211_vif *curr_vif;
687962306a36Sopenharmony_ci	struct ath10k *ar;
688062306a36Sopenharmony_ci	bool reset_config;
688162306a36Sopenharmony_ci};
688262306a36Sopenharmony_ci
688362306a36Sopenharmony_cistatic bool
688462306a36Sopenharmony_ciath10k_mac_bitrate_mask_has_single_rate(struct ath10k *ar,
688562306a36Sopenharmony_ci					enum nl80211_band band,
688662306a36Sopenharmony_ci					const struct cfg80211_bitrate_mask *mask,
688762306a36Sopenharmony_ci					int *vht_num_rates)
688862306a36Sopenharmony_ci{
688962306a36Sopenharmony_ci	int num_rates = 0;
689062306a36Sopenharmony_ci	int i, tmp;
689162306a36Sopenharmony_ci
689262306a36Sopenharmony_ci	num_rates += hweight32(mask->control[band].legacy);
689362306a36Sopenharmony_ci
689462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++)
689562306a36Sopenharmony_ci		num_rates += hweight8(mask->control[band].ht_mcs[i]);
689662306a36Sopenharmony_ci
689762306a36Sopenharmony_ci	*vht_num_rates = 0;
689862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) {
689962306a36Sopenharmony_ci		tmp = hweight16(mask->control[band].vht_mcs[i]);
690062306a36Sopenharmony_ci		num_rates += tmp;
690162306a36Sopenharmony_ci		*vht_num_rates += tmp;
690262306a36Sopenharmony_ci	}
690362306a36Sopenharmony_ci
690462306a36Sopenharmony_ci	return num_rates == 1;
690562306a36Sopenharmony_ci}
690662306a36Sopenharmony_ci
690762306a36Sopenharmony_cistatic int
690862306a36Sopenharmony_ciath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar,
690962306a36Sopenharmony_ci					enum nl80211_band band,
691062306a36Sopenharmony_ci					const struct cfg80211_bitrate_mask *mask,
691162306a36Sopenharmony_ci					u8 *rate, u8 *nss, bool vht_only)
691262306a36Sopenharmony_ci{
691362306a36Sopenharmony_ci	int rate_idx;
691462306a36Sopenharmony_ci	int i;
691562306a36Sopenharmony_ci	u16 bitrate;
691662306a36Sopenharmony_ci	u8 preamble;
691762306a36Sopenharmony_ci	u8 hw_rate;
691862306a36Sopenharmony_ci
691962306a36Sopenharmony_ci	if (vht_only)
692062306a36Sopenharmony_ci		goto next;
692162306a36Sopenharmony_ci
692262306a36Sopenharmony_ci	if (hweight32(mask->control[band].legacy) == 1) {
692362306a36Sopenharmony_ci		rate_idx = ffs(mask->control[band].legacy) - 1;
692462306a36Sopenharmony_ci
692562306a36Sopenharmony_ci		if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY)
692662306a36Sopenharmony_ci			rate_idx += ATH10K_MAC_FIRST_OFDM_RATE_IDX;
692762306a36Sopenharmony_ci
692862306a36Sopenharmony_ci		hw_rate = ath10k_wmi_legacy_rates[rate_idx].hw_value;
692962306a36Sopenharmony_ci		bitrate = ath10k_wmi_legacy_rates[rate_idx].bitrate;
693062306a36Sopenharmony_ci
693162306a36Sopenharmony_ci		if (ath10k_mac_bitrate_is_cck(bitrate))
693262306a36Sopenharmony_ci			preamble = WMI_RATE_PREAMBLE_CCK;
693362306a36Sopenharmony_ci		else
693462306a36Sopenharmony_ci			preamble = WMI_RATE_PREAMBLE_OFDM;
693562306a36Sopenharmony_ci
693662306a36Sopenharmony_ci		*nss = 1;
693762306a36Sopenharmony_ci		*rate = preamble << 6 |
693862306a36Sopenharmony_ci			(*nss - 1) << 4 |
693962306a36Sopenharmony_ci			hw_rate << 0;
694062306a36Sopenharmony_ci
694162306a36Sopenharmony_ci		return 0;
694262306a36Sopenharmony_ci	}
694362306a36Sopenharmony_ci
694462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) {
694562306a36Sopenharmony_ci		if (hweight8(mask->control[band].ht_mcs[i]) == 1) {
694662306a36Sopenharmony_ci			*nss = i + 1;
694762306a36Sopenharmony_ci			*rate = WMI_RATE_PREAMBLE_HT << 6 |
694862306a36Sopenharmony_ci				(*nss - 1) << 4 |
694962306a36Sopenharmony_ci				(ffs(mask->control[band].ht_mcs[i]) - 1);
695062306a36Sopenharmony_ci
695162306a36Sopenharmony_ci			return 0;
695262306a36Sopenharmony_ci		}
695362306a36Sopenharmony_ci	}
695462306a36Sopenharmony_ci
695562306a36Sopenharmony_cinext:
695662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) {
695762306a36Sopenharmony_ci		if (hweight16(mask->control[band].vht_mcs[i]) == 1) {
695862306a36Sopenharmony_ci			*nss = i + 1;
695962306a36Sopenharmony_ci			*rate = WMI_RATE_PREAMBLE_VHT << 6 |
696062306a36Sopenharmony_ci				(*nss - 1) << 4 |
696162306a36Sopenharmony_ci				(ffs(mask->control[band].vht_mcs[i]) - 1);
696262306a36Sopenharmony_ci
696362306a36Sopenharmony_ci			return 0;
696462306a36Sopenharmony_ci		}
696562306a36Sopenharmony_ci	}
696662306a36Sopenharmony_ci
696762306a36Sopenharmony_ci	return -EINVAL;
696862306a36Sopenharmony_ci}
696962306a36Sopenharmony_ci
697062306a36Sopenharmony_cistatic int ath10k_mac_validate_rate_mask(struct ath10k *ar,
697162306a36Sopenharmony_ci					 struct ieee80211_sta *sta,
697262306a36Sopenharmony_ci					 u32 rate_ctrl_flag, u8 nss)
697362306a36Sopenharmony_ci{
697462306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
697562306a36Sopenharmony_ci	struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
697662306a36Sopenharmony_ci
697762306a36Sopenharmony_ci	if (nss > sta->deflink.rx_nss) {
697862306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid nss field, configured %u limit %u\n",
697962306a36Sopenharmony_ci			    nss, sta->deflink.rx_nss);
698062306a36Sopenharmony_ci		return -EINVAL;
698162306a36Sopenharmony_ci	}
698262306a36Sopenharmony_ci
698362306a36Sopenharmony_ci	if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_VHT) {
698462306a36Sopenharmony_ci		if (!vht_cap->vht_supported) {
698562306a36Sopenharmony_ci			ath10k_warn(ar, "Invalid VHT rate for sta %pM\n",
698662306a36Sopenharmony_ci				    sta->addr);
698762306a36Sopenharmony_ci			return -EINVAL;
698862306a36Sopenharmony_ci		}
698962306a36Sopenharmony_ci	} else if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HT) {
699062306a36Sopenharmony_ci		if (!ht_cap->ht_supported || vht_cap->vht_supported) {
699162306a36Sopenharmony_ci			ath10k_warn(ar, "Invalid HT rate for sta %pM\n",
699262306a36Sopenharmony_ci				    sta->addr);
699362306a36Sopenharmony_ci			return -EINVAL;
699462306a36Sopenharmony_ci		}
699562306a36Sopenharmony_ci	} else {
699662306a36Sopenharmony_ci		if (ht_cap->ht_supported || vht_cap->vht_supported)
699762306a36Sopenharmony_ci			return -EINVAL;
699862306a36Sopenharmony_ci	}
699962306a36Sopenharmony_ci
700062306a36Sopenharmony_ci	return 0;
700162306a36Sopenharmony_ci}
700262306a36Sopenharmony_ci
700362306a36Sopenharmony_cistatic int
700462306a36Sopenharmony_ciath10k_mac_tid_bitrate_config(struct ath10k *ar,
700562306a36Sopenharmony_ci			      struct ieee80211_vif *vif,
700662306a36Sopenharmony_ci			      struct ieee80211_sta *sta,
700762306a36Sopenharmony_ci			      u32 *rate_ctrl_flag, u8 *rate_ctrl,
700862306a36Sopenharmony_ci			      enum nl80211_tx_rate_setting txrate_type,
700962306a36Sopenharmony_ci			      const struct cfg80211_bitrate_mask *mask)
701062306a36Sopenharmony_ci{
701162306a36Sopenharmony_ci	struct cfg80211_chan_def def;
701262306a36Sopenharmony_ci	enum nl80211_band band;
701362306a36Sopenharmony_ci	u8 nss, rate;
701462306a36Sopenharmony_ci	int vht_num_rates, ret;
701562306a36Sopenharmony_ci
701662306a36Sopenharmony_ci	if (WARN_ON(ath10k_mac_vif_chan(vif, &def)))
701762306a36Sopenharmony_ci		return -EINVAL;
701862306a36Sopenharmony_ci
701962306a36Sopenharmony_ci	if (txrate_type == NL80211_TX_RATE_AUTOMATIC) {
702062306a36Sopenharmony_ci		*rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_AUTO;
702162306a36Sopenharmony_ci		*rate_ctrl_flag = 0;
702262306a36Sopenharmony_ci		return 0;
702362306a36Sopenharmony_ci	}
702462306a36Sopenharmony_ci
702562306a36Sopenharmony_ci	band = def.chan->band;
702662306a36Sopenharmony_ci
702762306a36Sopenharmony_ci	if (!ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask,
702862306a36Sopenharmony_ci						     &vht_num_rates)) {
702962306a36Sopenharmony_ci		return -EINVAL;
703062306a36Sopenharmony_ci	}
703162306a36Sopenharmony_ci
703262306a36Sopenharmony_ci	ret = ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
703362306a36Sopenharmony_ci						      &rate, &nss, false);
703462306a36Sopenharmony_ci	if (ret) {
703562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to get single rate: %d\n",
703662306a36Sopenharmony_ci			    ret);
703762306a36Sopenharmony_ci		return ret;
703862306a36Sopenharmony_ci	}
703962306a36Sopenharmony_ci
704062306a36Sopenharmony_ci	*rate_ctrl_flag = rate;
704162306a36Sopenharmony_ci
704262306a36Sopenharmony_ci	if (sta && ath10k_mac_validate_rate_mask(ar, sta, *rate_ctrl_flag, nss))
704362306a36Sopenharmony_ci		return -EINVAL;
704462306a36Sopenharmony_ci
704562306a36Sopenharmony_ci	if (txrate_type == NL80211_TX_RATE_FIXED)
704662306a36Sopenharmony_ci		*rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_FIXED_RATE;
704762306a36Sopenharmony_ci	else if (txrate_type == NL80211_TX_RATE_LIMITED &&
704862306a36Sopenharmony_ci		 (test_bit(WMI_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
704962306a36Sopenharmony_ci			   ar->wmi.svc_map)))
705062306a36Sopenharmony_ci		*rate_ctrl = WMI_PEER_TID_CONFIG_RATE_UPPER_CAP;
705162306a36Sopenharmony_ci	else
705262306a36Sopenharmony_ci		return -EOPNOTSUPP;
705362306a36Sopenharmony_ci
705462306a36Sopenharmony_ci	return 0;
705562306a36Sopenharmony_ci}
705662306a36Sopenharmony_ci
705762306a36Sopenharmony_cistatic int ath10k_mac_set_tid_config(struct ath10k *ar, struct ieee80211_sta *sta,
705862306a36Sopenharmony_ci				     struct ieee80211_vif *vif, u32 changed,
705962306a36Sopenharmony_ci				     struct wmi_per_peer_per_tid_cfg_arg *arg)
706062306a36Sopenharmony_ci{
706162306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
706262306a36Sopenharmony_ci	struct ath10k_sta *arsta;
706362306a36Sopenharmony_ci	int ret;
706462306a36Sopenharmony_ci
706562306a36Sopenharmony_ci	if (sta) {
706662306a36Sopenharmony_ci		if (!sta->wme)
706762306a36Sopenharmony_ci			return -ENOTSUPP;
706862306a36Sopenharmony_ci
706962306a36Sopenharmony_ci		arsta = (struct ath10k_sta *)sta->drv_priv;
707062306a36Sopenharmony_ci
707162306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
707262306a36Sopenharmony_ci			if ((arsta->retry_long[arg->tid] > 0 ||
707362306a36Sopenharmony_ci			     arsta->rate_code[arg->tid] > 0 ||
707462306a36Sopenharmony_ci			     arsta->ampdu[arg->tid] ==
707562306a36Sopenharmony_ci					WMI_TID_CONFIG_AGGR_CONTROL_ENABLE) &&
707662306a36Sopenharmony_ci			     arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
707762306a36Sopenharmony_ci				changed &= ~BIT(NL80211_TID_CONFIG_ATTR_NOACK);
707862306a36Sopenharmony_ci				arg->ack_policy = 0;
707962306a36Sopenharmony_ci				arg->aggr_control = 0;
708062306a36Sopenharmony_ci				arg->rate_ctrl = 0;
708162306a36Sopenharmony_ci				arg->rcode_flags = 0;
708262306a36Sopenharmony_ci			}
708362306a36Sopenharmony_ci		}
708462306a36Sopenharmony_ci
708562306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
708662306a36Sopenharmony_ci			if (arsta->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK ||
708762306a36Sopenharmony_ci			    arvif->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK) {
708862306a36Sopenharmony_ci				arg->aggr_control = 0;
708962306a36Sopenharmony_ci				changed &= ~BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG);
709062306a36Sopenharmony_ci			}
709162306a36Sopenharmony_ci		}
709262306a36Sopenharmony_ci
709362306a36Sopenharmony_ci		if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
709462306a36Sopenharmony_ci		    BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
709562306a36Sopenharmony_ci			if (arsta->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK ||
709662306a36Sopenharmony_ci			    arvif->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK) {
709762306a36Sopenharmony_ci				arg->rate_ctrl = 0;
709862306a36Sopenharmony_ci				arg->rcode_flags = 0;
709962306a36Sopenharmony_ci			}
710062306a36Sopenharmony_ci		}
710162306a36Sopenharmony_ci
710262306a36Sopenharmony_ci		ether_addr_copy(arg->peer_macaddr.addr, sta->addr);
710362306a36Sopenharmony_ci
710462306a36Sopenharmony_ci		ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, arg);
710562306a36Sopenharmony_ci		if (ret)
710662306a36Sopenharmony_ci			return ret;
710762306a36Sopenharmony_ci
710862306a36Sopenharmony_ci		/* Store the configured parameters in success case */
710962306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
711062306a36Sopenharmony_ci			arsta->noack[arg->tid] = arg->ack_policy;
711162306a36Sopenharmony_ci			arg->ack_policy = 0;
711262306a36Sopenharmony_ci			arg->aggr_control = 0;
711362306a36Sopenharmony_ci			arg->rate_ctrl = 0;
711462306a36Sopenharmony_ci			arg->rcode_flags = 0;
711562306a36Sopenharmony_ci		}
711662306a36Sopenharmony_ci
711762306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG)) {
711862306a36Sopenharmony_ci			arsta->retry_long[arg->tid] = arg->retry_count;
711962306a36Sopenharmony_ci			arg->retry_count = 0;
712062306a36Sopenharmony_ci		}
712162306a36Sopenharmony_ci
712262306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
712362306a36Sopenharmony_ci			arsta->ampdu[arg->tid] = arg->aggr_control;
712462306a36Sopenharmony_ci			arg->aggr_control = 0;
712562306a36Sopenharmony_ci		}
712662306a36Sopenharmony_ci
712762306a36Sopenharmony_ci		if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
712862306a36Sopenharmony_ci		    BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
712962306a36Sopenharmony_ci			arsta->rate_ctrl[arg->tid] = arg->rate_ctrl;
713062306a36Sopenharmony_ci			arg->rate_ctrl = 0;
713162306a36Sopenharmony_ci			arg->rcode_flags = 0;
713262306a36Sopenharmony_ci		}
713362306a36Sopenharmony_ci
713462306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
713562306a36Sopenharmony_ci			arsta->rtscts[arg->tid] = arg->rtscts_ctrl;
713662306a36Sopenharmony_ci			arg->ext_tid_cfg_bitmap = 0;
713762306a36Sopenharmony_ci		}
713862306a36Sopenharmony_ci	} else {
713962306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
714062306a36Sopenharmony_ci			if ((arvif->retry_long[arg->tid] ||
714162306a36Sopenharmony_ci			     arvif->rate_code[arg->tid] ||
714262306a36Sopenharmony_ci			     arvif->ampdu[arg->tid] ==
714362306a36Sopenharmony_ci					WMI_TID_CONFIG_AGGR_CONTROL_ENABLE) &&
714462306a36Sopenharmony_ci			     arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
714562306a36Sopenharmony_ci				changed &= ~BIT(NL80211_TID_CONFIG_ATTR_NOACK);
714662306a36Sopenharmony_ci			} else {
714762306a36Sopenharmony_ci				arvif->noack[arg->tid] = arg->ack_policy;
714862306a36Sopenharmony_ci				arvif->ampdu[arg->tid] = arg->aggr_control;
714962306a36Sopenharmony_ci				arvif->rate_ctrl[arg->tid] = arg->rate_ctrl;
715062306a36Sopenharmony_ci			}
715162306a36Sopenharmony_ci		}
715262306a36Sopenharmony_ci
715362306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG)) {
715462306a36Sopenharmony_ci			if (arvif->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK)
715562306a36Sopenharmony_ci				changed &= ~BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG);
715662306a36Sopenharmony_ci			else
715762306a36Sopenharmony_ci				arvif->retry_long[arg->tid] = arg->retry_count;
715862306a36Sopenharmony_ci		}
715962306a36Sopenharmony_ci
716062306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
716162306a36Sopenharmony_ci			if (arvif->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK)
716262306a36Sopenharmony_ci				changed &= ~BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL);
716362306a36Sopenharmony_ci			else
716462306a36Sopenharmony_ci				arvif->ampdu[arg->tid] = arg->aggr_control;
716562306a36Sopenharmony_ci		}
716662306a36Sopenharmony_ci
716762306a36Sopenharmony_ci		if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
716862306a36Sopenharmony_ci		    BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
716962306a36Sopenharmony_ci			if (arvif->noack[arg->tid] == WMI_PEER_TID_CONFIG_NOACK) {
717062306a36Sopenharmony_ci				changed &= ~(BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
717162306a36Sopenharmony_ci					     BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE));
717262306a36Sopenharmony_ci			} else {
717362306a36Sopenharmony_ci				arvif->rate_ctrl[arg->tid] = arg->rate_ctrl;
717462306a36Sopenharmony_ci				arvif->rate_code[arg->tid] = arg->rcode_flags;
717562306a36Sopenharmony_ci			}
717662306a36Sopenharmony_ci		}
717762306a36Sopenharmony_ci
717862306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
717962306a36Sopenharmony_ci			arvif->rtscts[arg->tid] = arg->rtscts_ctrl;
718062306a36Sopenharmony_ci			arg->ext_tid_cfg_bitmap = 0;
718162306a36Sopenharmony_ci		}
718262306a36Sopenharmony_ci
718362306a36Sopenharmony_ci		if (changed)
718462306a36Sopenharmony_ci			arvif->tid_conf_changed[arg->tid] |= changed;
718562306a36Sopenharmony_ci	}
718662306a36Sopenharmony_ci
718762306a36Sopenharmony_ci	return 0;
718862306a36Sopenharmony_ci}
718962306a36Sopenharmony_ci
719062306a36Sopenharmony_cistatic int
719162306a36Sopenharmony_ciath10k_mac_parse_tid_config(struct ath10k *ar,
719262306a36Sopenharmony_ci			    struct ieee80211_sta *sta,
719362306a36Sopenharmony_ci			    struct ieee80211_vif *vif,
719462306a36Sopenharmony_ci			    struct cfg80211_tid_cfg *tid_conf,
719562306a36Sopenharmony_ci			    struct wmi_per_peer_per_tid_cfg_arg *arg)
719662306a36Sopenharmony_ci{
719762306a36Sopenharmony_ci	u32 changed = tid_conf->mask;
719862306a36Sopenharmony_ci	int ret = 0, i = 0;
719962306a36Sopenharmony_ci
720062306a36Sopenharmony_ci	if (!changed)
720162306a36Sopenharmony_ci		return -EINVAL;
720262306a36Sopenharmony_ci
720362306a36Sopenharmony_ci	while (i < ATH10K_TID_MAX) {
720462306a36Sopenharmony_ci		if (!(tid_conf->tids & BIT(i))) {
720562306a36Sopenharmony_ci			i++;
720662306a36Sopenharmony_ci			continue;
720762306a36Sopenharmony_ci		}
720862306a36Sopenharmony_ci
720962306a36Sopenharmony_ci		arg->tid = i;
721062306a36Sopenharmony_ci
721162306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
721262306a36Sopenharmony_ci			if (tid_conf->noack == NL80211_TID_CONFIG_ENABLE) {
721362306a36Sopenharmony_ci				arg->ack_policy = WMI_PEER_TID_CONFIG_NOACK;
721462306a36Sopenharmony_ci				arg->rate_ctrl =
721562306a36Sopenharmony_ci				WMI_TID_CONFIG_RATE_CONTROL_DEFAULT_LOWEST_RATE;
721662306a36Sopenharmony_ci				arg->aggr_control =
721762306a36Sopenharmony_ci					WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
721862306a36Sopenharmony_ci			} else {
721962306a36Sopenharmony_ci				arg->ack_policy =
722062306a36Sopenharmony_ci					WMI_PEER_TID_CONFIG_ACK;
722162306a36Sopenharmony_ci				arg->rate_ctrl =
722262306a36Sopenharmony_ci					WMI_TID_CONFIG_RATE_CONTROL_AUTO;
722362306a36Sopenharmony_ci				arg->aggr_control =
722462306a36Sopenharmony_ci					WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
722562306a36Sopenharmony_ci			}
722662306a36Sopenharmony_ci		}
722762306a36Sopenharmony_ci
722862306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG))
722962306a36Sopenharmony_ci			arg->retry_count = tid_conf->retry_long;
723062306a36Sopenharmony_ci
723162306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
723262306a36Sopenharmony_ci			if (tid_conf->noack == NL80211_TID_CONFIG_ENABLE)
723362306a36Sopenharmony_ci				arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
723462306a36Sopenharmony_ci			else
723562306a36Sopenharmony_ci				arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
723662306a36Sopenharmony_ci		}
723762306a36Sopenharmony_ci
723862306a36Sopenharmony_ci		if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
723962306a36Sopenharmony_ci		    BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
724062306a36Sopenharmony_ci			ret = ath10k_mac_tid_bitrate_config(ar, vif, sta,
724162306a36Sopenharmony_ci							    &arg->rcode_flags,
724262306a36Sopenharmony_ci							    &arg->rate_ctrl,
724362306a36Sopenharmony_ci							    tid_conf->txrate_type,
724462306a36Sopenharmony_ci							&tid_conf->txrate_mask);
724562306a36Sopenharmony_ci			if (ret) {
724662306a36Sopenharmony_ci				ath10k_warn(ar, "failed to configure bitrate mask %d\n",
724762306a36Sopenharmony_ci					    ret);
724862306a36Sopenharmony_ci				arg->rcode_flags = 0;
724962306a36Sopenharmony_ci				arg->rate_ctrl = 0;
725062306a36Sopenharmony_ci			}
725162306a36Sopenharmony_ci		}
725262306a36Sopenharmony_ci
725362306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
725462306a36Sopenharmony_ci			if (tid_conf->rtscts)
725562306a36Sopenharmony_ci				arg->rtscts_ctrl = tid_conf->rtscts;
725662306a36Sopenharmony_ci
725762306a36Sopenharmony_ci			arg->ext_tid_cfg_bitmap = WMI_EXT_TID_RTS_CTS_CONFIG;
725862306a36Sopenharmony_ci		}
725962306a36Sopenharmony_ci
726062306a36Sopenharmony_ci		ret = ath10k_mac_set_tid_config(ar, sta, vif, changed, arg);
726162306a36Sopenharmony_ci		if (ret)
726262306a36Sopenharmony_ci			return ret;
726362306a36Sopenharmony_ci		i++;
726462306a36Sopenharmony_ci	}
726562306a36Sopenharmony_ci
726662306a36Sopenharmony_ci	return ret;
726762306a36Sopenharmony_ci}
726862306a36Sopenharmony_ci
726962306a36Sopenharmony_cistatic int ath10k_mac_reset_tid_config(struct ath10k *ar,
727062306a36Sopenharmony_ci				       struct ieee80211_sta *sta,
727162306a36Sopenharmony_ci				       struct ath10k_vif *arvif,
727262306a36Sopenharmony_ci				       u8 tids)
727362306a36Sopenharmony_ci{
727462306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
727562306a36Sopenharmony_ci	struct wmi_per_peer_per_tid_cfg_arg arg;
727662306a36Sopenharmony_ci	int ret = 0, i = 0;
727762306a36Sopenharmony_ci
727862306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
727962306a36Sopenharmony_ci	while (i < ATH10K_TID_MAX) {
728062306a36Sopenharmony_ci		if (!(tids & BIT(i))) {
728162306a36Sopenharmony_ci			i++;
728262306a36Sopenharmony_ci			continue;
728362306a36Sopenharmony_ci		}
728462306a36Sopenharmony_ci
728562306a36Sopenharmony_ci		arg.tid = i;
728662306a36Sopenharmony_ci		arg.ack_policy = WMI_PEER_TID_CONFIG_ACK;
728762306a36Sopenharmony_ci		arg.retry_count = ATH10K_MAX_RETRY_COUNT;
728862306a36Sopenharmony_ci		arg.rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_AUTO;
728962306a36Sopenharmony_ci		arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
729062306a36Sopenharmony_ci		arg.rtscts_ctrl = WMI_TID_CONFIG_RTSCTS_CONTROL_ENABLE;
729162306a36Sopenharmony_ci		arg.ext_tid_cfg_bitmap = WMI_EXT_TID_RTS_CTS_CONFIG;
729262306a36Sopenharmony_ci
729362306a36Sopenharmony_ci		ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
729462306a36Sopenharmony_ci
729562306a36Sopenharmony_ci		ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
729662306a36Sopenharmony_ci		if (ret)
729762306a36Sopenharmony_ci			return ret;
729862306a36Sopenharmony_ci
729962306a36Sopenharmony_ci		if (!arvif->tids_rst) {
730062306a36Sopenharmony_ci			arsta->retry_long[i] = -1;
730162306a36Sopenharmony_ci			arsta->noack[i] = -1;
730262306a36Sopenharmony_ci			arsta->ampdu[i] = -1;
730362306a36Sopenharmony_ci			arsta->rate_code[i] = -1;
730462306a36Sopenharmony_ci			arsta->rate_ctrl[i] = 0;
730562306a36Sopenharmony_ci			arsta->rtscts[i] = -1;
730662306a36Sopenharmony_ci		} else {
730762306a36Sopenharmony_ci			arvif->retry_long[i] = 0;
730862306a36Sopenharmony_ci			arvif->noack[i] = 0;
730962306a36Sopenharmony_ci			arvif->ampdu[i] = 0;
731062306a36Sopenharmony_ci			arvif->rate_code[i] = 0;
731162306a36Sopenharmony_ci			arvif->rate_ctrl[i] = 0;
731262306a36Sopenharmony_ci			arvif->rtscts[i] = 0;
731362306a36Sopenharmony_ci		}
731462306a36Sopenharmony_ci
731562306a36Sopenharmony_ci		i++;
731662306a36Sopenharmony_ci	}
731762306a36Sopenharmony_ci
731862306a36Sopenharmony_ci	return ret;
731962306a36Sopenharmony_ci}
732062306a36Sopenharmony_ci
732162306a36Sopenharmony_cistatic void ath10k_sta_tid_cfg_wk(struct work_struct *wk)
732262306a36Sopenharmony_ci{
732362306a36Sopenharmony_ci	struct wmi_per_peer_per_tid_cfg_arg arg = {};
732462306a36Sopenharmony_ci	struct ieee80211_sta *sta;
732562306a36Sopenharmony_ci	struct ath10k_sta *arsta;
732662306a36Sopenharmony_ci	struct ath10k_vif *arvif;
732762306a36Sopenharmony_ci	struct ath10k *ar;
732862306a36Sopenharmony_ci	bool config_apply;
732962306a36Sopenharmony_ci	int ret, i;
733062306a36Sopenharmony_ci	u32 changed;
733162306a36Sopenharmony_ci	u8 nss;
733262306a36Sopenharmony_ci
733362306a36Sopenharmony_ci	arsta = container_of(wk, struct ath10k_sta, tid_config_wk);
733462306a36Sopenharmony_ci	sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
733562306a36Sopenharmony_ci	arvif = arsta->arvif;
733662306a36Sopenharmony_ci	ar = arvif->ar;
733762306a36Sopenharmony_ci
733862306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
733962306a36Sopenharmony_ci
734062306a36Sopenharmony_ci	if (arvif->tids_rst) {
734162306a36Sopenharmony_ci		ret = ath10k_mac_reset_tid_config(ar, sta, arvif,
734262306a36Sopenharmony_ci						  arvif->tids_rst);
734362306a36Sopenharmony_ci		goto exit;
734462306a36Sopenharmony_ci	}
734562306a36Sopenharmony_ci
734662306a36Sopenharmony_ci	ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
734762306a36Sopenharmony_ci
734862306a36Sopenharmony_ci	for (i = 0; i < ATH10K_TID_MAX; i++) {
734962306a36Sopenharmony_ci		config_apply = false;
735062306a36Sopenharmony_ci		changed = arvif->tid_conf_changed[i];
735162306a36Sopenharmony_ci
735262306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
735362306a36Sopenharmony_ci			if (arsta->noack[i] != -1) {
735462306a36Sopenharmony_ci				arg.ack_policy  = 0;
735562306a36Sopenharmony_ci			} else {
735662306a36Sopenharmony_ci				config_apply = true;
735762306a36Sopenharmony_ci				arg.ack_policy = arvif->noack[i];
735862306a36Sopenharmony_ci				arg.aggr_control = arvif->ampdu[i];
735962306a36Sopenharmony_ci				arg.rate_ctrl = arvif->rate_ctrl[i];
736062306a36Sopenharmony_ci			}
736162306a36Sopenharmony_ci		}
736262306a36Sopenharmony_ci
736362306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG)) {
736462306a36Sopenharmony_ci			if (arsta->retry_long[i] != -1 ||
736562306a36Sopenharmony_ci			    arsta->noack[i] == WMI_PEER_TID_CONFIG_NOACK ||
736662306a36Sopenharmony_ci			    arvif->noack[i] == WMI_PEER_TID_CONFIG_NOACK) {
736762306a36Sopenharmony_ci				arg.retry_count = 0;
736862306a36Sopenharmony_ci			} else {
736962306a36Sopenharmony_ci				arg.retry_count = arvif->retry_long[i];
737062306a36Sopenharmony_ci				config_apply = true;
737162306a36Sopenharmony_ci			}
737262306a36Sopenharmony_ci		}
737362306a36Sopenharmony_ci
737462306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
737562306a36Sopenharmony_ci			if (arsta->ampdu[i] != -1 ||
737662306a36Sopenharmony_ci			    arsta->noack[i] == WMI_PEER_TID_CONFIG_NOACK ||
737762306a36Sopenharmony_ci			    arvif->noack[i] == WMI_PEER_TID_CONFIG_NOACK) {
737862306a36Sopenharmony_ci				arg.aggr_control = 0;
737962306a36Sopenharmony_ci			} else {
738062306a36Sopenharmony_ci				arg.aggr_control = arvif->ampdu[i];
738162306a36Sopenharmony_ci				config_apply = true;
738262306a36Sopenharmony_ci			}
738362306a36Sopenharmony_ci		}
738462306a36Sopenharmony_ci
738562306a36Sopenharmony_ci		if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
738662306a36Sopenharmony_ci		    BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
738762306a36Sopenharmony_ci			nss = ATH10K_HW_NSS(arvif->rate_code[i]);
738862306a36Sopenharmony_ci			ret = ath10k_mac_validate_rate_mask(ar, sta,
738962306a36Sopenharmony_ci							    arvif->rate_code[i],
739062306a36Sopenharmony_ci							    nss);
739162306a36Sopenharmony_ci			if (ret &&
739262306a36Sopenharmony_ci			    arvif->rate_ctrl[i] > WMI_TID_CONFIG_RATE_CONTROL_AUTO) {
739362306a36Sopenharmony_ci				arg.rate_ctrl = 0;
739462306a36Sopenharmony_ci				arg.rcode_flags = 0;
739562306a36Sopenharmony_ci			}
739662306a36Sopenharmony_ci
739762306a36Sopenharmony_ci			if (arsta->rate_ctrl[i] >
739862306a36Sopenharmony_ci			    WMI_TID_CONFIG_RATE_CONTROL_AUTO ||
739962306a36Sopenharmony_ci			    arsta->noack[i] == WMI_PEER_TID_CONFIG_NOACK ||
740062306a36Sopenharmony_ci			    arvif->noack[i] == WMI_PEER_TID_CONFIG_NOACK) {
740162306a36Sopenharmony_ci				arg.rate_ctrl = 0;
740262306a36Sopenharmony_ci				arg.rcode_flags = 0;
740362306a36Sopenharmony_ci			} else {
740462306a36Sopenharmony_ci				arg.rate_ctrl = arvif->rate_ctrl[i];
740562306a36Sopenharmony_ci				arg.rcode_flags = arvif->rate_code[i];
740662306a36Sopenharmony_ci				config_apply = true;
740762306a36Sopenharmony_ci			}
740862306a36Sopenharmony_ci		}
740962306a36Sopenharmony_ci
741062306a36Sopenharmony_ci		if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
741162306a36Sopenharmony_ci			if (arsta->rtscts[i]) {
741262306a36Sopenharmony_ci				arg.rtscts_ctrl = 0;
741362306a36Sopenharmony_ci				arg.ext_tid_cfg_bitmap = 0;
741462306a36Sopenharmony_ci			} else {
741562306a36Sopenharmony_ci				arg.rtscts_ctrl = arvif->rtscts[i] - 1;
741662306a36Sopenharmony_ci				arg.ext_tid_cfg_bitmap =
741762306a36Sopenharmony_ci					WMI_EXT_TID_RTS_CTS_CONFIG;
741862306a36Sopenharmony_ci				config_apply = true;
741962306a36Sopenharmony_ci			}
742062306a36Sopenharmony_ci		}
742162306a36Sopenharmony_ci
742262306a36Sopenharmony_ci		arg.tid = i;
742362306a36Sopenharmony_ci
742462306a36Sopenharmony_ci		if (config_apply) {
742562306a36Sopenharmony_ci			ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
742662306a36Sopenharmony_ci			if (ret)
742762306a36Sopenharmony_ci				ath10k_warn(ar, "failed to set per tid config for sta %pM: %d\n",
742862306a36Sopenharmony_ci					    sta->addr, ret);
742962306a36Sopenharmony_ci		}
743062306a36Sopenharmony_ci
743162306a36Sopenharmony_ci		arg.ack_policy  = 0;
743262306a36Sopenharmony_ci		arg.retry_count  = 0;
743362306a36Sopenharmony_ci		arg.aggr_control  = 0;
743462306a36Sopenharmony_ci		arg.rate_ctrl = 0;
743562306a36Sopenharmony_ci		arg.rcode_flags = 0;
743662306a36Sopenharmony_ci	}
743762306a36Sopenharmony_ci
743862306a36Sopenharmony_ciexit:
743962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
744062306a36Sopenharmony_ci}
744162306a36Sopenharmony_ci
744262306a36Sopenharmony_cistatic void ath10k_mac_vif_stations_tid_conf(void *data,
744362306a36Sopenharmony_ci					     struct ieee80211_sta *sta)
744462306a36Sopenharmony_ci{
744562306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
744662306a36Sopenharmony_ci	struct ath10k_mac_iter_tid_conf_data *iter_data = data;
744762306a36Sopenharmony_ci	struct ieee80211_vif *sta_vif = arsta->arvif->vif;
744862306a36Sopenharmony_ci
744962306a36Sopenharmony_ci	if (sta_vif != iter_data->curr_vif || !sta->wme)
745062306a36Sopenharmony_ci		return;
745162306a36Sopenharmony_ci
745262306a36Sopenharmony_ci	ieee80211_queue_work(iter_data->ar->hw, &arsta->tid_config_wk);
745362306a36Sopenharmony_ci}
745462306a36Sopenharmony_ci
745562306a36Sopenharmony_cistatic int ath10k_sta_state(struct ieee80211_hw *hw,
745662306a36Sopenharmony_ci			    struct ieee80211_vif *vif,
745762306a36Sopenharmony_ci			    struct ieee80211_sta *sta,
745862306a36Sopenharmony_ci			    enum ieee80211_sta_state old_state,
745962306a36Sopenharmony_ci			    enum ieee80211_sta_state new_state)
746062306a36Sopenharmony_ci{
746162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
746262306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
746362306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
746462306a36Sopenharmony_ci	struct ath10k_peer *peer;
746562306a36Sopenharmony_ci	int ret = 0;
746662306a36Sopenharmony_ci	int i;
746762306a36Sopenharmony_ci
746862306a36Sopenharmony_ci	if (old_state == IEEE80211_STA_NOTEXIST &&
746962306a36Sopenharmony_ci	    new_state == IEEE80211_STA_NONE) {
747062306a36Sopenharmony_ci		memset(arsta, 0, sizeof(*arsta));
747162306a36Sopenharmony_ci		arsta->arvif = arvif;
747262306a36Sopenharmony_ci		arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
747362306a36Sopenharmony_ci		INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
747462306a36Sopenharmony_ci		INIT_WORK(&arsta->tid_config_wk, ath10k_sta_tid_cfg_wk);
747562306a36Sopenharmony_ci
747662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
747762306a36Sopenharmony_ci			ath10k_mac_txq_init(sta->txq[i]);
747862306a36Sopenharmony_ci	}
747962306a36Sopenharmony_ci
748062306a36Sopenharmony_ci	/* cancel must be done outside the mutex to avoid deadlock */
748162306a36Sopenharmony_ci	if ((old_state == IEEE80211_STA_NONE &&
748262306a36Sopenharmony_ci	     new_state == IEEE80211_STA_NOTEXIST)) {
748362306a36Sopenharmony_ci		cancel_work_sync(&arsta->update_wk);
748462306a36Sopenharmony_ci		cancel_work_sync(&arsta->tid_config_wk);
748562306a36Sopenharmony_ci	}
748662306a36Sopenharmony_ci
748762306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
748862306a36Sopenharmony_ci
748962306a36Sopenharmony_ci	if (old_state == IEEE80211_STA_NOTEXIST &&
749062306a36Sopenharmony_ci	    new_state == IEEE80211_STA_NONE) {
749162306a36Sopenharmony_ci		/*
749262306a36Sopenharmony_ci		 * New station addition.
749362306a36Sopenharmony_ci		 */
749462306a36Sopenharmony_ci		enum wmi_peer_type peer_type = WMI_PEER_TYPE_DEFAULT;
749562306a36Sopenharmony_ci		u32 num_tdls_stations;
749662306a36Sopenharmony_ci
749762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA,
749862306a36Sopenharmony_ci			   "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
749962306a36Sopenharmony_ci			   arvif->vdev_id, sta->addr,
750062306a36Sopenharmony_ci			   ar->num_stations + 1, ar->max_num_stations,
750162306a36Sopenharmony_ci			   ar->num_peers + 1, ar->max_num_peers);
750262306a36Sopenharmony_ci
750362306a36Sopenharmony_ci		num_tdls_stations = ath10k_mac_tdls_vif_stations_count(hw, vif);
750462306a36Sopenharmony_ci
750562306a36Sopenharmony_ci		if (sta->tdls) {
750662306a36Sopenharmony_ci			if (num_tdls_stations >= ar->max_num_tdls_vdevs) {
750762306a36Sopenharmony_ci				ath10k_warn(ar, "vdev %i exceeded maximum number of tdls vdevs %i\n",
750862306a36Sopenharmony_ci					    arvif->vdev_id,
750962306a36Sopenharmony_ci					    ar->max_num_tdls_vdevs);
751062306a36Sopenharmony_ci				ret = -ELNRNG;
751162306a36Sopenharmony_ci				goto exit;
751262306a36Sopenharmony_ci			}
751362306a36Sopenharmony_ci			peer_type = WMI_PEER_TYPE_TDLS;
751462306a36Sopenharmony_ci		}
751562306a36Sopenharmony_ci
751662306a36Sopenharmony_ci		ret = ath10k_mac_inc_num_stations(arvif, sta);
751762306a36Sopenharmony_ci		if (ret) {
751862306a36Sopenharmony_ci			ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
751962306a36Sopenharmony_ci				    ar->max_num_stations);
752062306a36Sopenharmony_ci			goto exit;
752162306a36Sopenharmony_ci		}
752262306a36Sopenharmony_ci
752362306a36Sopenharmony_ci		if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
752462306a36Sopenharmony_ci			arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats),
752562306a36Sopenharmony_ci						  GFP_KERNEL);
752662306a36Sopenharmony_ci			if (!arsta->tx_stats) {
752762306a36Sopenharmony_ci				ath10k_mac_dec_num_stations(arvif, sta);
752862306a36Sopenharmony_ci				ret = -ENOMEM;
752962306a36Sopenharmony_ci				goto exit;
753062306a36Sopenharmony_ci			}
753162306a36Sopenharmony_ci		}
753262306a36Sopenharmony_ci
753362306a36Sopenharmony_ci		ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
753462306a36Sopenharmony_ci					 sta->addr, peer_type);
753562306a36Sopenharmony_ci		if (ret) {
753662306a36Sopenharmony_ci			ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
753762306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
753862306a36Sopenharmony_ci			ath10k_mac_dec_num_stations(arvif, sta);
753962306a36Sopenharmony_ci			kfree(arsta->tx_stats);
754062306a36Sopenharmony_ci			goto exit;
754162306a36Sopenharmony_ci		}
754262306a36Sopenharmony_ci
754362306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
754462306a36Sopenharmony_ci
754562306a36Sopenharmony_ci		peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
754662306a36Sopenharmony_ci		if (!peer) {
754762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
754862306a36Sopenharmony_ci				    vif->addr, arvif->vdev_id);
754962306a36Sopenharmony_ci			spin_unlock_bh(&ar->data_lock);
755062306a36Sopenharmony_ci			ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
755162306a36Sopenharmony_ci			ath10k_mac_dec_num_stations(arvif, sta);
755262306a36Sopenharmony_ci			kfree(arsta->tx_stats);
755362306a36Sopenharmony_ci			ret = -ENOENT;
755462306a36Sopenharmony_ci			goto exit;
755562306a36Sopenharmony_ci		}
755662306a36Sopenharmony_ci
755762306a36Sopenharmony_ci		arsta->peer_id = find_first_bit(peer->peer_ids,
755862306a36Sopenharmony_ci						ATH10K_MAX_NUM_PEER_IDS);
755962306a36Sopenharmony_ci
756062306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
756162306a36Sopenharmony_ci
756262306a36Sopenharmony_ci		if (!sta->tdls)
756362306a36Sopenharmony_ci			goto exit;
756462306a36Sopenharmony_ci
756562306a36Sopenharmony_ci		ret = ath10k_wmi_update_fw_tdls_state(ar, arvif->vdev_id,
756662306a36Sopenharmony_ci						      WMI_TDLS_ENABLE_ACTIVE);
756762306a36Sopenharmony_ci		if (ret) {
756862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update fw tdls state on vdev %i: %i\n",
756962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
757062306a36Sopenharmony_ci			ath10k_peer_delete(ar, arvif->vdev_id,
757162306a36Sopenharmony_ci					   sta->addr);
757262306a36Sopenharmony_ci			ath10k_mac_dec_num_stations(arvif, sta);
757362306a36Sopenharmony_ci			kfree(arsta->tx_stats);
757462306a36Sopenharmony_ci			goto exit;
757562306a36Sopenharmony_ci		}
757662306a36Sopenharmony_ci
757762306a36Sopenharmony_ci		ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id, sta,
757862306a36Sopenharmony_ci						  WMI_TDLS_PEER_STATE_PEERING);
757962306a36Sopenharmony_ci		if (ret) {
758062306a36Sopenharmony_ci			ath10k_warn(ar,
758162306a36Sopenharmony_ci				    "failed to update tdls peer %pM for vdev %d when adding a new sta: %i\n",
758262306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
758362306a36Sopenharmony_ci			ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
758462306a36Sopenharmony_ci			ath10k_mac_dec_num_stations(arvif, sta);
758562306a36Sopenharmony_ci			kfree(arsta->tx_stats);
758662306a36Sopenharmony_ci
758762306a36Sopenharmony_ci			if (num_tdls_stations != 0)
758862306a36Sopenharmony_ci				goto exit;
758962306a36Sopenharmony_ci			ath10k_wmi_update_fw_tdls_state(ar, arvif->vdev_id,
759062306a36Sopenharmony_ci							WMI_TDLS_DISABLE);
759162306a36Sopenharmony_ci		}
759262306a36Sopenharmony_ci	} else if ((old_state == IEEE80211_STA_NONE &&
759362306a36Sopenharmony_ci		    new_state == IEEE80211_STA_NOTEXIST)) {
759462306a36Sopenharmony_ci		/*
759562306a36Sopenharmony_ci		 * Existing station deletion.
759662306a36Sopenharmony_ci		 */
759762306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA,
759862306a36Sopenharmony_ci			   "mac vdev %d peer delete %pM sta %pK (sta gone)\n",
759962306a36Sopenharmony_ci			   arvif->vdev_id, sta->addr, sta);
760062306a36Sopenharmony_ci
760162306a36Sopenharmony_ci		if (sta->tdls) {
760262306a36Sopenharmony_ci			ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
760362306a36Sopenharmony_ci							  sta,
760462306a36Sopenharmony_ci							  WMI_TDLS_PEER_STATE_TEARDOWN);
760562306a36Sopenharmony_ci			if (ret)
760662306a36Sopenharmony_ci				ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
760762306a36Sopenharmony_ci					    sta->addr,
760862306a36Sopenharmony_ci					    WMI_TDLS_PEER_STATE_TEARDOWN, ret);
760962306a36Sopenharmony_ci		}
761062306a36Sopenharmony_ci
761162306a36Sopenharmony_ci		ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
761262306a36Sopenharmony_ci		if (ret)
761362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
761462306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
761562306a36Sopenharmony_ci
761662306a36Sopenharmony_ci		ath10k_mac_dec_num_stations(arvif, sta);
761762306a36Sopenharmony_ci
761862306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
761962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
762062306a36Sopenharmony_ci			peer = ar->peer_map[i];
762162306a36Sopenharmony_ci			if (!peer)
762262306a36Sopenharmony_ci				continue;
762362306a36Sopenharmony_ci
762462306a36Sopenharmony_ci			if (peer->sta == sta) {
762562306a36Sopenharmony_ci				ath10k_warn(ar, "found sta peer %pM (ptr %pK id %d) entry on vdev %i after it was supposedly removed\n",
762662306a36Sopenharmony_ci					    sta->addr, peer, i, arvif->vdev_id);
762762306a36Sopenharmony_ci				peer->sta = NULL;
762862306a36Sopenharmony_ci
762962306a36Sopenharmony_ci				/* Clean up the peer object as well since we
763062306a36Sopenharmony_ci				 * must have failed to do this above.
763162306a36Sopenharmony_ci				 */
763262306a36Sopenharmony_ci				ath10k_peer_map_cleanup(ar, peer);
763362306a36Sopenharmony_ci			}
763462306a36Sopenharmony_ci		}
763562306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
763662306a36Sopenharmony_ci
763762306a36Sopenharmony_ci		if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
763862306a36Sopenharmony_ci			kfree(arsta->tx_stats);
763962306a36Sopenharmony_ci			arsta->tx_stats = NULL;
764062306a36Sopenharmony_ci		}
764162306a36Sopenharmony_ci
764262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
764362306a36Sopenharmony_ci			ath10k_mac_txq_unref(ar, sta->txq[i]);
764462306a36Sopenharmony_ci
764562306a36Sopenharmony_ci		if (!sta->tdls)
764662306a36Sopenharmony_ci			goto exit;
764762306a36Sopenharmony_ci
764862306a36Sopenharmony_ci		if (ath10k_mac_tdls_vif_stations_count(hw, vif))
764962306a36Sopenharmony_ci			goto exit;
765062306a36Sopenharmony_ci
765162306a36Sopenharmony_ci		/* This was the last tdls peer in current vif */
765262306a36Sopenharmony_ci		ret = ath10k_wmi_update_fw_tdls_state(ar, arvif->vdev_id,
765362306a36Sopenharmony_ci						      WMI_TDLS_DISABLE);
765462306a36Sopenharmony_ci		if (ret) {
765562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update fw tdls state on vdev %i: %i\n",
765662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
765762306a36Sopenharmony_ci		}
765862306a36Sopenharmony_ci	} else if (old_state == IEEE80211_STA_AUTH &&
765962306a36Sopenharmony_ci		   new_state == IEEE80211_STA_ASSOC &&
766062306a36Sopenharmony_ci		   (vif->type == NL80211_IFTYPE_AP ||
766162306a36Sopenharmony_ci		    vif->type == NL80211_IFTYPE_MESH_POINT ||
766262306a36Sopenharmony_ci		    vif->type == NL80211_IFTYPE_ADHOC)) {
766362306a36Sopenharmony_ci		/*
766462306a36Sopenharmony_ci		 * New association.
766562306a36Sopenharmony_ci		 */
766662306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac sta %pM associated\n",
766762306a36Sopenharmony_ci			   sta->addr);
766862306a36Sopenharmony_ci
766962306a36Sopenharmony_ci		ret = ath10k_station_assoc(ar, vif, sta, false);
767062306a36Sopenharmony_ci		if (ret)
767162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n",
767262306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
767362306a36Sopenharmony_ci	} else if (old_state == IEEE80211_STA_ASSOC &&
767462306a36Sopenharmony_ci		   new_state == IEEE80211_STA_AUTHORIZED &&
767562306a36Sopenharmony_ci		   sta->tdls) {
767662306a36Sopenharmony_ci		/*
767762306a36Sopenharmony_ci		 * Tdls station authorized.
767862306a36Sopenharmony_ci		 */
767962306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac tdls sta %pM authorized\n",
768062306a36Sopenharmony_ci			   sta->addr);
768162306a36Sopenharmony_ci
768262306a36Sopenharmony_ci		ret = ath10k_station_assoc(ar, vif, sta, false);
768362306a36Sopenharmony_ci		if (ret) {
768462306a36Sopenharmony_ci			ath10k_warn(ar, "failed to associate tdls station %pM for vdev %i: %i\n",
768562306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
768662306a36Sopenharmony_ci			goto exit;
768762306a36Sopenharmony_ci		}
768862306a36Sopenharmony_ci
768962306a36Sopenharmony_ci		ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id, sta,
769062306a36Sopenharmony_ci						  WMI_TDLS_PEER_STATE_CONNECTED);
769162306a36Sopenharmony_ci		if (ret)
769262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update tdls peer %pM for vdev %i: %i\n",
769362306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
769462306a36Sopenharmony_ci	} else if (old_state == IEEE80211_STA_ASSOC &&
769562306a36Sopenharmony_ci		    new_state == IEEE80211_STA_AUTH &&
769662306a36Sopenharmony_ci		    (vif->type == NL80211_IFTYPE_AP ||
769762306a36Sopenharmony_ci		     vif->type == NL80211_IFTYPE_MESH_POINT ||
769862306a36Sopenharmony_ci		     vif->type == NL80211_IFTYPE_ADHOC)) {
769962306a36Sopenharmony_ci		/*
770062306a36Sopenharmony_ci		 * Disassociation.
770162306a36Sopenharmony_ci		 */
770262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_STA, "mac sta %pM disassociated\n",
770362306a36Sopenharmony_ci			   sta->addr);
770462306a36Sopenharmony_ci
770562306a36Sopenharmony_ci		ret = ath10k_station_disassoc(ar, vif, sta);
770662306a36Sopenharmony_ci		if (ret)
770762306a36Sopenharmony_ci			ath10k_warn(ar, "failed to disassociate station: %pM vdev %i: %i\n",
770862306a36Sopenharmony_ci				    sta->addr, arvif->vdev_id, ret);
770962306a36Sopenharmony_ci	}
771062306a36Sopenharmony_ciexit:
771162306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
771262306a36Sopenharmony_ci	return ret;
771362306a36Sopenharmony_ci}
771462306a36Sopenharmony_ci
771562306a36Sopenharmony_cistatic int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
771662306a36Sopenharmony_ci				u16 ac, bool enable)
771762306a36Sopenharmony_ci{
771862306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
771962306a36Sopenharmony_ci	struct wmi_sta_uapsd_auto_trig_arg arg = {};
772062306a36Sopenharmony_ci	u32 prio = 0, acc = 0;
772162306a36Sopenharmony_ci	u32 value = 0;
772262306a36Sopenharmony_ci	int ret = 0;
772362306a36Sopenharmony_ci
772462306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
772562306a36Sopenharmony_ci
772662306a36Sopenharmony_ci	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
772762306a36Sopenharmony_ci		return 0;
772862306a36Sopenharmony_ci
772962306a36Sopenharmony_ci	switch (ac) {
773062306a36Sopenharmony_ci	case IEEE80211_AC_VO:
773162306a36Sopenharmony_ci		value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
773262306a36Sopenharmony_ci			WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
773362306a36Sopenharmony_ci		prio = 7;
773462306a36Sopenharmony_ci		acc = 3;
773562306a36Sopenharmony_ci		break;
773662306a36Sopenharmony_ci	case IEEE80211_AC_VI:
773762306a36Sopenharmony_ci		value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
773862306a36Sopenharmony_ci			WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
773962306a36Sopenharmony_ci		prio = 5;
774062306a36Sopenharmony_ci		acc = 2;
774162306a36Sopenharmony_ci		break;
774262306a36Sopenharmony_ci	case IEEE80211_AC_BE:
774362306a36Sopenharmony_ci		value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
774462306a36Sopenharmony_ci			WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
774562306a36Sopenharmony_ci		prio = 2;
774662306a36Sopenharmony_ci		acc = 1;
774762306a36Sopenharmony_ci		break;
774862306a36Sopenharmony_ci	case IEEE80211_AC_BK:
774962306a36Sopenharmony_ci		value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
775062306a36Sopenharmony_ci			WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
775162306a36Sopenharmony_ci		prio = 0;
775262306a36Sopenharmony_ci		acc = 0;
775362306a36Sopenharmony_ci		break;
775462306a36Sopenharmony_ci	}
775562306a36Sopenharmony_ci
775662306a36Sopenharmony_ci	if (enable)
775762306a36Sopenharmony_ci		arvif->u.sta.uapsd |= value;
775862306a36Sopenharmony_ci	else
775962306a36Sopenharmony_ci		arvif->u.sta.uapsd &= ~value;
776062306a36Sopenharmony_ci
776162306a36Sopenharmony_ci	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
776262306a36Sopenharmony_ci					  WMI_STA_PS_PARAM_UAPSD,
776362306a36Sopenharmony_ci					  arvif->u.sta.uapsd);
776462306a36Sopenharmony_ci	if (ret) {
776562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set uapsd params: %d\n", ret);
776662306a36Sopenharmony_ci		goto exit;
776762306a36Sopenharmony_ci	}
776862306a36Sopenharmony_ci
776962306a36Sopenharmony_ci	if (arvif->u.sta.uapsd)
777062306a36Sopenharmony_ci		value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD;
777162306a36Sopenharmony_ci	else
777262306a36Sopenharmony_ci		value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
777362306a36Sopenharmony_ci
777462306a36Sopenharmony_ci	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
777562306a36Sopenharmony_ci					  WMI_STA_PS_PARAM_RX_WAKE_POLICY,
777662306a36Sopenharmony_ci					  value);
777762306a36Sopenharmony_ci	if (ret)
777862306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set rx wake param: %d\n", ret);
777962306a36Sopenharmony_ci
778062306a36Sopenharmony_ci	ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
778162306a36Sopenharmony_ci	if (ret) {
778262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
778362306a36Sopenharmony_ci			    arvif->vdev_id, ret);
778462306a36Sopenharmony_ci		return ret;
778562306a36Sopenharmony_ci	}
778662306a36Sopenharmony_ci
778762306a36Sopenharmony_ci	ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
778862306a36Sopenharmony_ci	if (ret) {
778962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
779062306a36Sopenharmony_ci			    arvif->vdev_id, ret);
779162306a36Sopenharmony_ci		return ret;
779262306a36Sopenharmony_ci	}
779362306a36Sopenharmony_ci
779462306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
779562306a36Sopenharmony_ci	    test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) {
779662306a36Sopenharmony_ci		/* Only userspace can make an educated decision when to send
779762306a36Sopenharmony_ci		 * trigger frame. The following effectively disables u-UAPSD
779862306a36Sopenharmony_ci		 * autotrigger in firmware (which is enabled by default
779962306a36Sopenharmony_ci		 * provided the autotrigger service is available).
780062306a36Sopenharmony_ci		 */
780162306a36Sopenharmony_ci
780262306a36Sopenharmony_ci		arg.wmm_ac = acc;
780362306a36Sopenharmony_ci		arg.user_priority = prio;
780462306a36Sopenharmony_ci		arg.service_interval = 0;
780562306a36Sopenharmony_ci		arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
780662306a36Sopenharmony_ci		arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
780762306a36Sopenharmony_ci
780862306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
780962306a36Sopenharmony_ci						arvif->bssid, &arg, 1);
781062306a36Sopenharmony_ci		if (ret) {
781162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
781262306a36Sopenharmony_ci				    ret);
781362306a36Sopenharmony_ci			return ret;
781462306a36Sopenharmony_ci		}
781562306a36Sopenharmony_ci	}
781662306a36Sopenharmony_ci
781762306a36Sopenharmony_ciexit:
781862306a36Sopenharmony_ci	return ret;
781962306a36Sopenharmony_ci}
782062306a36Sopenharmony_ci
782162306a36Sopenharmony_cistatic int ath10k_conf_tx(struct ieee80211_hw *hw,
782262306a36Sopenharmony_ci			  struct ieee80211_vif *vif,
782362306a36Sopenharmony_ci			  unsigned int link_id, u16 ac,
782462306a36Sopenharmony_ci			  const struct ieee80211_tx_queue_params *params)
782562306a36Sopenharmony_ci{
782662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
782762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
782862306a36Sopenharmony_ci	struct wmi_wmm_params_arg *p = NULL;
782962306a36Sopenharmony_ci	int ret;
783062306a36Sopenharmony_ci
783162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
783262306a36Sopenharmony_ci
783362306a36Sopenharmony_ci	switch (ac) {
783462306a36Sopenharmony_ci	case IEEE80211_AC_VO:
783562306a36Sopenharmony_ci		p = &arvif->wmm_params.ac_vo;
783662306a36Sopenharmony_ci		break;
783762306a36Sopenharmony_ci	case IEEE80211_AC_VI:
783862306a36Sopenharmony_ci		p = &arvif->wmm_params.ac_vi;
783962306a36Sopenharmony_ci		break;
784062306a36Sopenharmony_ci	case IEEE80211_AC_BE:
784162306a36Sopenharmony_ci		p = &arvif->wmm_params.ac_be;
784262306a36Sopenharmony_ci		break;
784362306a36Sopenharmony_ci	case IEEE80211_AC_BK:
784462306a36Sopenharmony_ci		p = &arvif->wmm_params.ac_bk;
784562306a36Sopenharmony_ci		break;
784662306a36Sopenharmony_ci	}
784762306a36Sopenharmony_ci
784862306a36Sopenharmony_ci	if (WARN_ON(!p)) {
784962306a36Sopenharmony_ci		ret = -EINVAL;
785062306a36Sopenharmony_ci		goto exit;
785162306a36Sopenharmony_ci	}
785262306a36Sopenharmony_ci
785362306a36Sopenharmony_ci	p->cwmin = params->cw_min;
785462306a36Sopenharmony_ci	p->cwmax = params->cw_max;
785562306a36Sopenharmony_ci	p->aifs = params->aifs;
785662306a36Sopenharmony_ci
785762306a36Sopenharmony_ci	/*
785862306a36Sopenharmony_ci	 * The channel time duration programmed in the HW is in absolute
785962306a36Sopenharmony_ci	 * microseconds, while mac80211 gives the txop in units of
786062306a36Sopenharmony_ci	 * 32 microseconds.
786162306a36Sopenharmony_ci	 */
786262306a36Sopenharmony_ci	p->txop = params->txop * 32;
786362306a36Sopenharmony_ci
786462306a36Sopenharmony_ci	if (ar->wmi.ops->gen_vdev_wmm_conf) {
786562306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
786662306a36Sopenharmony_ci					       &arvif->wmm_params);
786762306a36Sopenharmony_ci		if (ret) {
786862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
786962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
787062306a36Sopenharmony_ci			goto exit;
787162306a36Sopenharmony_ci		}
787262306a36Sopenharmony_ci	} else {
787362306a36Sopenharmony_ci		/* This won't work well with multi-interface cases but it's
787462306a36Sopenharmony_ci		 * better than nothing.
787562306a36Sopenharmony_ci		 */
787662306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params);
787762306a36Sopenharmony_ci		if (ret) {
787862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
787962306a36Sopenharmony_ci			goto exit;
788062306a36Sopenharmony_ci		}
788162306a36Sopenharmony_ci	}
788262306a36Sopenharmony_ci
788362306a36Sopenharmony_ci	ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
788462306a36Sopenharmony_ci	if (ret)
788562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set sta uapsd: %d\n", ret);
788662306a36Sopenharmony_ci
788762306a36Sopenharmony_ciexit:
788862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
788962306a36Sopenharmony_ci	return ret;
789062306a36Sopenharmony_ci}
789162306a36Sopenharmony_ci
789262306a36Sopenharmony_cistatic int ath10k_remain_on_channel(struct ieee80211_hw *hw,
789362306a36Sopenharmony_ci				    struct ieee80211_vif *vif,
789462306a36Sopenharmony_ci				    struct ieee80211_channel *chan,
789562306a36Sopenharmony_ci				    int duration,
789662306a36Sopenharmony_ci				    enum ieee80211_roc_type type)
789762306a36Sopenharmony_ci{
789862306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
789962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
790062306a36Sopenharmony_ci	struct wmi_start_scan_arg arg;
790162306a36Sopenharmony_ci	int ret = 0;
790262306a36Sopenharmony_ci	u32 scan_time_msec;
790362306a36Sopenharmony_ci
790462306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
790562306a36Sopenharmony_ci
790662306a36Sopenharmony_ci	if (ath10k_mac_tdls_vif_stations_count(hw, vif) > 0) {
790762306a36Sopenharmony_ci		ret = -EBUSY;
790862306a36Sopenharmony_ci		goto exit;
790962306a36Sopenharmony_ci	}
791062306a36Sopenharmony_ci
791162306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
791262306a36Sopenharmony_ci	switch (ar->scan.state) {
791362306a36Sopenharmony_ci	case ATH10K_SCAN_IDLE:
791462306a36Sopenharmony_ci		reinit_completion(&ar->scan.started);
791562306a36Sopenharmony_ci		reinit_completion(&ar->scan.completed);
791662306a36Sopenharmony_ci		reinit_completion(&ar->scan.on_channel);
791762306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_STARTING;
791862306a36Sopenharmony_ci		ar->scan.is_roc = true;
791962306a36Sopenharmony_ci		ar->scan.vdev_id = arvif->vdev_id;
792062306a36Sopenharmony_ci		ar->scan.roc_freq = chan->center_freq;
792162306a36Sopenharmony_ci		ar->scan.roc_notify = true;
792262306a36Sopenharmony_ci		ret = 0;
792362306a36Sopenharmony_ci		break;
792462306a36Sopenharmony_ci	case ATH10K_SCAN_STARTING:
792562306a36Sopenharmony_ci	case ATH10K_SCAN_RUNNING:
792662306a36Sopenharmony_ci	case ATH10K_SCAN_ABORTING:
792762306a36Sopenharmony_ci		ret = -EBUSY;
792862306a36Sopenharmony_ci		break;
792962306a36Sopenharmony_ci	}
793062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
793162306a36Sopenharmony_ci
793262306a36Sopenharmony_ci	if (ret)
793362306a36Sopenharmony_ci		goto exit;
793462306a36Sopenharmony_ci
793562306a36Sopenharmony_ci	scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2;
793662306a36Sopenharmony_ci
793762306a36Sopenharmony_ci	memset(&arg, 0, sizeof(arg));
793862306a36Sopenharmony_ci	ath10k_wmi_start_scan_init(ar, &arg);
793962306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
794062306a36Sopenharmony_ci	arg.scan_id = ATH10K_SCAN_ID;
794162306a36Sopenharmony_ci	arg.n_channels = 1;
794262306a36Sopenharmony_ci	arg.channels[0] = chan->center_freq;
794362306a36Sopenharmony_ci	arg.dwell_time_active = scan_time_msec;
794462306a36Sopenharmony_ci	arg.dwell_time_passive = scan_time_msec;
794562306a36Sopenharmony_ci	arg.max_scan_time = scan_time_msec;
794662306a36Sopenharmony_ci	arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
794762306a36Sopenharmony_ci	arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
794862306a36Sopenharmony_ci	arg.burst_duration_ms = duration;
794962306a36Sopenharmony_ci
795062306a36Sopenharmony_ci	ret = ath10k_start_scan(ar, &arg);
795162306a36Sopenharmony_ci	if (ret) {
795262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start roc scan: %d\n", ret);
795362306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
795462306a36Sopenharmony_ci		ar->scan.state = ATH10K_SCAN_IDLE;
795562306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
795662306a36Sopenharmony_ci		goto exit;
795762306a36Sopenharmony_ci	}
795862306a36Sopenharmony_ci
795962306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ);
796062306a36Sopenharmony_ci	if (ret == 0) {
796162306a36Sopenharmony_ci		ath10k_warn(ar, "failed to switch to channel for roc scan\n");
796262306a36Sopenharmony_ci
796362306a36Sopenharmony_ci		ret = ath10k_scan_stop(ar);
796462306a36Sopenharmony_ci		if (ret)
796562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to stop scan: %d\n", ret);
796662306a36Sopenharmony_ci
796762306a36Sopenharmony_ci		ret = -ETIMEDOUT;
796862306a36Sopenharmony_ci		goto exit;
796962306a36Sopenharmony_ci	}
797062306a36Sopenharmony_ci
797162306a36Sopenharmony_ci	ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
797262306a36Sopenharmony_ci				     msecs_to_jiffies(duration));
797362306a36Sopenharmony_ci
797462306a36Sopenharmony_ci	ret = 0;
797562306a36Sopenharmony_ciexit:
797662306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
797762306a36Sopenharmony_ci	return ret;
797862306a36Sopenharmony_ci}
797962306a36Sopenharmony_ci
798062306a36Sopenharmony_cistatic int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw,
798162306a36Sopenharmony_ci					   struct ieee80211_vif *vif)
798262306a36Sopenharmony_ci{
798362306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
798462306a36Sopenharmony_ci
798562306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
798662306a36Sopenharmony_ci
798762306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
798862306a36Sopenharmony_ci	ar->scan.roc_notify = false;
798962306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
799062306a36Sopenharmony_ci
799162306a36Sopenharmony_ci	ath10k_scan_abort(ar);
799262306a36Sopenharmony_ci
799362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
799462306a36Sopenharmony_ci
799562306a36Sopenharmony_ci	cancel_delayed_work_sync(&ar->scan.timeout);
799662306a36Sopenharmony_ci
799762306a36Sopenharmony_ci	return 0;
799862306a36Sopenharmony_ci}
799962306a36Sopenharmony_ci
800062306a36Sopenharmony_ci/*
800162306a36Sopenharmony_ci * Both RTS and Fragmentation threshold are interface-specific
800262306a36Sopenharmony_ci * in ath10k, but device-specific in mac80211.
800362306a36Sopenharmony_ci */
800462306a36Sopenharmony_ci
800562306a36Sopenharmony_cistatic int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
800662306a36Sopenharmony_ci{
800762306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
800862306a36Sopenharmony_ci	struct ath10k_vif *arvif;
800962306a36Sopenharmony_ci	int ret = 0;
801062306a36Sopenharmony_ci
801162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
801262306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list) {
801362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
801462306a36Sopenharmony_ci			   arvif->vdev_id, value);
801562306a36Sopenharmony_ci
801662306a36Sopenharmony_ci		ret = ath10k_mac_set_rts(arvif, value);
801762306a36Sopenharmony_ci		if (ret) {
801862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
801962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
802062306a36Sopenharmony_ci			break;
802162306a36Sopenharmony_ci		}
802262306a36Sopenharmony_ci	}
802362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
802462306a36Sopenharmony_ci
802562306a36Sopenharmony_ci	return ret;
802662306a36Sopenharmony_ci}
802762306a36Sopenharmony_ci
802862306a36Sopenharmony_cistatic int ath10k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
802962306a36Sopenharmony_ci{
803062306a36Sopenharmony_ci	/* Even though there's a WMI enum for fragmentation threshold no known
803162306a36Sopenharmony_ci	 * firmware actually implements it. Moreover it is not possible to rely
803262306a36Sopenharmony_ci	 * frame fragmentation to mac80211 because firmware clears the "more
803362306a36Sopenharmony_ci	 * fragments" bit in frame control making it impossible for remote
803462306a36Sopenharmony_ci	 * devices to reassemble frames.
803562306a36Sopenharmony_ci	 *
803662306a36Sopenharmony_ci	 * Hence implement a dummy callback just to say fragmentation isn't
803762306a36Sopenharmony_ci	 * supported. This effectively prevents mac80211 from doing frame
803862306a36Sopenharmony_ci	 * fragmentation in software.
803962306a36Sopenharmony_ci	 */
804062306a36Sopenharmony_ci	return -EOPNOTSUPP;
804162306a36Sopenharmony_ci}
804262306a36Sopenharmony_ci
804362306a36Sopenharmony_civoid ath10k_mac_wait_tx_complete(struct ath10k *ar)
804462306a36Sopenharmony_ci{
804562306a36Sopenharmony_ci	bool skip;
804662306a36Sopenharmony_ci	long time_left;
804762306a36Sopenharmony_ci
804862306a36Sopenharmony_ci	/* mac80211 doesn't care if we really xmit queued frames or not
804962306a36Sopenharmony_ci	 * we'll collect those frames either way if we stop/delete vdevs
805062306a36Sopenharmony_ci	 */
805162306a36Sopenharmony_ci
805262306a36Sopenharmony_ci	if (ar->state == ATH10K_STATE_WEDGED)
805362306a36Sopenharmony_ci		return;
805462306a36Sopenharmony_ci
805562306a36Sopenharmony_ci	time_left = wait_event_timeout(ar->htt.empty_tx_wq, ({
805662306a36Sopenharmony_ci			bool empty;
805762306a36Sopenharmony_ci
805862306a36Sopenharmony_ci			spin_lock_bh(&ar->htt.tx_lock);
805962306a36Sopenharmony_ci			empty = (ar->htt.num_pending_tx == 0);
806062306a36Sopenharmony_ci			spin_unlock_bh(&ar->htt.tx_lock);
806162306a36Sopenharmony_ci
806262306a36Sopenharmony_ci			skip = (ar->state == ATH10K_STATE_WEDGED) ||
806362306a36Sopenharmony_ci			       test_bit(ATH10K_FLAG_CRASH_FLUSH,
806462306a36Sopenharmony_ci					&ar->dev_flags);
806562306a36Sopenharmony_ci
806662306a36Sopenharmony_ci			(empty || skip);
806762306a36Sopenharmony_ci		}), ATH10K_FLUSH_TIMEOUT_HZ);
806862306a36Sopenharmony_ci
806962306a36Sopenharmony_ci	if (time_left == 0 || skip)
807062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to flush transmit queue (skip %i ar-state %i): %ld\n",
807162306a36Sopenharmony_ci			    skip, ar->state, time_left);
807262306a36Sopenharmony_ci}
807362306a36Sopenharmony_ci
807462306a36Sopenharmony_cistatic void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
807562306a36Sopenharmony_ci			 u32 queues, bool drop)
807662306a36Sopenharmony_ci{
807762306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
807862306a36Sopenharmony_ci	struct ath10k_vif *arvif;
807962306a36Sopenharmony_ci	u32 bitmap;
808062306a36Sopenharmony_ci
808162306a36Sopenharmony_ci	if (drop) {
808262306a36Sopenharmony_ci		if (vif && vif->type == NL80211_IFTYPE_STATION) {
808362306a36Sopenharmony_ci			bitmap = ~(1 << WMI_MGMT_TID);
808462306a36Sopenharmony_ci			list_for_each_entry(arvif, &ar->arvifs, list) {
808562306a36Sopenharmony_ci				if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
808662306a36Sopenharmony_ci					ath10k_wmi_peer_flush(ar, arvif->vdev_id,
808762306a36Sopenharmony_ci							      arvif->bssid, bitmap);
808862306a36Sopenharmony_ci			}
808962306a36Sopenharmony_ci			ath10k_htt_flush_tx(&ar->htt);
809062306a36Sopenharmony_ci		}
809162306a36Sopenharmony_ci		return;
809262306a36Sopenharmony_ci	}
809362306a36Sopenharmony_ci
809462306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
809562306a36Sopenharmony_ci	ath10k_mac_wait_tx_complete(ar);
809662306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
809762306a36Sopenharmony_ci}
809862306a36Sopenharmony_ci
809962306a36Sopenharmony_ci/* TODO: Implement this function properly
810062306a36Sopenharmony_ci * For now it is needed to reply to Probe Requests in IBSS mode.
810162306a36Sopenharmony_ci * Probably we need this information from FW.
810262306a36Sopenharmony_ci */
810362306a36Sopenharmony_cistatic int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
810462306a36Sopenharmony_ci{
810562306a36Sopenharmony_ci	return 1;
810662306a36Sopenharmony_ci}
810762306a36Sopenharmony_ci
810862306a36Sopenharmony_cistatic void ath10k_reconfig_complete(struct ieee80211_hw *hw,
810962306a36Sopenharmony_ci				     enum ieee80211_reconfig_type reconfig_type)
811062306a36Sopenharmony_ci{
811162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
811262306a36Sopenharmony_ci	struct ath10k_vif *arvif;
811362306a36Sopenharmony_ci
811462306a36Sopenharmony_ci	if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
811562306a36Sopenharmony_ci		return;
811662306a36Sopenharmony_ci
811762306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
811862306a36Sopenharmony_ci
811962306a36Sopenharmony_ci	/* If device failed to restart it will be in a different state, e.g.
812062306a36Sopenharmony_ci	 * ATH10K_STATE_WEDGED
812162306a36Sopenharmony_ci	 */
812262306a36Sopenharmony_ci	if (ar->state == ATH10K_STATE_RESTARTED) {
812362306a36Sopenharmony_ci		ath10k_info(ar, "device successfully recovered\n");
812462306a36Sopenharmony_ci		ar->state = ATH10K_STATE_ON;
812562306a36Sopenharmony_ci		ieee80211_wake_queues(ar->hw);
812662306a36Sopenharmony_ci		clear_bit(ATH10K_FLAG_RESTARTING, &ar->dev_flags);
812762306a36Sopenharmony_ci		if (ar->hw_params.hw_restart_disconnect) {
812862306a36Sopenharmony_ci			list_for_each_entry(arvif, &ar->arvifs, list) {
812962306a36Sopenharmony_ci				if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA)
813062306a36Sopenharmony_ci					ieee80211_hw_restart_disconnect(arvif->vif);
813162306a36Sopenharmony_ci				}
813262306a36Sopenharmony_ci		}
813362306a36Sopenharmony_ci	}
813462306a36Sopenharmony_ci
813562306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
813662306a36Sopenharmony_ci}
813762306a36Sopenharmony_ci
813862306a36Sopenharmony_cistatic void
813962306a36Sopenharmony_ciath10k_mac_update_bss_chan_survey(struct ath10k *ar,
814062306a36Sopenharmony_ci				  struct ieee80211_channel *channel)
814162306a36Sopenharmony_ci{
814262306a36Sopenharmony_ci	int ret;
814362306a36Sopenharmony_ci	enum wmi_bss_survey_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ;
814462306a36Sopenharmony_ci
814562306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
814662306a36Sopenharmony_ci
814762306a36Sopenharmony_ci	if (!test_bit(WMI_SERVICE_BSS_CHANNEL_INFO_64, ar->wmi.svc_map) ||
814862306a36Sopenharmony_ci	    (ar->rx_channel != channel))
814962306a36Sopenharmony_ci		return;
815062306a36Sopenharmony_ci
815162306a36Sopenharmony_ci	if (ar->scan.state != ATH10K_SCAN_IDLE) {
815262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC, "ignoring bss chan info request while scanning..\n");
815362306a36Sopenharmony_ci		return;
815462306a36Sopenharmony_ci	}
815562306a36Sopenharmony_ci
815662306a36Sopenharmony_ci	reinit_completion(&ar->bss_survey_done);
815762306a36Sopenharmony_ci
815862306a36Sopenharmony_ci	ret = ath10k_wmi_pdev_bss_chan_info_request(ar, type);
815962306a36Sopenharmony_ci	if (ret) {
816062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to send pdev bss chan info request\n");
816162306a36Sopenharmony_ci		return;
816262306a36Sopenharmony_ci	}
816362306a36Sopenharmony_ci
816462306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&ar->bss_survey_done, 3 * HZ);
816562306a36Sopenharmony_ci	if (!ret) {
816662306a36Sopenharmony_ci		ath10k_warn(ar, "bss channel survey timed out\n");
816762306a36Sopenharmony_ci		return;
816862306a36Sopenharmony_ci	}
816962306a36Sopenharmony_ci}
817062306a36Sopenharmony_ci
817162306a36Sopenharmony_cistatic int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
817262306a36Sopenharmony_ci			     struct survey_info *survey)
817362306a36Sopenharmony_ci{
817462306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
817562306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
817662306a36Sopenharmony_ci	struct survey_info *ar_survey = &ar->survey[idx];
817762306a36Sopenharmony_ci	int ret = 0;
817862306a36Sopenharmony_ci
817962306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
818062306a36Sopenharmony_ci
818162306a36Sopenharmony_ci	sband = hw->wiphy->bands[NL80211_BAND_2GHZ];
818262306a36Sopenharmony_ci	if (sband && idx >= sband->n_channels) {
818362306a36Sopenharmony_ci		idx -= sband->n_channels;
818462306a36Sopenharmony_ci		sband = NULL;
818562306a36Sopenharmony_ci	}
818662306a36Sopenharmony_ci
818762306a36Sopenharmony_ci	if (!sband)
818862306a36Sopenharmony_ci		sband = hw->wiphy->bands[NL80211_BAND_5GHZ];
818962306a36Sopenharmony_ci
819062306a36Sopenharmony_ci	if (!sband || idx >= sband->n_channels) {
819162306a36Sopenharmony_ci		ret = -ENOENT;
819262306a36Sopenharmony_ci		goto exit;
819362306a36Sopenharmony_ci	}
819462306a36Sopenharmony_ci
819562306a36Sopenharmony_ci	ath10k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
819662306a36Sopenharmony_ci
819762306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
819862306a36Sopenharmony_ci	memcpy(survey, ar_survey, sizeof(*survey));
819962306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
820062306a36Sopenharmony_ci
820162306a36Sopenharmony_ci	survey->channel = &sband->channels[idx];
820262306a36Sopenharmony_ci
820362306a36Sopenharmony_ci	if (ar->rx_channel == survey->channel)
820462306a36Sopenharmony_ci		survey->filled |= SURVEY_INFO_IN_USE;
820562306a36Sopenharmony_ci
820662306a36Sopenharmony_ciexit:
820762306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
820862306a36Sopenharmony_ci	return ret;
820962306a36Sopenharmony_ci}
821062306a36Sopenharmony_ci
821162306a36Sopenharmony_cistatic bool
821262306a36Sopenharmony_ciath10k_mac_bitrate_mask_get_single_nss(struct ath10k *ar,
821362306a36Sopenharmony_ci				       enum nl80211_band band,
821462306a36Sopenharmony_ci				       const struct cfg80211_bitrate_mask *mask,
821562306a36Sopenharmony_ci				       int *nss)
821662306a36Sopenharmony_ci{
821762306a36Sopenharmony_ci	struct ieee80211_supported_band *sband = &ar->mac.sbands[band];
821862306a36Sopenharmony_ci	u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
821962306a36Sopenharmony_ci	u8 ht_nss_mask = 0;
822062306a36Sopenharmony_ci	u8 vht_nss_mask = 0;
822162306a36Sopenharmony_ci	int i;
822262306a36Sopenharmony_ci
822362306a36Sopenharmony_ci	if (mask->control[band].legacy)
822462306a36Sopenharmony_ci		return false;
822562306a36Sopenharmony_ci
822662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) {
822762306a36Sopenharmony_ci		if (mask->control[band].ht_mcs[i] == 0)
822862306a36Sopenharmony_ci			continue;
822962306a36Sopenharmony_ci		else if (mask->control[band].ht_mcs[i] ==
823062306a36Sopenharmony_ci			 sband->ht_cap.mcs.rx_mask[i])
823162306a36Sopenharmony_ci			ht_nss_mask |= BIT(i);
823262306a36Sopenharmony_ci		else
823362306a36Sopenharmony_ci			return false;
823462306a36Sopenharmony_ci	}
823562306a36Sopenharmony_ci
823662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) {
823762306a36Sopenharmony_ci		if (mask->control[band].vht_mcs[i] == 0)
823862306a36Sopenharmony_ci			continue;
823962306a36Sopenharmony_ci		else if (mask->control[band].vht_mcs[i] ==
824062306a36Sopenharmony_ci			 ath10k_mac_get_max_vht_mcs_map(vht_mcs_map, i))
824162306a36Sopenharmony_ci			vht_nss_mask |= BIT(i);
824262306a36Sopenharmony_ci		else
824362306a36Sopenharmony_ci			return false;
824462306a36Sopenharmony_ci	}
824562306a36Sopenharmony_ci
824662306a36Sopenharmony_ci	if (ht_nss_mask != vht_nss_mask)
824762306a36Sopenharmony_ci		return false;
824862306a36Sopenharmony_ci
824962306a36Sopenharmony_ci	if (ht_nss_mask == 0)
825062306a36Sopenharmony_ci		return false;
825162306a36Sopenharmony_ci
825262306a36Sopenharmony_ci	if (BIT(fls(ht_nss_mask)) - 1 != ht_nss_mask)
825362306a36Sopenharmony_ci		return false;
825462306a36Sopenharmony_ci
825562306a36Sopenharmony_ci	*nss = fls(ht_nss_mask);
825662306a36Sopenharmony_ci
825762306a36Sopenharmony_ci	return true;
825862306a36Sopenharmony_ci}
825962306a36Sopenharmony_ci
826062306a36Sopenharmony_cistatic int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif,
826162306a36Sopenharmony_ci					    u8 rate, u8 nss, u8 sgi, u8 ldpc)
826262306a36Sopenharmony_ci{
826362306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
826462306a36Sopenharmony_ci	u32 vdev_param;
826562306a36Sopenharmony_ci	int ret;
826662306a36Sopenharmony_ci
826762306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
826862306a36Sopenharmony_ci
826962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02x nss %u sgi %u\n",
827062306a36Sopenharmony_ci		   arvif->vdev_id, rate, nss, sgi);
827162306a36Sopenharmony_ci
827262306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->fixed_rate;
827362306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, rate);
827462306a36Sopenharmony_ci	if (ret) {
827562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set fixed rate param 0x%02x: %d\n",
827662306a36Sopenharmony_ci			    rate, ret);
827762306a36Sopenharmony_ci		return ret;
827862306a36Sopenharmony_ci	}
827962306a36Sopenharmony_ci
828062306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->nss;
828162306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, nss);
828262306a36Sopenharmony_ci	if (ret) {
828362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set nss param %d: %d\n", nss, ret);
828462306a36Sopenharmony_ci		return ret;
828562306a36Sopenharmony_ci	}
828662306a36Sopenharmony_ci
828762306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->sgi;
828862306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, sgi);
828962306a36Sopenharmony_ci	if (ret) {
829062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set sgi param %d: %d\n", sgi, ret);
829162306a36Sopenharmony_ci		return ret;
829262306a36Sopenharmony_ci	}
829362306a36Sopenharmony_ci
829462306a36Sopenharmony_ci	vdev_param = ar->wmi.vdev_param->ldpc;
829562306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ldpc);
829662306a36Sopenharmony_ci	if (ret) {
829762306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set ldpc param %d: %d\n", ldpc, ret);
829862306a36Sopenharmony_ci		return ret;
829962306a36Sopenharmony_ci	}
830062306a36Sopenharmony_ci
830162306a36Sopenharmony_ci	return 0;
830262306a36Sopenharmony_ci}
830362306a36Sopenharmony_ci
830462306a36Sopenharmony_cistatic bool
830562306a36Sopenharmony_ciath10k_mac_can_set_bitrate_mask(struct ath10k *ar,
830662306a36Sopenharmony_ci				enum nl80211_band band,
830762306a36Sopenharmony_ci				const struct cfg80211_bitrate_mask *mask,
830862306a36Sopenharmony_ci				bool allow_pfr)
830962306a36Sopenharmony_ci{
831062306a36Sopenharmony_ci	int i;
831162306a36Sopenharmony_ci	u16 vht_mcs;
831262306a36Sopenharmony_ci
831362306a36Sopenharmony_ci	/* Due to firmware limitation in WMI_PEER_ASSOC_CMDID it is impossible
831462306a36Sopenharmony_ci	 * to express all VHT MCS rate masks. Effectively only the following
831562306a36Sopenharmony_ci	 * ranges can be used: none, 0-7, 0-8 and 0-9.
831662306a36Sopenharmony_ci	 */
831762306a36Sopenharmony_ci	for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
831862306a36Sopenharmony_ci		vht_mcs = mask->control[band].vht_mcs[i];
831962306a36Sopenharmony_ci
832062306a36Sopenharmony_ci		switch (vht_mcs) {
832162306a36Sopenharmony_ci		case 0:
832262306a36Sopenharmony_ci		case BIT(8) - 1:
832362306a36Sopenharmony_ci		case BIT(9) - 1:
832462306a36Sopenharmony_ci		case BIT(10) - 1:
832562306a36Sopenharmony_ci			break;
832662306a36Sopenharmony_ci		default:
832762306a36Sopenharmony_ci			if (!allow_pfr)
832862306a36Sopenharmony_ci				ath10k_warn(ar, "refusing bitrate mask with missing 0-7 VHT MCS rates\n");
832962306a36Sopenharmony_ci			return false;
833062306a36Sopenharmony_ci		}
833162306a36Sopenharmony_ci	}
833262306a36Sopenharmony_ci
833362306a36Sopenharmony_ci	return true;
833462306a36Sopenharmony_ci}
833562306a36Sopenharmony_ci
833662306a36Sopenharmony_cistatic bool ath10k_mac_set_vht_bitrate_mask_fixup(struct ath10k *ar,
833762306a36Sopenharmony_ci						  struct ath10k_vif *arvif,
833862306a36Sopenharmony_ci						  struct ieee80211_sta *sta)
833962306a36Sopenharmony_ci{
834062306a36Sopenharmony_ci	int err;
834162306a36Sopenharmony_ci	u8 rate = arvif->vht_pfr;
834262306a36Sopenharmony_ci
834362306a36Sopenharmony_ci	/* skip non vht and multiple rate peers */
834462306a36Sopenharmony_ci	if (!sta->deflink.vht_cap.vht_supported || arvif->vht_num_rates != 1)
834562306a36Sopenharmony_ci		return false;
834662306a36Sopenharmony_ci
834762306a36Sopenharmony_ci	err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
834862306a36Sopenharmony_ci					WMI_PEER_PARAM_FIXED_RATE, rate);
834962306a36Sopenharmony_ci	if (err)
835062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to enable STA %pM peer fixed rate: %d\n",
835162306a36Sopenharmony_ci			    sta->addr, err);
835262306a36Sopenharmony_ci
835362306a36Sopenharmony_ci	return true;
835462306a36Sopenharmony_ci}
835562306a36Sopenharmony_ci
835662306a36Sopenharmony_cistatic void ath10k_mac_set_bitrate_mask_iter(void *data,
835762306a36Sopenharmony_ci					     struct ieee80211_sta *sta)
835862306a36Sopenharmony_ci{
835962306a36Sopenharmony_ci	struct ath10k_vif *arvif = data;
836062306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
836162306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
836262306a36Sopenharmony_ci
836362306a36Sopenharmony_ci	if (arsta->arvif != arvif)
836462306a36Sopenharmony_ci		return;
836562306a36Sopenharmony_ci
836662306a36Sopenharmony_ci	if (ath10k_mac_set_vht_bitrate_mask_fixup(ar, arvif, sta))
836762306a36Sopenharmony_ci		return;
836862306a36Sopenharmony_ci
836962306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
837062306a36Sopenharmony_ci	arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
837162306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
837262306a36Sopenharmony_ci
837362306a36Sopenharmony_ci	ieee80211_queue_work(ar->hw, &arsta->update_wk);
837462306a36Sopenharmony_ci}
837562306a36Sopenharmony_ci
837662306a36Sopenharmony_cistatic void ath10k_mac_clr_bitrate_mask_iter(void *data,
837762306a36Sopenharmony_ci					     struct ieee80211_sta *sta)
837862306a36Sopenharmony_ci{
837962306a36Sopenharmony_ci	struct ath10k_vif *arvif = data;
838062306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
838162306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
838262306a36Sopenharmony_ci	int err;
838362306a36Sopenharmony_ci
838462306a36Sopenharmony_ci	/* clear vht peers only */
838562306a36Sopenharmony_ci	if (arsta->arvif != arvif || !sta->deflink.vht_cap.vht_supported)
838662306a36Sopenharmony_ci		return;
838762306a36Sopenharmony_ci
838862306a36Sopenharmony_ci	err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
838962306a36Sopenharmony_ci					WMI_PEER_PARAM_FIXED_RATE,
839062306a36Sopenharmony_ci					WMI_FIXED_RATE_NONE);
839162306a36Sopenharmony_ci	if (err)
839262306a36Sopenharmony_ci		ath10k_warn(ar, "failed to clear STA %pM peer fixed rate: %d\n",
839362306a36Sopenharmony_ci			    sta->addr, err);
839462306a36Sopenharmony_ci}
839562306a36Sopenharmony_ci
839662306a36Sopenharmony_cistatic int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
839762306a36Sopenharmony_ci					  struct ieee80211_vif *vif,
839862306a36Sopenharmony_ci					  const struct cfg80211_bitrate_mask *mask)
839962306a36Sopenharmony_ci{
840062306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
840162306a36Sopenharmony_ci	struct cfg80211_chan_def def;
840262306a36Sopenharmony_ci	struct ath10k *ar = arvif->ar;
840362306a36Sopenharmony_ci	enum nl80211_band band;
840462306a36Sopenharmony_ci	const u8 *ht_mcs_mask;
840562306a36Sopenharmony_ci	const u16 *vht_mcs_mask;
840662306a36Sopenharmony_ci	u8 rate;
840762306a36Sopenharmony_ci	u8 nss;
840862306a36Sopenharmony_ci	u8 sgi;
840962306a36Sopenharmony_ci	u8 ldpc;
841062306a36Sopenharmony_ci	int single_nss;
841162306a36Sopenharmony_ci	int ret;
841262306a36Sopenharmony_ci	int vht_num_rates, allow_pfr;
841362306a36Sopenharmony_ci	u8 vht_pfr;
841462306a36Sopenharmony_ci	bool update_bitrate_mask = true;
841562306a36Sopenharmony_ci
841662306a36Sopenharmony_ci	if (ath10k_mac_vif_chan(vif, &def))
841762306a36Sopenharmony_ci		return -EPERM;
841862306a36Sopenharmony_ci
841962306a36Sopenharmony_ci	band = def.chan->band;
842062306a36Sopenharmony_ci	ht_mcs_mask = mask->control[band].ht_mcs;
842162306a36Sopenharmony_ci	vht_mcs_mask = mask->control[band].vht_mcs;
842262306a36Sopenharmony_ci	ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC);
842362306a36Sopenharmony_ci
842462306a36Sopenharmony_ci	sgi = mask->control[band].gi;
842562306a36Sopenharmony_ci	if (sgi == NL80211_TXRATE_FORCE_LGI)
842662306a36Sopenharmony_ci		return -EINVAL;
842762306a36Sopenharmony_ci
842862306a36Sopenharmony_ci	allow_pfr = test_bit(ATH10K_FW_FEATURE_PEER_FIXED_RATE,
842962306a36Sopenharmony_ci			     ar->normal_mode_fw.fw_file.fw_features);
843062306a36Sopenharmony_ci	if (allow_pfr) {
843162306a36Sopenharmony_ci		mutex_lock(&ar->conf_mutex);
843262306a36Sopenharmony_ci		ieee80211_iterate_stations_atomic(ar->hw,
843362306a36Sopenharmony_ci						  ath10k_mac_clr_bitrate_mask_iter,
843462306a36Sopenharmony_ci						  arvif);
843562306a36Sopenharmony_ci		mutex_unlock(&ar->conf_mutex);
843662306a36Sopenharmony_ci	}
843762306a36Sopenharmony_ci
843862306a36Sopenharmony_ci	if (ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask,
843962306a36Sopenharmony_ci						    &vht_num_rates)) {
844062306a36Sopenharmony_ci		ret = ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
844162306a36Sopenharmony_ci							      &rate, &nss,
844262306a36Sopenharmony_ci							      false);
844362306a36Sopenharmony_ci		if (ret) {
844462306a36Sopenharmony_ci			ath10k_warn(ar, "failed to get single rate for vdev %i: %d\n",
844562306a36Sopenharmony_ci				    arvif->vdev_id, ret);
844662306a36Sopenharmony_ci			return ret;
844762306a36Sopenharmony_ci		}
844862306a36Sopenharmony_ci	} else if (ath10k_mac_bitrate_mask_get_single_nss(ar, band, mask,
844962306a36Sopenharmony_ci							  &single_nss)) {
845062306a36Sopenharmony_ci		rate = WMI_FIXED_RATE_NONE;
845162306a36Sopenharmony_ci		nss = single_nss;
845262306a36Sopenharmony_ci	} else {
845362306a36Sopenharmony_ci		rate = WMI_FIXED_RATE_NONE;
845462306a36Sopenharmony_ci		nss = min(ar->num_rf_chains,
845562306a36Sopenharmony_ci			  max(ath10k_mac_max_ht_nss(ht_mcs_mask),
845662306a36Sopenharmony_ci			      ath10k_mac_max_vht_nss(vht_mcs_mask)));
845762306a36Sopenharmony_ci
845862306a36Sopenharmony_ci		if (!ath10k_mac_can_set_bitrate_mask(ar, band, mask,
845962306a36Sopenharmony_ci						     allow_pfr)) {
846062306a36Sopenharmony_ci			u8 vht_nss;
846162306a36Sopenharmony_ci
846262306a36Sopenharmony_ci			if (!allow_pfr || vht_num_rates != 1)
846362306a36Sopenharmony_ci				return -EINVAL;
846462306a36Sopenharmony_ci
846562306a36Sopenharmony_ci			/* Reach here, firmware supports peer fixed rate and has
846662306a36Sopenharmony_ci			 * single vht rate, and don't update vif birate_mask, as
846762306a36Sopenharmony_ci			 * the rate only for specific peer.
846862306a36Sopenharmony_ci			 */
846962306a36Sopenharmony_ci			ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
847062306a36Sopenharmony_ci								&vht_pfr,
847162306a36Sopenharmony_ci								&vht_nss,
847262306a36Sopenharmony_ci								true);
847362306a36Sopenharmony_ci			update_bitrate_mask = false;
847462306a36Sopenharmony_ci		} else {
847562306a36Sopenharmony_ci			vht_pfr = 0;
847662306a36Sopenharmony_ci		}
847762306a36Sopenharmony_ci
847862306a36Sopenharmony_ci		mutex_lock(&ar->conf_mutex);
847962306a36Sopenharmony_ci
848062306a36Sopenharmony_ci		if (update_bitrate_mask)
848162306a36Sopenharmony_ci			arvif->bitrate_mask = *mask;
848262306a36Sopenharmony_ci		arvif->vht_num_rates = vht_num_rates;
848362306a36Sopenharmony_ci		arvif->vht_pfr = vht_pfr;
848462306a36Sopenharmony_ci		ieee80211_iterate_stations_atomic(ar->hw,
848562306a36Sopenharmony_ci						  ath10k_mac_set_bitrate_mask_iter,
848662306a36Sopenharmony_ci						  arvif);
848762306a36Sopenharmony_ci
848862306a36Sopenharmony_ci		mutex_unlock(&ar->conf_mutex);
848962306a36Sopenharmony_ci	}
849062306a36Sopenharmony_ci
849162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
849262306a36Sopenharmony_ci
849362306a36Sopenharmony_ci	ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc);
849462306a36Sopenharmony_ci	if (ret) {
849562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n",
849662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
849762306a36Sopenharmony_ci		goto exit;
849862306a36Sopenharmony_ci	}
849962306a36Sopenharmony_ci
850062306a36Sopenharmony_ciexit:
850162306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
850262306a36Sopenharmony_ci
850362306a36Sopenharmony_ci	return ret;
850462306a36Sopenharmony_ci}
850562306a36Sopenharmony_ci
850662306a36Sopenharmony_cistatic void ath10k_sta_rc_update(struct ieee80211_hw *hw,
850762306a36Sopenharmony_ci				 struct ieee80211_vif *vif,
850862306a36Sopenharmony_ci				 struct ieee80211_sta *sta,
850962306a36Sopenharmony_ci				 u32 changed)
851062306a36Sopenharmony_ci{
851162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
851262306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
851362306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
851462306a36Sopenharmony_ci	struct ath10k_peer *peer;
851562306a36Sopenharmony_ci	u32 bw, smps;
851662306a36Sopenharmony_ci
851762306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
851862306a36Sopenharmony_ci
851962306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
852062306a36Sopenharmony_ci	if (!peer) {
852162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
852262306a36Sopenharmony_ci		ath10k_warn(ar, "mac sta rc update failed to find peer %pM on vdev %i\n",
852362306a36Sopenharmony_ci			    sta->addr, arvif->vdev_id);
852462306a36Sopenharmony_ci		return;
852562306a36Sopenharmony_ci	}
852662306a36Sopenharmony_ci
852762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_STA,
852862306a36Sopenharmony_ci		   "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
852962306a36Sopenharmony_ci		   sta->addr, changed, sta->deflink.bandwidth,
853062306a36Sopenharmony_ci		   sta->deflink.rx_nss,
853162306a36Sopenharmony_ci		   sta->deflink.smps_mode);
853262306a36Sopenharmony_ci
853362306a36Sopenharmony_ci	if (changed & IEEE80211_RC_BW_CHANGED) {
853462306a36Sopenharmony_ci		bw = WMI_PEER_CHWIDTH_20MHZ;
853562306a36Sopenharmony_ci
853662306a36Sopenharmony_ci		switch (sta->deflink.bandwidth) {
853762306a36Sopenharmony_ci		case IEEE80211_STA_RX_BW_20:
853862306a36Sopenharmony_ci			bw = WMI_PEER_CHWIDTH_20MHZ;
853962306a36Sopenharmony_ci			break;
854062306a36Sopenharmony_ci		case IEEE80211_STA_RX_BW_40:
854162306a36Sopenharmony_ci			bw = WMI_PEER_CHWIDTH_40MHZ;
854262306a36Sopenharmony_ci			break;
854362306a36Sopenharmony_ci		case IEEE80211_STA_RX_BW_80:
854462306a36Sopenharmony_ci			bw = WMI_PEER_CHWIDTH_80MHZ;
854562306a36Sopenharmony_ci			break;
854662306a36Sopenharmony_ci		case IEEE80211_STA_RX_BW_160:
854762306a36Sopenharmony_ci			bw = WMI_PEER_CHWIDTH_160MHZ;
854862306a36Sopenharmony_ci			break;
854962306a36Sopenharmony_ci		default:
855062306a36Sopenharmony_ci			ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n",
855162306a36Sopenharmony_ci				    sta->deflink.bandwidth, sta->addr);
855262306a36Sopenharmony_ci			bw = WMI_PEER_CHWIDTH_20MHZ;
855362306a36Sopenharmony_ci			break;
855462306a36Sopenharmony_ci		}
855562306a36Sopenharmony_ci
855662306a36Sopenharmony_ci		arsta->bw = bw;
855762306a36Sopenharmony_ci	}
855862306a36Sopenharmony_ci
855962306a36Sopenharmony_ci	if (changed & IEEE80211_RC_NSS_CHANGED)
856062306a36Sopenharmony_ci		arsta->nss = sta->deflink.rx_nss;
856162306a36Sopenharmony_ci
856262306a36Sopenharmony_ci	if (changed & IEEE80211_RC_SMPS_CHANGED) {
856362306a36Sopenharmony_ci		smps = WMI_PEER_SMPS_PS_NONE;
856462306a36Sopenharmony_ci
856562306a36Sopenharmony_ci		switch (sta->deflink.smps_mode) {
856662306a36Sopenharmony_ci		case IEEE80211_SMPS_AUTOMATIC:
856762306a36Sopenharmony_ci		case IEEE80211_SMPS_OFF:
856862306a36Sopenharmony_ci			smps = WMI_PEER_SMPS_PS_NONE;
856962306a36Sopenharmony_ci			break;
857062306a36Sopenharmony_ci		case IEEE80211_SMPS_STATIC:
857162306a36Sopenharmony_ci			smps = WMI_PEER_SMPS_STATIC;
857262306a36Sopenharmony_ci			break;
857362306a36Sopenharmony_ci		case IEEE80211_SMPS_DYNAMIC:
857462306a36Sopenharmony_ci			smps = WMI_PEER_SMPS_DYNAMIC;
857562306a36Sopenharmony_ci			break;
857662306a36Sopenharmony_ci		case IEEE80211_SMPS_NUM_MODES:
857762306a36Sopenharmony_ci			ath10k_warn(ar, "Invalid smps %d in sta rc update for %pM\n",
857862306a36Sopenharmony_ci				    sta->deflink.smps_mode, sta->addr);
857962306a36Sopenharmony_ci			smps = WMI_PEER_SMPS_PS_NONE;
858062306a36Sopenharmony_ci			break;
858162306a36Sopenharmony_ci		}
858262306a36Sopenharmony_ci
858362306a36Sopenharmony_ci		arsta->smps = smps;
858462306a36Sopenharmony_ci	}
858562306a36Sopenharmony_ci
858662306a36Sopenharmony_ci	arsta->changed |= changed;
858762306a36Sopenharmony_ci
858862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
858962306a36Sopenharmony_ci
859062306a36Sopenharmony_ci	ieee80211_queue_work(hw, &arsta->update_wk);
859162306a36Sopenharmony_ci}
859262306a36Sopenharmony_ci
859362306a36Sopenharmony_cistatic void ath10k_offset_tsf(struct ieee80211_hw *hw,
859462306a36Sopenharmony_ci			      struct ieee80211_vif *vif, s64 tsf_offset)
859562306a36Sopenharmony_ci{
859662306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
859762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
859862306a36Sopenharmony_ci	u32 offset, vdev_param;
859962306a36Sopenharmony_ci	int ret;
860062306a36Sopenharmony_ci
860162306a36Sopenharmony_ci	if (tsf_offset < 0) {
860262306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->dec_tsf;
860362306a36Sopenharmony_ci		offset = -tsf_offset;
860462306a36Sopenharmony_ci	} else {
860562306a36Sopenharmony_ci		vdev_param = ar->wmi.vdev_param->inc_tsf;
860662306a36Sopenharmony_ci		offset = tsf_offset;
860762306a36Sopenharmony_ci	}
860862306a36Sopenharmony_ci
860962306a36Sopenharmony_ci	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
861062306a36Sopenharmony_ci					vdev_param, offset);
861162306a36Sopenharmony_ci
861262306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP)
861362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to set tsf offset %d cmd %d: %d\n",
861462306a36Sopenharmony_ci			    offset, vdev_param, ret);
861562306a36Sopenharmony_ci}
861662306a36Sopenharmony_ci
861762306a36Sopenharmony_cistatic int ath10k_ampdu_action(struct ieee80211_hw *hw,
861862306a36Sopenharmony_ci			       struct ieee80211_vif *vif,
861962306a36Sopenharmony_ci			       struct ieee80211_ampdu_params *params)
862062306a36Sopenharmony_ci{
862162306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
862262306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
862362306a36Sopenharmony_ci	struct ieee80211_sta *sta = params->sta;
862462306a36Sopenharmony_ci	enum ieee80211_ampdu_mlme_action action = params->action;
862562306a36Sopenharmony_ci	u16 tid = params->tid;
862662306a36Sopenharmony_ci
862762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %u action %d\n",
862862306a36Sopenharmony_ci		   arvif->vdev_id, sta->addr, tid, action);
862962306a36Sopenharmony_ci
863062306a36Sopenharmony_ci	switch (action) {
863162306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
863262306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
863362306a36Sopenharmony_ci		/* HTT AddBa/DelBa events trigger mac80211 Rx BA session
863462306a36Sopenharmony_ci		 * creation/removal. Do we need to verify this?
863562306a36Sopenharmony_ci		 */
863662306a36Sopenharmony_ci		return 0;
863762306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_START:
863862306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
863962306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH:
864062306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
864162306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL:
864262306a36Sopenharmony_ci		/* Firmware offloads Tx aggregation entirely so deny mac80211
864362306a36Sopenharmony_ci		 * Tx aggregation requests.
864462306a36Sopenharmony_ci		 */
864562306a36Sopenharmony_ci		return -EOPNOTSUPP;
864662306a36Sopenharmony_ci	}
864762306a36Sopenharmony_ci
864862306a36Sopenharmony_ci	return -EINVAL;
864962306a36Sopenharmony_ci}
865062306a36Sopenharmony_ci
865162306a36Sopenharmony_cistatic void
865262306a36Sopenharmony_ciath10k_mac_update_rx_channel(struct ath10k *ar,
865362306a36Sopenharmony_ci			     struct ieee80211_chanctx_conf *ctx,
865462306a36Sopenharmony_ci			     struct ieee80211_vif_chanctx_switch *vifs,
865562306a36Sopenharmony_ci			     int n_vifs)
865662306a36Sopenharmony_ci{
865762306a36Sopenharmony_ci	struct cfg80211_chan_def *def = NULL;
865862306a36Sopenharmony_ci
865962306a36Sopenharmony_ci	/* Both locks are required because ar->rx_channel is modified. This
866062306a36Sopenharmony_ci	 * allows readers to hold either lock.
866162306a36Sopenharmony_ci	 */
866262306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
866362306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
866462306a36Sopenharmony_ci
866562306a36Sopenharmony_ci	WARN_ON(ctx && vifs);
866662306a36Sopenharmony_ci	WARN_ON(vifs && !n_vifs);
866762306a36Sopenharmony_ci
866862306a36Sopenharmony_ci	/* FIXME: Sort of an optimization and a workaround. Peers and vifs are
866962306a36Sopenharmony_ci	 * on a linked list now. Doing a lookup peer -> vif -> chanctx for each
867062306a36Sopenharmony_ci	 * ppdu on Rx may reduce performance on low-end systems. It should be
867162306a36Sopenharmony_ci	 * possible to make tables/hashmaps to speed the lookup up (be vary of
867262306a36Sopenharmony_ci	 * cpu data cache lines though regarding sizes) but to keep the initial
867362306a36Sopenharmony_ci	 * implementation simple and less intrusive fallback to the slow lookup
867462306a36Sopenharmony_ci	 * only for multi-channel cases. Single-channel cases will remain to
867562306a36Sopenharmony_ci	 * use the old channel derival and thus performance should not be
867662306a36Sopenharmony_ci	 * affected much.
867762306a36Sopenharmony_ci	 */
867862306a36Sopenharmony_ci	rcu_read_lock();
867962306a36Sopenharmony_ci	if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) {
868062306a36Sopenharmony_ci		ieee80211_iter_chan_contexts_atomic(ar->hw,
868162306a36Sopenharmony_ci						    ath10k_mac_get_any_chandef_iter,
868262306a36Sopenharmony_ci						    &def);
868362306a36Sopenharmony_ci
868462306a36Sopenharmony_ci		if (vifs)
868562306a36Sopenharmony_ci			def = &vifs[0].new_ctx->def;
868662306a36Sopenharmony_ci
868762306a36Sopenharmony_ci		ar->rx_channel = def->chan;
868862306a36Sopenharmony_ci	} else if ((ctx && ath10k_mac_num_chanctxs(ar) == 0) ||
868962306a36Sopenharmony_ci		   (ctx && (ar->state == ATH10K_STATE_RESTARTED))) {
869062306a36Sopenharmony_ci		/* During driver restart due to firmware assert, since mac80211
869162306a36Sopenharmony_ci		 * already has valid channel context for given radio, channel
869262306a36Sopenharmony_ci		 * context iteration return num_chanctx > 0. So fix rx_channel
869362306a36Sopenharmony_ci		 * when restart is in progress.
869462306a36Sopenharmony_ci		 */
869562306a36Sopenharmony_ci		ar->rx_channel = ctx->def.chan;
869662306a36Sopenharmony_ci	} else {
869762306a36Sopenharmony_ci		ar->rx_channel = NULL;
869862306a36Sopenharmony_ci	}
869962306a36Sopenharmony_ci	rcu_read_unlock();
870062306a36Sopenharmony_ci}
870162306a36Sopenharmony_ci
870262306a36Sopenharmony_cistatic void
870362306a36Sopenharmony_ciath10k_mac_update_vif_chan(struct ath10k *ar,
870462306a36Sopenharmony_ci			   struct ieee80211_vif_chanctx_switch *vifs,
870562306a36Sopenharmony_ci			   int n_vifs)
870662306a36Sopenharmony_ci{
870762306a36Sopenharmony_ci	struct ath10k_vif *arvif;
870862306a36Sopenharmony_ci	int ret;
870962306a36Sopenharmony_ci	int i;
871062306a36Sopenharmony_ci
871162306a36Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
871262306a36Sopenharmony_ci
871362306a36Sopenharmony_ci	/* First stop monitor interface. Some FW versions crash if there's a
871462306a36Sopenharmony_ci	 * lone monitor interface.
871562306a36Sopenharmony_ci	 */
871662306a36Sopenharmony_ci	if (ar->monitor_started)
871762306a36Sopenharmony_ci		ath10k_monitor_stop(ar);
871862306a36Sopenharmony_ci
871962306a36Sopenharmony_ci	for (i = 0; i < n_vifs; i++) {
872062306a36Sopenharmony_ci		arvif = (void *)vifs[i].vif->drv_priv;
872162306a36Sopenharmony_ci
872262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_MAC,
872362306a36Sopenharmony_ci			   "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
872462306a36Sopenharmony_ci			   arvif->vdev_id,
872562306a36Sopenharmony_ci			   vifs[i].old_ctx->def.chan->center_freq,
872662306a36Sopenharmony_ci			   vifs[i].new_ctx->def.chan->center_freq,
872762306a36Sopenharmony_ci			   vifs[i].old_ctx->def.width,
872862306a36Sopenharmony_ci			   vifs[i].new_ctx->def.width);
872962306a36Sopenharmony_ci
873062306a36Sopenharmony_ci		if (WARN_ON(!arvif->is_started))
873162306a36Sopenharmony_ci			continue;
873262306a36Sopenharmony_ci
873362306a36Sopenharmony_ci		if (WARN_ON(!arvif->is_up))
873462306a36Sopenharmony_ci			continue;
873562306a36Sopenharmony_ci
873662306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
873762306a36Sopenharmony_ci		if (ret) {
873862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to down vdev %d: %d\n",
873962306a36Sopenharmony_ci				    arvif->vdev_id, ret);
874062306a36Sopenharmony_ci			continue;
874162306a36Sopenharmony_ci		}
874262306a36Sopenharmony_ci	}
874362306a36Sopenharmony_ci
874462306a36Sopenharmony_ci	/* All relevant vdevs are downed and associated channel resources
874562306a36Sopenharmony_ci	 * should be available for the channel switch now.
874662306a36Sopenharmony_ci	 */
874762306a36Sopenharmony_ci
874862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
874962306a36Sopenharmony_ci	ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs);
875062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
875162306a36Sopenharmony_ci
875262306a36Sopenharmony_ci	for (i = 0; i < n_vifs; i++) {
875362306a36Sopenharmony_ci		arvif = (void *)vifs[i].vif->drv_priv;
875462306a36Sopenharmony_ci
875562306a36Sopenharmony_ci		if (WARN_ON(!arvif->is_started))
875662306a36Sopenharmony_ci			continue;
875762306a36Sopenharmony_ci
875862306a36Sopenharmony_ci		if (WARN_ON(!arvif->is_up))
875962306a36Sopenharmony_ci			continue;
876062306a36Sopenharmony_ci
876162306a36Sopenharmony_ci		ret = ath10k_mac_setup_bcn_tmpl(arvif);
876262306a36Sopenharmony_ci		if (ret)
876362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n",
876462306a36Sopenharmony_ci				    ret);
876562306a36Sopenharmony_ci
876662306a36Sopenharmony_ci		ret = ath10k_mac_setup_prb_tmpl(arvif);
876762306a36Sopenharmony_ci		if (ret)
876862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n",
876962306a36Sopenharmony_ci				    ret);
877062306a36Sopenharmony_ci
877162306a36Sopenharmony_ci		ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def);
877262306a36Sopenharmony_ci		if (ret) {
877362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to restart vdev %d: %d\n",
877462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
877562306a36Sopenharmony_ci			continue;
877662306a36Sopenharmony_ci		}
877762306a36Sopenharmony_ci
877862306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
877962306a36Sopenharmony_ci					 arvif->bssid);
878062306a36Sopenharmony_ci		if (ret) {
878162306a36Sopenharmony_ci			ath10k_warn(ar, "failed to bring vdev up %d: %d\n",
878262306a36Sopenharmony_ci				    arvif->vdev_id, ret);
878362306a36Sopenharmony_ci			continue;
878462306a36Sopenharmony_ci		}
878562306a36Sopenharmony_ci	}
878662306a36Sopenharmony_ci
878762306a36Sopenharmony_ci	ath10k_monitor_recalc(ar);
878862306a36Sopenharmony_ci}
878962306a36Sopenharmony_ci
879062306a36Sopenharmony_cistatic int
879162306a36Sopenharmony_ciath10k_mac_op_add_chanctx(struct ieee80211_hw *hw,
879262306a36Sopenharmony_ci			  struct ieee80211_chanctx_conf *ctx)
879362306a36Sopenharmony_ci{
879462306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
879562306a36Sopenharmony_ci
879662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
879762306a36Sopenharmony_ci		   "mac chanctx add freq %u width %d ptr %pK\n",
879862306a36Sopenharmony_ci		   ctx->def.chan->center_freq, ctx->def.width, ctx);
879962306a36Sopenharmony_ci
880062306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
880162306a36Sopenharmony_ci
880262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
880362306a36Sopenharmony_ci	ath10k_mac_update_rx_channel(ar, ctx, NULL, 0);
880462306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
880562306a36Sopenharmony_ci
880662306a36Sopenharmony_ci	ath10k_recalc_radar_detection(ar);
880762306a36Sopenharmony_ci	ath10k_monitor_recalc(ar);
880862306a36Sopenharmony_ci
880962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
881062306a36Sopenharmony_ci
881162306a36Sopenharmony_ci	return 0;
881262306a36Sopenharmony_ci}
881362306a36Sopenharmony_ci
881462306a36Sopenharmony_cistatic void
881562306a36Sopenharmony_ciath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw,
881662306a36Sopenharmony_ci			     struct ieee80211_chanctx_conf *ctx)
881762306a36Sopenharmony_ci{
881862306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
881962306a36Sopenharmony_ci
882062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
882162306a36Sopenharmony_ci		   "mac chanctx remove freq %u width %d ptr %pK\n",
882262306a36Sopenharmony_ci		   ctx->def.chan->center_freq, ctx->def.width, ctx);
882362306a36Sopenharmony_ci
882462306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
882562306a36Sopenharmony_ci
882662306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
882762306a36Sopenharmony_ci	ath10k_mac_update_rx_channel(ar, NULL, NULL, 0);
882862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
882962306a36Sopenharmony_ci
883062306a36Sopenharmony_ci	ath10k_recalc_radar_detection(ar);
883162306a36Sopenharmony_ci	ath10k_monitor_recalc(ar);
883262306a36Sopenharmony_ci
883362306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
883462306a36Sopenharmony_ci}
883562306a36Sopenharmony_ci
883662306a36Sopenharmony_cistruct ath10k_mac_change_chanctx_arg {
883762306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *ctx;
883862306a36Sopenharmony_ci	struct ieee80211_vif_chanctx_switch *vifs;
883962306a36Sopenharmony_ci	int n_vifs;
884062306a36Sopenharmony_ci	int next_vif;
884162306a36Sopenharmony_ci};
884262306a36Sopenharmony_ci
884362306a36Sopenharmony_cistatic void
884462306a36Sopenharmony_ciath10k_mac_change_chanctx_cnt_iter(void *data, u8 *mac,
884562306a36Sopenharmony_ci				   struct ieee80211_vif *vif)
884662306a36Sopenharmony_ci{
884762306a36Sopenharmony_ci	struct ath10k_mac_change_chanctx_arg *arg = data;
884862306a36Sopenharmony_ci
884962306a36Sopenharmony_ci	if (rcu_access_pointer(vif->bss_conf.chanctx_conf) != arg->ctx)
885062306a36Sopenharmony_ci		return;
885162306a36Sopenharmony_ci
885262306a36Sopenharmony_ci	arg->n_vifs++;
885362306a36Sopenharmony_ci}
885462306a36Sopenharmony_ci
885562306a36Sopenharmony_cistatic void
885662306a36Sopenharmony_ciath10k_mac_change_chanctx_fill_iter(void *data, u8 *mac,
885762306a36Sopenharmony_ci				    struct ieee80211_vif *vif)
885862306a36Sopenharmony_ci{
885962306a36Sopenharmony_ci	struct ath10k_mac_change_chanctx_arg *arg = data;
886062306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *ctx;
886162306a36Sopenharmony_ci
886262306a36Sopenharmony_ci	ctx = rcu_access_pointer(vif->bss_conf.chanctx_conf);
886362306a36Sopenharmony_ci	if (ctx != arg->ctx)
886462306a36Sopenharmony_ci		return;
886562306a36Sopenharmony_ci
886662306a36Sopenharmony_ci	if (WARN_ON(arg->next_vif == arg->n_vifs))
886762306a36Sopenharmony_ci		return;
886862306a36Sopenharmony_ci
886962306a36Sopenharmony_ci	arg->vifs[arg->next_vif].vif = vif;
887062306a36Sopenharmony_ci	arg->vifs[arg->next_vif].old_ctx = ctx;
887162306a36Sopenharmony_ci	arg->vifs[arg->next_vif].new_ctx = ctx;
887262306a36Sopenharmony_ci	arg->next_vif++;
887362306a36Sopenharmony_ci}
887462306a36Sopenharmony_ci
887562306a36Sopenharmony_cistatic void
887662306a36Sopenharmony_ciath10k_mac_op_change_chanctx(struct ieee80211_hw *hw,
887762306a36Sopenharmony_ci			     struct ieee80211_chanctx_conf *ctx,
887862306a36Sopenharmony_ci			     u32 changed)
887962306a36Sopenharmony_ci{
888062306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
888162306a36Sopenharmony_ci	struct ath10k_mac_change_chanctx_arg arg = { .ctx = ctx };
888262306a36Sopenharmony_ci
888362306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
888462306a36Sopenharmony_ci
888562306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
888662306a36Sopenharmony_ci		   "mac chanctx change freq %u width %d ptr %pK changed %x\n",
888762306a36Sopenharmony_ci		   ctx->def.chan->center_freq, ctx->def.width, ctx, changed);
888862306a36Sopenharmony_ci
888962306a36Sopenharmony_ci	/* This shouldn't really happen because channel switching should use
889062306a36Sopenharmony_ci	 * switch_vif_chanctx().
889162306a36Sopenharmony_ci	 */
889262306a36Sopenharmony_ci	if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL))
889362306a36Sopenharmony_ci		goto unlock;
889462306a36Sopenharmony_ci
889562306a36Sopenharmony_ci	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
889662306a36Sopenharmony_ci		ieee80211_iterate_active_interfaces_atomic(
889762306a36Sopenharmony_ci					hw,
889862306a36Sopenharmony_ci					ATH10K_ITER_NORMAL_FLAGS,
889962306a36Sopenharmony_ci					ath10k_mac_change_chanctx_cnt_iter,
890062306a36Sopenharmony_ci					&arg);
890162306a36Sopenharmony_ci		if (arg.n_vifs == 0)
890262306a36Sopenharmony_ci			goto radar;
890362306a36Sopenharmony_ci
890462306a36Sopenharmony_ci		arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]),
890562306a36Sopenharmony_ci				   GFP_KERNEL);
890662306a36Sopenharmony_ci		if (!arg.vifs)
890762306a36Sopenharmony_ci			goto radar;
890862306a36Sopenharmony_ci
890962306a36Sopenharmony_ci		ieee80211_iterate_active_interfaces_atomic(
891062306a36Sopenharmony_ci					hw,
891162306a36Sopenharmony_ci					ATH10K_ITER_NORMAL_FLAGS,
891262306a36Sopenharmony_ci					ath10k_mac_change_chanctx_fill_iter,
891362306a36Sopenharmony_ci					&arg);
891462306a36Sopenharmony_ci		ath10k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs);
891562306a36Sopenharmony_ci		kfree(arg.vifs);
891662306a36Sopenharmony_ci	}
891762306a36Sopenharmony_ci
891862306a36Sopenharmony_ciradar:
891962306a36Sopenharmony_ci	ath10k_recalc_radar_detection(ar);
892062306a36Sopenharmony_ci
892162306a36Sopenharmony_ci	/* FIXME: How to configure Rx chains properly? */
892262306a36Sopenharmony_ci
892362306a36Sopenharmony_ci	/* No other actions are actually necessary. Firmware maintains channel
892462306a36Sopenharmony_ci	 * definitions per vdev internally and there's no host-side channel
892562306a36Sopenharmony_ci	 * context abstraction to configure, e.g. channel width.
892662306a36Sopenharmony_ci	 */
892762306a36Sopenharmony_ci
892862306a36Sopenharmony_ciunlock:
892962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
893062306a36Sopenharmony_ci}
893162306a36Sopenharmony_ci
893262306a36Sopenharmony_cistatic int
893362306a36Sopenharmony_ciath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
893462306a36Sopenharmony_ci				 struct ieee80211_vif *vif,
893562306a36Sopenharmony_ci				 struct ieee80211_bss_conf *link_conf,
893662306a36Sopenharmony_ci				 struct ieee80211_chanctx_conf *ctx)
893762306a36Sopenharmony_ci{
893862306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
893962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
894062306a36Sopenharmony_ci	int ret;
894162306a36Sopenharmony_ci
894262306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
894362306a36Sopenharmony_ci
894462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
894562306a36Sopenharmony_ci		   "mac chanctx assign ptr %pK vdev_id %i\n",
894662306a36Sopenharmony_ci		   ctx, arvif->vdev_id);
894762306a36Sopenharmony_ci
894862306a36Sopenharmony_ci	if (WARN_ON(arvif->is_started)) {
894962306a36Sopenharmony_ci		mutex_unlock(&ar->conf_mutex);
895062306a36Sopenharmony_ci		return -EBUSY;
895162306a36Sopenharmony_ci	}
895262306a36Sopenharmony_ci
895362306a36Sopenharmony_ci	ret = ath10k_vdev_start(arvif, &ctx->def);
895462306a36Sopenharmony_ci	if (ret) {
895562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to start vdev %i addr %pM on freq %d: %d\n",
895662306a36Sopenharmony_ci			    arvif->vdev_id, vif->addr,
895762306a36Sopenharmony_ci			    ctx->def.chan->center_freq, ret);
895862306a36Sopenharmony_ci		goto err;
895962306a36Sopenharmony_ci	}
896062306a36Sopenharmony_ci
896162306a36Sopenharmony_ci	arvif->is_started = true;
896262306a36Sopenharmony_ci
896362306a36Sopenharmony_ci	ret = ath10k_mac_vif_setup_ps(arvif);
896462306a36Sopenharmony_ci	if (ret) {
896562306a36Sopenharmony_ci		ath10k_warn(ar, "failed to update vdev %i ps: %d\n",
896662306a36Sopenharmony_ci			    arvif->vdev_id, ret);
896762306a36Sopenharmony_ci		goto err_stop;
896862306a36Sopenharmony_ci	}
896962306a36Sopenharmony_ci
897062306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR) {
897162306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, 0, vif->addr);
897262306a36Sopenharmony_ci		if (ret) {
897362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to up monitor vdev %i: %d\n",
897462306a36Sopenharmony_ci				    arvif->vdev_id, ret);
897562306a36Sopenharmony_ci			goto err_stop;
897662306a36Sopenharmony_ci		}
897762306a36Sopenharmony_ci
897862306a36Sopenharmony_ci		arvif->is_up = true;
897962306a36Sopenharmony_ci	}
898062306a36Sopenharmony_ci
898162306a36Sopenharmony_ci	if (ath10k_mac_can_set_cts_prot(arvif)) {
898262306a36Sopenharmony_ci		ret = ath10k_mac_set_cts_prot(arvif);
898362306a36Sopenharmony_ci		if (ret)
898462306a36Sopenharmony_ci			ath10k_warn(ar, "failed to set cts protection for vdev %d: %d\n",
898562306a36Sopenharmony_ci				    arvif->vdev_id, ret);
898662306a36Sopenharmony_ci	}
898762306a36Sopenharmony_ci
898862306a36Sopenharmony_ci	if (ath10k_peer_stats_enabled(ar) &&
898962306a36Sopenharmony_ci	    ar->hw_params.tx_stats_over_pktlog) {
899062306a36Sopenharmony_ci		ar->pktlog_filter |= ATH10K_PKTLOG_PEER_STATS;
899162306a36Sopenharmony_ci		ret = ath10k_wmi_pdev_pktlog_enable(ar,
899262306a36Sopenharmony_ci						    ar->pktlog_filter);
899362306a36Sopenharmony_ci		if (ret) {
899462306a36Sopenharmony_ci			ath10k_warn(ar, "failed to enable pktlog %d\n", ret);
899562306a36Sopenharmony_ci			goto err_stop;
899662306a36Sopenharmony_ci		}
899762306a36Sopenharmony_ci	}
899862306a36Sopenharmony_ci
899962306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
900062306a36Sopenharmony_ci	return 0;
900162306a36Sopenharmony_ci
900262306a36Sopenharmony_cierr_stop:
900362306a36Sopenharmony_ci	ath10k_vdev_stop(arvif);
900462306a36Sopenharmony_ci	arvif->is_started = false;
900562306a36Sopenharmony_ci	ath10k_mac_vif_setup_ps(arvif);
900662306a36Sopenharmony_ci
900762306a36Sopenharmony_cierr:
900862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
900962306a36Sopenharmony_ci	return ret;
901062306a36Sopenharmony_ci}
901162306a36Sopenharmony_ci
901262306a36Sopenharmony_cistatic void
901362306a36Sopenharmony_ciath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
901462306a36Sopenharmony_ci				   struct ieee80211_vif *vif,
901562306a36Sopenharmony_ci				   struct ieee80211_bss_conf *link_conf,
901662306a36Sopenharmony_ci				   struct ieee80211_chanctx_conf *ctx)
901762306a36Sopenharmony_ci{
901862306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
901962306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
902062306a36Sopenharmony_ci	int ret;
902162306a36Sopenharmony_ci
902262306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
902362306a36Sopenharmony_ci
902462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
902562306a36Sopenharmony_ci		   "mac chanctx unassign ptr %pK vdev_id %i\n",
902662306a36Sopenharmony_ci		   ctx, arvif->vdev_id);
902762306a36Sopenharmony_ci
902862306a36Sopenharmony_ci	WARN_ON(!arvif->is_started);
902962306a36Sopenharmony_ci
903062306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR) {
903162306a36Sopenharmony_ci		WARN_ON(!arvif->is_up);
903262306a36Sopenharmony_ci
903362306a36Sopenharmony_ci		ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
903462306a36Sopenharmony_ci		if (ret)
903562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to down monitor vdev %i: %d\n",
903662306a36Sopenharmony_ci				    arvif->vdev_id, ret);
903762306a36Sopenharmony_ci
903862306a36Sopenharmony_ci		arvif->is_up = false;
903962306a36Sopenharmony_ci	}
904062306a36Sopenharmony_ci
904162306a36Sopenharmony_ci	ret = ath10k_vdev_stop(arvif);
904262306a36Sopenharmony_ci	if (ret)
904362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to stop vdev %i: %d\n",
904462306a36Sopenharmony_ci			    arvif->vdev_id, ret);
904562306a36Sopenharmony_ci
904662306a36Sopenharmony_ci	arvif->is_started = false;
904762306a36Sopenharmony_ci
904862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
904962306a36Sopenharmony_ci}
905062306a36Sopenharmony_ci
905162306a36Sopenharmony_cistatic int
905262306a36Sopenharmony_ciath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
905362306a36Sopenharmony_ci				 struct ieee80211_vif_chanctx_switch *vifs,
905462306a36Sopenharmony_ci				 int n_vifs,
905562306a36Sopenharmony_ci				 enum ieee80211_chanctx_switch_mode mode)
905662306a36Sopenharmony_ci{
905762306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
905862306a36Sopenharmony_ci
905962306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
906062306a36Sopenharmony_ci
906162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
906262306a36Sopenharmony_ci		   "mac chanctx switch n_vifs %d mode %d\n",
906362306a36Sopenharmony_ci		   n_vifs, mode);
906462306a36Sopenharmony_ci	ath10k_mac_update_vif_chan(ar, vifs, n_vifs);
906562306a36Sopenharmony_ci
906662306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
906762306a36Sopenharmony_ci	return 0;
906862306a36Sopenharmony_ci}
906962306a36Sopenharmony_ci
907062306a36Sopenharmony_cistatic void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
907162306a36Sopenharmony_ci					     struct ieee80211_vif *vif,
907262306a36Sopenharmony_ci					     struct ieee80211_sta *sta)
907362306a36Sopenharmony_ci{
907462306a36Sopenharmony_ci	struct ath10k *ar;
907562306a36Sopenharmony_ci	struct ath10k_peer *peer;
907662306a36Sopenharmony_ci
907762306a36Sopenharmony_ci	ar = hw->priv;
907862306a36Sopenharmony_ci
907962306a36Sopenharmony_ci	list_for_each_entry(peer, &ar->peers, list)
908062306a36Sopenharmony_ci		if (peer->sta == sta)
908162306a36Sopenharmony_ci			peer->removed = true;
908262306a36Sopenharmony_ci}
908362306a36Sopenharmony_ci
908462306a36Sopenharmony_ci/* HT MCS parameters with Nss = 1 */
908562306a36Sopenharmony_cistatic const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss1[] = {
908662306a36Sopenharmony_ci	/* MCS  L20   L40   S20  S40 */
908762306a36Sopenharmony_ci	{0,  { 65,  135,  72,  150} },
908862306a36Sopenharmony_ci	{1,  { 130, 270,  144, 300} },
908962306a36Sopenharmony_ci	{2,  { 195, 405,  217, 450} },
909062306a36Sopenharmony_ci	{3,  { 260, 540,  289, 600} },
909162306a36Sopenharmony_ci	{4,  { 390, 810,  433, 900} },
909262306a36Sopenharmony_ci	{5,  { 520, 1080, 578, 1200} },
909362306a36Sopenharmony_ci	{6,  { 585, 1215, 650, 1350} },
909462306a36Sopenharmony_ci	{7,  { 650, 1350, 722, 1500} }
909562306a36Sopenharmony_ci};
909662306a36Sopenharmony_ci
909762306a36Sopenharmony_ci/* HT MCS parameters with Nss = 2 */
909862306a36Sopenharmony_cistatic const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss2[] = {
909962306a36Sopenharmony_ci	/* MCS  L20    L40   S20   S40 */
910062306a36Sopenharmony_ci	{0,  {130,  270,  144,  300} },
910162306a36Sopenharmony_ci	{1,  {260,  540,  289,  600} },
910262306a36Sopenharmony_ci	{2,  {390,  810,  433,  900} },
910362306a36Sopenharmony_ci	{3,  {520,  1080, 578,  1200} },
910462306a36Sopenharmony_ci	{4,  {780,  1620, 867,  1800} },
910562306a36Sopenharmony_ci	{5,  {1040, 2160, 1156, 2400} },
910662306a36Sopenharmony_ci	{6,  {1170, 2430, 1300, 2700} },
910762306a36Sopenharmony_ci	{7,  {1300, 2700, 1444, 3000} }
910862306a36Sopenharmony_ci};
910962306a36Sopenharmony_ci
911062306a36Sopenharmony_ci/* MCS parameters with Nss = 1 */
911162306a36Sopenharmony_cistatic const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = {
911262306a36Sopenharmony_ci	/* MCS  L80    S80     L40   S40    L20   S20 */
911362306a36Sopenharmony_ci	{0,  {293,  325},  {135,  150},  {65,   72} },
911462306a36Sopenharmony_ci	{1,  {585,  650},  {270,  300},  {130,  144} },
911562306a36Sopenharmony_ci	{2,  {878,  975},  {405,  450},  {195,  217} },
911662306a36Sopenharmony_ci	{3,  {1170, 1300}, {540,  600},  {260,  289} },
911762306a36Sopenharmony_ci	{4,  {1755, 1950}, {810,  900},  {390,  433} },
911862306a36Sopenharmony_ci	{5,  {2340, 2600}, {1080, 1200}, {520,  578} },
911962306a36Sopenharmony_ci	{6,  {2633, 2925}, {1215, 1350}, {585,  650} },
912062306a36Sopenharmony_ci	{7,  {2925, 3250}, {1350, 1500}, {650,  722} },
912162306a36Sopenharmony_ci	{8,  {3510, 3900}, {1620, 1800}, {780,  867} },
912262306a36Sopenharmony_ci	{9,  {3900, 4333}, {1800, 2000}, {780,  867} }
912362306a36Sopenharmony_ci};
912462306a36Sopenharmony_ci
912562306a36Sopenharmony_ci/*MCS parameters with Nss = 2 */
912662306a36Sopenharmony_cistatic const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = {
912762306a36Sopenharmony_ci	/* MCS  L80    S80     L40   S40    L20   S20 */
912862306a36Sopenharmony_ci	{0,  {585,  650},  {270,  300},  {130,  144} },
912962306a36Sopenharmony_ci	{1,  {1170, 1300}, {540,  600},  {260,  289} },
913062306a36Sopenharmony_ci	{2,  {1755, 1950}, {810,  900},  {390,  433} },
913162306a36Sopenharmony_ci	{3,  {2340, 2600}, {1080, 1200}, {520,  578} },
913262306a36Sopenharmony_ci	{4,  {3510, 3900}, {1620, 1800}, {780,  867} },
913362306a36Sopenharmony_ci	{5,  {4680, 5200}, {2160, 2400}, {1040, 1156} },
913462306a36Sopenharmony_ci	{6,  {5265, 5850}, {2430, 2700}, {1170, 1300} },
913562306a36Sopenharmony_ci	{7,  {5850, 6500}, {2700, 3000}, {1300, 1444} },
913662306a36Sopenharmony_ci	{8,  {7020, 7800}, {3240, 3600}, {1560, 1733} },
913762306a36Sopenharmony_ci	{9,  {7800, 8667}, {3600, 4000}, {1560, 1733} }
913862306a36Sopenharmony_ci};
913962306a36Sopenharmony_ci
914062306a36Sopenharmony_cistatic void ath10k_mac_get_rate_flags_ht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
914162306a36Sopenharmony_ci					 u8 *flags, u8 *bw)
914262306a36Sopenharmony_ci{
914362306a36Sopenharmony_ci	struct ath10k_index_ht_data_rate_type *mcs_rate;
914462306a36Sopenharmony_ci	u8 index;
914562306a36Sopenharmony_ci	size_t len_nss1 = ARRAY_SIZE(supported_ht_mcs_rate_nss1);
914662306a36Sopenharmony_ci	size_t len_nss2 = ARRAY_SIZE(supported_ht_mcs_rate_nss2);
914762306a36Sopenharmony_ci
914862306a36Sopenharmony_ci	if (mcs >= (len_nss1 + len_nss2)) {
914962306a36Sopenharmony_ci		ath10k_warn(ar, "not supported mcs %d in current rate table", mcs);
915062306a36Sopenharmony_ci		return;
915162306a36Sopenharmony_ci	}
915262306a36Sopenharmony_ci
915362306a36Sopenharmony_ci	mcs_rate = (struct ath10k_index_ht_data_rate_type *)
915462306a36Sopenharmony_ci		   ((nss == 1) ? &supported_ht_mcs_rate_nss1 :
915562306a36Sopenharmony_ci		   &supported_ht_mcs_rate_nss2);
915662306a36Sopenharmony_ci
915762306a36Sopenharmony_ci	if (mcs >= len_nss1)
915862306a36Sopenharmony_ci		index = mcs - len_nss1;
915962306a36Sopenharmony_ci	else
916062306a36Sopenharmony_ci		index = mcs;
916162306a36Sopenharmony_ci
916262306a36Sopenharmony_ci	if (rate == mcs_rate[index].supported_rate[0]) {
916362306a36Sopenharmony_ci		*bw = RATE_INFO_BW_20;
916462306a36Sopenharmony_ci	} else if (rate == mcs_rate[index].supported_rate[1]) {
916562306a36Sopenharmony_ci		*bw |= RATE_INFO_BW_40;
916662306a36Sopenharmony_ci	} else if (rate == mcs_rate[index].supported_rate[2]) {
916762306a36Sopenharmony_ci		*bw |= RATE_INFO_BW_20;
916862306a36Sopenharmony_ci		*flags |= RATE_INFO_FLAGS_SHORT_GI;
916962306a36Sopenharmony_ci	} else if (rate == mcs_rate[index].supported_rate[3]) {
917062306a36Sopenharmony_ci		*bw |= RATE_INFO_BW_40;
917162306a36Sopenharmony_ci		*flags |= RATE_INFO_FLAGS_SHORT_GI;
917262306a36Sopenharmony_ci	} else {
917362306a36Sopenharmony_ci		ath10k_warn(ar, "invalid ht params rate %d 100kbps nss %d mcs %d",
917462306a36Sopenharmony_ci			    rate, nss, mcs);
917562306a36Sopenharmony_ci	}
917662306a36Sopenharmony_ci}
917762306a36Sopenharmony_ci
917862306a36Sopenharmony_cistatic void ath10k_mac_get_rate_flags_vht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
917962306a36Sopenharmony_ci					  u8 *flags, u8 *bw)
918062306a36Sopenharmony_ci{
918162306a36Sopenharmony_ci	struct ath10k_index_vht_data_rate_type *mcs_rate;
918262306a36Sopenharmony_ci
918362306a36Sopenharmony_ci	mcs_rate = (struct ath10k_index_vht_data_rate_type *)
918462306a36Sopenharmony_ci		   ((nss == 1) ? &supported_vht_mcs_rate_nss1 :
918562306a36Sopenharmony_ci		   &supported_vht_mcs_rate_nss2);
918662306a36Sopenharmony_ci
918762306a36Sopenharmony_ci	if (rate == mcs_rate[mcs].supported_VHT80_rate[0]) {
918862306a36Sopenharmony_ci		*bw = RATE_INFO_BW_80;
918962306a36Sopenharmony_ci	} else if (rate == mcs_rate[mcs].supported_VHT80_rate[1]) {
919062306a36Sopenharmony_ci		*bw = RATE_INFO_BW_80;
919162306a36Sopenharmony_ci		*flags |= RATE_INFO_FLAGS_SHORT_GI;
919262306a36Sopenharmony_ci	} else if (rate == mcs_rate[mcs].supported_VHT40_rate[0]) {
919362306a36Sopenharmony_ci		*bw = RATE_INFO_BW_40;
919462306a36Sopenharmony_ci	} else if (rate == mcs_rate[mcs].supported_VHT40_rate[1]) {
919562306a36Sopenharmony_ci		*bw = RATE_INFO_BW_40;
919662306a36Sopenharmony_ci		*flags |= RATE_INFO_FLAGS_SHORT_GI;
919762306a36Sopenharmony_ci	} else if (rate == mcs_rate[mcs].supported_VHT20_rate[0]) {
919862306a36Sopenharmony_ci		*bw = RATE_INFO_BW_20;
919962306a36Sopenharmony_ci	} else if (rate == mcs_rate[mcs].supported_VHT20_rate[1]) {
920062306a36Sopenharmony_ci		*bw = RATE_INFO_BW_20;
920162306a36Sopenharmony_ci		*flags |= RATE_INFO_FLAGS_SHORT_GI;
920262306a36Sopenharmony_ci	} else {
920362306a36Sopenharmony_ci		ath10k_warn(ar, "invalid vht params rate %d 100kbps nss %d mcs %d",
920462306a36Sopenharmony_ci			    rate, nss, mcs);
920562306a36Sopenharmony_ci	}
920662306a36Sopenharmony_ci}
920762306a36Sopenharmony_ci
920862306a36Sopenharmony_cistatic void ath10k_mac_get_rate_flags(struct ath10k *ar, u32 rate,
920962306a36Sopenharmony_ci				      enum ath10k_phy_mode mode, u8 nss, u8 mcs,
921062306a36Sopenharmony_ci				      u8 *flags, u8 *bw)
921162306a36Sopenharmony_ci{
921262306a36Sopenharmony_ci	if (mode == ATH10K_PHY_MODE_HT) {
921362306a36Sopenharmony_ci		*flags = RATE_INFO_FLAGS_MCS;
921462306a36Sopenharmony_ci		ath10k_mac_get_rate_flags_ht(ar, rate, nss, mcs, flags, bw);
921562306a36Sopenharmony_ci	} else if (mode == ATH10K_PHY_MODE_VHT) {
921662306a36Sopenharmony_ci		*flags = RATE_INFO_FLAGS_VHT_MCS;
921762306a36Sopenharmony_ci		ath10k_mac_get_rate_flags_vht(ar, rate, nss, mcs, flags, bw);
921862306a36Sopenharmony_ci	}
921962306a36Sopenharmony_ci}
922062306a36Sopenharmony_ci
922162306a36Sopenharmony_cistatic void ath10k_mac_parse_bitrate(struct ath10k *ar, u32 rate_code,
922262306a36Sopenharmony_ci				     u32 bitrate_kbps, struct rate_info *rate)
922362306a36Sopenharmony_ci{
922462306a36Sopenharmony_ci	enum ath10k_phy_mode mode = ATH10K_PHY_MODE_LEGACY;
922562306a36Sopenharmony_ci	enum wmi_rate_preamble preamble = WMI_TLV_GET_HW_RC_PREAM_V1(rate_code);
922662306a36Sopenharmony_ci	u8 nss = WMI_TLV_GET_HW_RC_NSS_V1(rate_code) + 1;
922762306a36Sopenharmony_ci	u8 mcs = WMI_TLV_GET_HW_RC_RATE_V1(rate_code);
922862306a36Sopenharmony_ci	u8 flags = 0, bw = 0;
922962306a36Sopenharmony_ci
923062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC, "mac parse rate code 0x%x bitrate %d kbps\n",
923162306a36Sopenharmony_ci		   rate_code, bitrate_kbps);
923262306a36Sopenharmony_ci
923362306a36Sopenharmony_ci	if (preamble == WMI_RATE_PREAMBLE_HT)
923462306a36Sopenharmony_ci		mode = ATH10K_PHY_MODE_HT;
923562306a36Sopenharmony_ci	else if (preamble == WMI_RATE_PREAMBLE_VHT)
923662306a36Sopenharmony_ci		mode = ATH10K_PHY_MODE_VHT;
923762306a36Sopenharmony_ci
923862306a36Sopenharmony_ci	ath10k_mac_get_rate_flags(ar, bitrate_kbps / 100, mode, nss, mcs, &flags, &bw);
923962306a36Sopenharmony_ci
924062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_MAC,
924162306a36Sopenharmony_ci		   "mac parse bitrate preamble %d mode %d nss %d mcs %d flags %x bw %d\n",
924262306a36Sopenharmony_ci		   preamble, mode, nss, mcs, flags, bw);
924362306a36Sopenharmony_ci
924462306a36Sopenharmony_ci	rate->flags = flags;
924562306a36Sopenharmony_ci	rate->bw = bw;
924662306a36Sopenharmony_ci	rate->legacy = bitrate_kbps / 100;
924762306a36Sopenharmony_ci	rate->nss = nss;
924862306a36Sopenharmony_ci	rate->mcs = mcs;
924962306a36Sopenharmony_ci}
925062306a36Sopenharmony_ci
925162306a36Sopenharmony_cistatic void ath10k_mac_sta_get_peer_stats_info(struct ath10k *ar,
925262306a36Sopenharmony_ci					       struct ieee80211_sta *sta,
925362306a36Sopenharmony_ci					       struct station_info *sinfo)
925462306a36Sopenharmony_ci{
925562306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
925662306a36Sopenharmony_ci	struct ath10k_peer *peer;
925762306a36Sopenharmony_ci	unsigned long time_left;
925862306a36Sopenharmony_ci	int ret;
925962306a36Sopenharmony_ci
926062306a36Sopenharmony_ci	if (!(ar->hw_params.supports_peer_stats_info &&
926162306a36Sopenharmony_ci	      arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA))
926262306a36Sopenharmony_ci		return;
926362306a36Sopenharmony_ci
926462306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
926562306a36Sopenharmony_ci	peer = ath10k_peer_find(ar, arsta->arvif->vdev_id, sta->addr);
926662306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
926762306a36Sopenharmony_ci	if (!peer)
926862306a36Sopenharmony_ci		return;
926962306a36Sopenharmony_ci
927062306a36Sopenharmony_ci	reinit_completion(&ar->peer_stats_info_complete);
927162306a36Sopenharmony_ci
927262306a36Sopenharmony_ci	ret = ath10k_wmi_request_peer_stats_info(ar,
927362306a36Sopenharmony_ci						 arsta->arvif->vdev_id,
927462306a36Sopenharmony_ci						 WMI_REQUEST_ONE_PEER_STATS_INFO,
927562306a36Sopenharmony_ci						 arsta->arvif->bssid,
927662306a36Sopenharmony_ci						 0);
927762306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP) {
927862306a36Sopenharmony_ci		ath10k_warn(ar, "could not request peer stats info: %d\n", ret);
927962306a36Sopenharmony_ci		return;
928062306a36Sopenharmony_ci	}
928162306a36Sopenharmony_ci
928262306a36Sopenharmony_ci	time_left = wait_for_completion_timeout(&ar->peer_stats_info_complete, 3 * HZ);
928362306a36Sopenharmony_ci	if (time_left == 0) {
928462306a36Sopenharmony_ci		ath10k_warn(ar, "timed out waiting peer stats info\n");
928562306a36Sopenharmony_ci		return;
928662306a36Sopenharmony_ci	}
928762306a36Sopenharmony_ci
928862306a36Sopenharmony_ci	if (arsta->rx_rate_code != 0 && arsta->rx_bitrate_kbps != 0) {
928962306a36Sopenharmony_ci		ath10k_mac_parse_bitrate(ar, arsta->rx_rate_code,
929062306a36Sopenharmony_ci					 arsta->rx_bitrate_kbps,
929162306a36Sopenharmony_ci					 &sinfo->rxrate);
929262306a36Sopenharmony_ci
929362306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
929462306a36Sopenharmony_ci		arsta->rx_rate_code = 0;
929562306a36Sopenharmony_ci		arsta->rx_bitrate_kbps = 0;
929662306a36Sopenharmony_ci	}
929762306a36Sopenharmony_ci
929862306a36Sopenharmony_ci	if (arsta->tx_rate_code != 0 && arsta->tx_bitrate_kbps != 0) {
929962306a36Sopenharmony_ci		ath10k_mac_parse_bitrate(ar, arsta->tx_rate_code,
930062306a36Sopenharmony_ci					 arsta->tx_bitrate_kbps,
930162306a36Sopenharmony_ci					 &sinfo->txrate);
930262306a36Sopenharmony_ci
930362306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
930462306a36Sopenharmony_ci		arsta->tx_rate_code = 0;
930562306a36Sopenharmony_ci		arsta->tx_bitrate_kbps = 0;
930662306a36Sopenharmony_ci	}
930762306a36Sopenharmony_ci}
930862306a36Sopenharmony_ci
930962306a36Sopenharmony_cistatic void ath10k_sta_statistics(struct ieee80211_hw *hw,
931062306a36Sopenharmony_ci				  struct ieee80211_vif *vif,
931162306a36Sopenharmony_ci				  struct ieee80211_sta *sta,
931262306a36Sopenharmony_ci				  struct station_info *sinfo)
931362306a36Sopenharmony_ci{
931462306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
931562306a36Sopenharmony_ci	struct ath10k *ar = arsta->arvif->ar;
931662306a36Sopenharmony_ci
931762306a36Sopenharmony_ci	if (!ath10k_peer_stats_enabled(ar))
931862306a36Sopenharmony_ci		return;
931962306a36Sopenharmony_ci
932062306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
932162306a36Sopenharmony_ci	ath10k_debug_fw_stats_request(ar);
932262306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
932362306a36Sopenharmony_ci
932462306a36Sopenharmony_ci	sinfo->rx_duration = arsta->rx_duration;
932562306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
932662306a36Sopenharmony_ci
932762306a36Sopenharmony_ci	if (arsta->txrate.legacy || arsta->txrate.nss) {
932862306a36Sopenharmony_ci		if (arsta->txrate.legacy) {
932962306a36Sopenharmony_ci			sinfo->txrate.legacy = arsta->txrate.legacy;
933062306a36Sopenharmony_ci		} else {
933162306a36Sopenharmony_ci			sinfo->txrate.mcs = arsta->txrate.mcs;
933262306a36Sopenharmony_ci			sinfo->txrate.nss = arsta->txrate.nss;
933362306a36Sopenharmony_ci			sinfo->txrate.bw = arsta->txrate.bw;
933462306a36Sopenharmony_ci		}
933562306a36Sopenharmony_ci		sinfo->txrate.flags = arsta->txrate.flags;
933662306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
933762306a36Sopenharmony_ci	}
933862306a36Sopenharmony_ci
933962306a36Sopenharmony_ci	if (ar->htt.disable_tx_comp) {
934062306a36Sopenharmony_ci		sinfo->tx_failed = arsta->tx_failed;
934162306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
934262306a36Sopenharmony_ci	}
934362306a36Sopenharmony_ci
934462306a36Sopenharmony_ci	sinfo->tx_retries = arsta->tx_retries;
934562306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
934662306a36Sopenharmony_ci
934762306a36Sopenharmony_ci	ath10k_mac_sta_get_peer_stats_info(ar, sta, sinfo);
934862306a36Sopenharmony_ci}
934962306a36Sopenharmony_ci
935062306a36Sopenharmony_cistatic int ath10k_mac_op_set_tid_config(struct ieee80211_hw *hw,
935162306a36Sopenharmony_ci					struct ieee80211_vif *vif,
935262306a36Sopenharmony_ci					struct ieee80211_sta *sta,
935362306a36Sopenharmony_ci					struct cfg80211_tid_config *tid_config)
935462306a36Sopenharmony_ci{
935562306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
935662306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
935762306a36Sopenharmony_ci	struct ath10k_mac_iter_tid_conf_data data = {};
935862306a36Sopenharmony_ci	struct wmi_per_peer_per_tid_cfg_arg arg = {};
935962306a36Sopenharmony_ci	int ret, i;
936062306a36Sopenharmony_ci
936162306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
936262306a36Sopenharmony_ci	arg.vdev_id = arvif->vdev_id;
936362306a36Sopenharmony_ci
936462306a36Sopenharmony_ci	arvif->tids_rst = 0;
936562306a36Sopenharmony_ci	memset(arvif->tid_conf_changed, 0, sizeof(arvif->tid_conf_changed));
936662306a36Sopenharmony_ci
936762306a36Sopenharmony_ci	for (i = 0; i < tid_config->n_tid_conf; i++) {
936862306a36Sopenharmony_ci		ret = ath10k_mac_parse_tid_config(ar, sta, vif,
936962306a36Sopenharmony_ci						  &tid_config->tid_conf[i],
937062306a36Sopenharmony_ci						  &arg);
937162306a36Sopenharmony_ci		if (ret)
937262306a36Sopenharmony_ci			goto exit;
937362306a36Sopenharmony_ci	}
937462306a36Sopenharmony_ci
937562306a36Sopenharmony_ci	ret = 0;
937662306a36Sopenharmony_ci
937762306a36Sopenharmony_ci	if (sta)
937862306a36Sopenharmony_ci		goto exit;
937962306a36Sopenharmony_ci
938062306a36Sopenharmony_ci	arvif->tids_rst = 0;
938162306a36Sopenharmony_ci	data.curr_vif = vif;
938262306a36Sopenharmony_ci	data.ar = ar;
938362306a36Sopenharmony_ci
938462306a36Sopenharmony_ci	ieee80211_iterate_stations_atomic(hw, ath10k_mac_vif_stations_tid_conf,
938562306a36Sopenharmony_ci					  &data);
938662306a36Sopenharmony_ci
938762306a36Sopenharmony_ciexit:
938862306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
938962306a36Sopenharmony_ci	return ret;
939062306a36Sopenharmony_ci}
939162306a36Sopenharmony_ci
939262306a36Sopenharmony_cistatic int ath10k_mac_op_reset_tid_config(struct ieee80211_hw *hw,
939362306a36Sopenharmony_ci					  struct ieee80211_vif *vif,
939462306a36Sopenharmony_ci					  struct ieee80211_sta *sta,
939562306a36Sopenharmony_ci					  u8 tids)
939662306a36Sopenharmony_ci{
939762306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
939862306a36Sopenharmony_ci	struct ath10k_mac_iter_tid_conf_data data = {};
939962306a36Sopenharmony_ci	struct ath10k *ar = hw->priv;
940062306a36Sopenharmony_ci	int ret = 0;
940162306a36Sopenharmony_ci
940262306a36Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
940362306a36Sopenharmony_ci
940462306a36Sopenharmony_ci	if (sta) {
940562306a36Sopenharmony_ci		arvif->tids_rst = 0;
940662306a36Sopenharmony_ci		ret = ath10k_mac_reset_tid_config(ar, sta, arvif, tids);
940762306a36Sopenharmony_ci		goto exit;
940862306a36Sopenharmony_ci	}
940962306a36Sopenharmony_ci
941062306a36Sopenharmony_ci	arvif->tids_rst = tids;
941162306a36Sopenharmony_ci	data.curr_vif = vif;
941262306a36Sopenharmony_ci	data.ar = ar;
941362306a36Sopenharmony_ci	ieee80211_iterate_stations_atomic(hw, ath10k_mac_vif_stations_tid_conf,
941462306a36Sopenharmony_ci					  &data);
941562306a36Sopenharmony_ci
941662306a36Sopenharmony_ciexit:
941762306a36Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
941862306a36Sopenharmony_ci	return ret;
941962306a36Sopenharmony_ci}
942062306a36Sopenharmony_ci
942162306a36Sopenharmony_cistatic const struct ieee80211_ops ath10k_ops = {
942262306a36Sopenharmony_ci	.tx				= ath10k_mac_op_tx,
942362306a36Sopenharmony_ci	.wake_tx_queue			= ath10k_mac_op_wake_tx_queue,
942462306a36Sopenharmony_ci	.start				= ath10k_start,
942562306a36Sopenharmony_ci	.stop				= ath10k_stop,
942662306a36Sopenharmony_ci	.config				= ath10k_config,
942762306a36Sopenharmony_ci	.add_interface			= ath10k_add_interface,
942862306a36Sopenharmony_ci	.update_vif_offload		= ath10k_update_vif_offload,
942962306a36Sopenharmony_ci	.remove_interface		= ath10k_remove_interface,
943062306a36Sopenharmony_ci	.configure_filter		= ath10k_configure_filter,
943162306a36Sopenharmony_ci	.bss_info_changed		= ath10k_bss_info_changed,
943262306a36Sopenharmony_ci	.set_coverage_class		= ath10k_mac_op_set_coverage_class,
943362306a36Sopenharmony_ci	.hw_scan			= ath10k_hw_scan,
943462306a36Sopenharmony_ci	.cancel_hw_scan			= ath10k_cancel_hw_scan,
943562306a36Sopenharmony_ci	.set_key			= ath10k_set_key,
943662306a36Sopenharmony_ci	.set_default_unicast_key        = ath10k_set_default_unicast_key,
943762306a36Sopenharmony_ci	.sta_state			= ath10k_sta_state,
943862306a36Sopenharmony_ci	.sta_set_txpwr			= ath10k_sta_set_txpwr,
943962306a36Sopenharmony_ci	.conf_tx			= ath10k_conf_tx,
944062306a36Sopenharmony_ci	.remain_on_channel		= ath10k_remain_on_channel,
944162306a36Sopenharmony_ci	.cancel_remain_on_channel	= ath10k_cancel_remain_on_channel,
944262306a36Sopenharmony_ci	.set_rts_threshold		= ath10k_set_rts_threshold,
944362306a36Sopenharmony_ci	.set_frag_threshold		= ath10k_mac_op_set_frag_threshold,
944462306a36Sopenharmony_ci	.flush				= ath10k_flush,
944562306a36Sopenharmony_ci	.tx_last_beacon			= ath10k_tx_last_beacon,
944662306a36Sopenharmony_ci	.set_antenna			= ath10k_set_antenna,
944762306a36Sopenharmony_ci	.get_antenna			= ath10k_get_antenna,
944862306a36Sopenharmony_ci	.reconfig_complete		= ath10k_reconfig_complete,
944962306a36Sopenharmony_ci	.get_survey			= ath10k_get_survey,
945062306a36Sopenharmony_ci	.set_bitrate_mask		= ath10k_mac_op_set_bitrate_mask,
945162306a36Sopenharmony_ci	.sta_rc_update			= ath10k_sta_rc_update,
945262306a36Sopenharmony_ci	.offset_tsf			= ath10k_offset_tsf,
945362306a36Sopenharmony_ci	.ampdu_action			= ath10k_ampdu_action,
945462306a36Sopenharmony_ci	.get_et_sset_count		= ath10k_debug_get_et_sset_count,
945562306a36Sopenharmony_ci	.get_et_stats			= ath10k_debug_get_et_stats,
945662306a36Sopenharmony_ci	.get_et_strings			= ath10k_debug_get_et_strings,
945762306a36Sopenharmony_ci	.add_chanctx			= ath10k_mac_op_add_chanctx,
945862306a36Sopenharmony_ci	.remove_chanctx			= ath10k_mac_op_remove_chanctx,
945962306a36Sopenharmony_ci	.change_chanctx			= ath10k_mac_op_change_chanctx,
946062306a36Sopenharmony_ci	.assign_vif_chanctx		= ath10k_mac_op_assign_vif_chanctx,
946162306a36Sopenharmony_ci	.unassign_vif_chanctx		= ath10k_mac_op_unassign_vif_chanctx,
946262306a36Sopenharmony_ci	.switch_vif_chanctx		= ath10k_mac_op_switch_vif_chanctx,
946362306a36Sopenharmony_ci	.sta_pre_rcu_remove		= ath10k_mac_op_sta_pre_rcu_remove,
946462306a36Sopenharmony_ci	.sta_statistics			= ath10k_sta_statistics,
946562306a36Sopenharmony_ci	.set_tid_config			= ath10k_mac_op_set_tid_config,
946662306a36Sopenharmony_ci	.reset_tid_config		= ath10k_mac_op_reset_tid_config,
946762306a36Sopenharmony_ci
946862306a36Sopenharmony_ci	CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
946962306a36Sopenharmony_ci
947062306a36Sopenharmony_ci#ifdef CONFIG_PM
947162306a36Sopenharmony_ci	.suspend			= ath10k_wow_op_suspend,
947262306a36Sopenharmony_ci	.resume				= ath10k_wow_op_resume,
947362306a36Sopenharmony_ci	.set_wakeup			= ath10k_wow_op_set_wakeup,
947462306a36Sopenharmony_ci#endif
947562306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS
947662306a36Sopenharmony_ci	.sta_add_debugfs		= ath10k_sta_add_debugfs,
947762306a36Sopenharmony_ci#endif
947862306a36Sopenharmony_ci	.set_sar_specs			= ath10k_mac_set_sar_specs,
947962306a36Sopenharmony_ci};
948062306a36Sopenharmony_ci
948162306a36Sopenharmony_ci#define CHAN2G(_channel, _freq, _flags) { \
948262306a36Sopenharmony_ci	.band			= NL80211_BAND_2GHZ, \
948362306a36Sopenharmony_ci	.hw_value		= (_channel), \
948462306a36Sopenharmony_ci	.center_freq		= (_freq), \
948562306a36Sopenharmony_ci	.flags			= (_flags), \
948662306a36Sopenharmony_ci	.max_antenna_gain	= 0, \
948762306a36Sopenharmony_ci	.max_power		= 30, \
948862306a36Sopenharmony_ci}
948962306a36Sopenharmony_ci
949062306a36Sopenharmony_ci#define CHAN5G(_channel, _freq, _flags) { \
949162306a36Sopenharmony_ci	.band			= NL80211_BAND_5GHZ, \
949262306a36Sopenharmony_ci	.hw_value		= (_channel), \
949362306a36Sopenharmony_ci	.center_freq		= (_freq), \
949462306a36Sopenharmony_ci	.flags			= (_flags), \
949562306a36Sopenharmony_ci	.max_antenna_gain	= 0, \
949662306a36Sopenharmony_ci	.max_power		= 30, \
949762306a36Sopenharmony_ci}
949862306a36Sopenharmony_ci
949962306a36Sopenharmony_cistatic const struct ieee80211_channel ath10k_2ghz_channels[] = {
950062306a36Sopenharmony_ci	CHAN2G(1, 2412, 0),
950162306a36Sopenharmony_ci	CHAN2G(2, 2417, 0),
950262306a36Sopenharmony_ci	CHAN2G(3, 2422, 0),
950362306a36Sopenharmony_ci	CHAN2G(4, 2427, 0),
950462306a36Sopenharmony_ci	CHAN2G(5, 2432, 0),
950562306a36Sopenharmony_ci	CHAN2G(6, 2437, 0),
950662306a36Sopenharmony_ci	CHAN2G(7, 2442, 0),
950762306a36Sopenharmony_ci	CHAN2G(8, 2447, 0),
950862306a36Sopenharmony_ci	CHAN2G(9, 2452, 0),
950962306a36Sopenharmony_ci	CHAN2G(10, 2457, 0),
951062306a36Sopenharmony_ci	CHAN2G(11, 2462, 0),
951162306a36Sopenharmony_ci	CHAN2G(12, 2467, 0),
951262306a36Sopenharmony_ci	CHAN2G(13, 2472, 0),
951362306a36Sopenharmony_ci	CHAN2G(14, 2484, 0),
951462306a36Sopenharmony_ci};
951562306a36Sopenharmony_ci
951662306a36Sopenharmony_cistatic const struct ieee80211_channel ath10k_5ghz_channels[] = {
951762306a36Sopenharmony_ci	CHAN5G(36, 5180, 0),
951862306a36Sopenharmony_ci	CHAN5G(40, 5200, 0),
951962306a36Sopenharmony_ci	CHAN5G(44, 5220, 0),
952062306a36Sopenharmony_ci	CHAN5G(48, 5240, 0),
952162306a36Sopenharmony_ci	CHAN5G(52, 5260, 0),
952262306a36Sopenharmony_ci	CHAN5G(56, 5280, 0),
952362306a36Sopenharmony_ci	CHAN5G(60, 5300, 0),
952462306a36Sopenharmony_ci	CHAN5G(64, 5320, 0),
952562306a36Sopenharmony_ci	CHAN5G(100, 5500, 0),
952662306a36Sopenharmony_ci	CHAN5G(104, 5520, 0),
952762306a36Sopenharmony_ci	CHAN5G(108, 5540, 0),
952862306a36Sopenharmony_ci	CHAN5G(112, 5560, 0),
952962306a36Sopenharmony_ci	CHAN5G(116, 5580, 0),
953062306a36Sopenharmony_ci	CHAN5G(120, 5600, 0),
953162306a36Sopenharmony_ci	CHAN5G(124, 5620, 0),
953262306a36Sopenharmony_ci	CHAN5G(128, 5640, 0),
953362306a36Sopenharmony_ci	CHAN5G(132, 5660, 0),
953462306a36Sopenharmony_ci	CHAN5G(136, 5680, 0),
953562306a36Sopenharmony_ci	CHAN5G(140, 5700, 0),
953662306a36Sopenharmony_ci	CHAN5G(144, 5720, 0),
953762306a36Sopenharmony_ci	CHAN5G(149, 5745, 0),
953862306a36Sopenharmony_ci	CHAN5G(153, 5765, 0),
953962306a36Sopenharmony_ci	CHAN5G(157, 5785, 0),
954062306a36Sopenharmony_ci	CHAN5G(161, 5805, 0),
954162306a36Sopenharmony_ci	CHAN5G(165, 5825, 0),
954262306a36Sopenharmony_ci	CHAN5G(169, 5845, 0),
954362306a36Sopenharmony_ci	CHAN5G(173, 5865, 0),
954462306a36Sopenharmony_ci	/* If you add more, you may need to change ATH10K_MAX_5G_CHAN */
954562306a36Sopenharmony_ci	/* And you will definitely need to change ATH10K_NUM_CHANS in core.h */
954662306a36Sopenharmony_ci};
954762306a36Sopenharmony_ci
954862306a36Sopenharmony_cistruct ath10k *ath10k_mac_create(size_t priv_size)
954962306a36Sopenharmony_ci{
955062306a36Sopenharmony_ci	struct ieee80211_hw *hw;
955162306a36Sopenharmony_ci	struct ieee80211_ops *ops;
955262306a36Sopenharmony_ci	struct ath10k *ar;
955362306a36Sopenharmony_ci
955462306a36Sopenharmony_ci	ops = kmemdup(&ath10k_ops, sizeof(ath10k_ops), GFP_KERNEL);
955562306a36Sopenharmony_ci	if (!ops)
955662306a36Sopenharmony_ci		return NULL;
955762306a36Sopenharmony_ci
955862306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(struct ath10k) + priv_size, ops);
955962306a36Sopenharmony_ci	if (!hw) {
956062306a36Sopenharmony_ci		kfree(ops);
956162306a36Sopenharmony_ci		return NULL;
956262306a36Sopenharmony_ci	}
956362306a36Sopenharmony_ci
956462306a36Sopenharmony_ci	ar = hw->priv;
956562306a36Sopenharmony_ci	ar->hw = hw;
956662306a36Sopenharmony_ci	ar->ops = ops;
956762306a36Sopenharmony_ci
956862306a36Sopenharmony_ci	return ar;
956962306a36Sopenharmony_ci}
957062306a36Sopenharmony_ci
957162306a36Sopenharmony_civoid ath10k_mac_destroy(struct ath10k *ar)
957262306a36Sopenharmony_ci{
957362306a36Sopenharmony_ci	struct ieee80211_ops *ops = ar->ops;
957462306a36Sopenharmony_ci
957562306a36Sopenharmony_ci	ieee80211_free_hw(ar->hw);
957662306a36Sopenharmony_ci	kfree(ops);
957762306a36Sopenharmony_ci}
957862306a36Sopenharmony_ci
957962306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_if_limits[] = {
958062306a36Sopenharmony_ci	{
958162306a36Sopenharmony_ci		.max	= 8,
958262306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_STATION)
958362306a36Sopenharmony_ci			| BIT(NL80211_IFTYPE_P2P_CLIENT)
958462306a36Sopenharmony_ci	},
958562306a36Sopenharmony_ci	{
958662306a36Sopenharmony_ci		.max	= 3,
958762306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_P2P_GO)
958862306a36Sopenharmony_ci	},
958962306a36Sopenharmony_ci	{
959062306a36Sopenharmony_ci		.max	= 1,
959162306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_P2P_DEVICE)
959262306a36Sopenharmony_ci	},
959362306a36Sopenharmony_ci	{
959462306a36Sopenharmony_ci		.max	= 7,
959562306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_AP)
959662306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
959762306a36Sopenharmony_ci			| BIT(NL80211_IFTYPE_MESH_POINT)
959862306a36Sopenharmony_ci#endif
959962306a36Sopenharmony_ci	},
960062306a36Sopenharmony_ci};
960162306a36Sopenharmony_ci
960262306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
960362306a36Sopenharmony_ci	{
960462306a36Sopenharmony_ci		.max	= 8,
960562306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_AP)
960662306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
960762306a36Sopenharmony_ci			| BIT(NL80211_IFTYPE_MESH_POINT)
960862306a36Sopenharmony_ci#endif
960962306a36Sopenharmony_ci	},
961062306a36Sopenharmony_ci	{
961162306a36Sopenharmony_ci		.max	= 1,
961262306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_STATION)
961362306a36Sopenharmony_ci	},
961462306a36Sopenharmony_ci};
961562306a36Sopenharmony_ci
961662306a36Sopenharmony_cistatic const struct ieee80211_iface_combination ath10k_if_comb[] = {
961762306a36Sopenharmony_ci	{
961862306a36Sopenharmony_ci		.limits = ath10k_if_limits,
961962306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_if_limits),
962062306a36Sopenharmony_ci		.max_interfaces = 8,
962162306a36Sopenharmony_ci		.num_different_channels = 1,
962262306a36Sopenharmony_ci		.beacon_int_infra_match = true,
962362306a36Sopenharmony_ci	},
962462306a36Sopenharmony_ci};
962562306a36Sopenharmony_ci
962662306a36Sopenharmony_cistatic const struct ieee80211_iface_combination ath10k_10x_if_comb[] = {
962762306a36Sopenharmony_ci	{
962862306a36Sopenharmony_ci		.limits = ath10k_10x_if_limits,
962962306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_10x_if_limits),
963062306a36Sopenharmony_ci		.max_interfaces = 8,
963162306a36Sopenharmony_ci		.num_different_channels = 1,
963262306a36Sopenharmony_ci		.beacon_int_infra_match = true,
963362306a36Sopenharmony_ci		.beacon_int_min_gcd = 1,
963462306a36Sopenharmony_ci#ifdef CONFIG_ATH10K_DFS_CERTIFIED
963562306a36Sopenharmony_ci		.radar_detect_widths =	BIT(NL80211_CHAN_WIDTH_20_NOHT) |
963662306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_20) |
963762306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_40) |
963862306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_80),
963962306a36Sopenharmony_ci#endif
964062306a36Sopenharmony_ci	},
964162306a36Sopenharmony_ci};
964262306a36Sopenharmony_ci
964362306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_tlv_if_limit[] = {
964462306a36Sopenharmony_ci	{
964562306a36Sopenharmony_ci		.max = 2,
964662306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_STATION),
964762306a36Sopenharmony_ci	},
964862306a36Sopenharmony_ci	{
964962306a36Sopenharmony_ci		.max = 2,
965062306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_AP) |
965162306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
965262306a36Sopenharmony_ci			 BIT(NL80211_IFTYPE_MESH_POINT) |
965362306a36Sopenharmony_ci#endif
965462306a36Sopenharmony_ci			 BIT(NL80211_IFTYPE_P2P_CLIENT) |
965562306a36Sopenharmony_ci			 BIT(NL80211_IFTYPE_P2P_GO),
965662306a36Sopenharmony_ci	},
965762306a36Sopenharmony_ci	{
965862306a36Sopenharmony_ci		.max = 1,
965962306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_P2P_DEVICE),
966062306a36Sopenharmony_ci	},
966162306a36Sopenharmony_ci};
966262306a36Sopenharmony_ci
966362306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_tlv_qcs_if_limit[] = {
966462306a36Sopenharmony_ci	{
966562306a36Sopenharmony_ci		.max = 2,
966662306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_STATION),
966762306a36Sopenharmony_ci	},
966862306a36Sopenharmony_ci	{
966962306a36Sopenharmony_ci		.max = 2,
967062306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_P2P_CLIENT),
967162306a36Sopenharmony_ci	},
967262306a36Sopenharmony_ci	{
967362306a36Sopenharmony_ci		.max = 1,
967462306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_AP) |
967562306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
967662306a36Sopenharmony_ci			 BIT(NL80211_IFTYPE_MESH_POINT) |
967762306a36Sopenharmony_ci#endif
967862306a36Sopenharmony_ci			 BIT(NL80211_IFTYPE_P2P_GO),
967962306a36Sopenharmony_ci	},
968062306a36Sopenharmony_ci	{
968162306a36Sopenharmony_ci		.max = 1,
968262306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_P2P_DEVICE),
968362306a36Sopenharmony_ci	},
968462306a36Sopenharmony_ci};
968562306a36Sopenharmony_ci
968662306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_tlv_if_limit_ibss[] = {
968762306a36Sopenharmony_ci	{
968862306a36Sopenharmony_ci		.max = 1,
968962306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_STATION),
969062306a36Sopenharmony_ci	},
969162306a36Sopenharmony_ci	{
969262306a36Sopenharmony_ci		.max = 1,
969362306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_ADHOC),
969462306a36Sopenharmony_ci	},
969562306a36Sopenharmony_ci};
969662306a36Sopenharmony_ci
969762306a36Sopenharmony_ci/* FIXME: This is not thoroughly tested. These combinations may over- or
969862306a36Sopenharmony_ci * underestimate hw/fw capabilities.
969962306a36Sopenharmony_ci */
970062306a36Sopenharmony_cistatic struct ieee80211_iface_combination ath10k_tlv_if_comb[] = {
970162306a36Sopenharmony_ci	{
970262306a36Sopenharmony_ci		.limits = ath10k_tlv_if_limit,
970362306a36Sopenharmony_ci		.num_different_channels = 1,
970462306a36Sopenharmony_ci		.max_interfaces = 4,
970562306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_tlv_if_limit),
970662306a36Sopenharmony_ci	},
970762306a36Sopenharmony_ci	{
970862306a36Sopenharmony_ci		.limits = ath10k_tlv_if_limit_ibss,
970962306a36Sopenharmony_ci		.num_different_channels = 1,
971062306a36Sopenharmony_ci		.max_interfaces = 2,
971162306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_tlv_if_limit_ibss),
971262306a36Sopenharmony_ci	},
971362306a36Sopenharmony_ci};
971462306a36Sopenharmony_ci
971562306a36Sopenharmony_cistatic struct ieee80211_iface_combination ath10k_tlv_qcs_if_comb[] = {
971662306a36Sopenharmony_ci	{
971762306a36Sopenharmony_ci		.limits = ath10k_tlv_if_limit,
971862306a36Sopenharmony_ci		.num_different_channels = 1,
971962306a36Sopenharmony_ci		.max_interfaces = 4,
972062306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_tlv_if_limit),
972162306a36Sopenharmony_ci	},
972262306a36Sopenharmony_ci	{
972362306a36Sopenharmony_ci		.limits = ath10k_tlv_qcs_if_limit,
972462306a36Sopenharmony_ci		.num_different_channels = 2,
972562306a36Sopenharmony_ci		.max_interfaces = 4,
972662306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_tlv_qcs_if_limit),
972762306a36Sopenharmony_ci	},
972862306a36Sopenharmony_ci	{
972962306a36Sopenharmony_ci		.limits = ath10k_tlv_if_limit_ibss,
973062306a36Sopenharmony_ci		.num_different_channels = 1,
973162306a36Sopenharmony_ci		.max_interfaces = 2,
973262306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_tlv_if_limit_ibss),
973362306a36Sopenharmony_ci	},
973462306a36Sopenharmony_ci};
973562306a36Sopenharmony_ci
973662306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = {
973762306a36Sopenharmony_ci	{
973862306a36Sopenharmony_ci		.max = 1,
973962306a36Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_STATION),
974062306a36Sopenharmony_ci	},
974162306a36Sopenharmony_ci	{
974262306a36Sopenharmony_ci		.max	= 16,
974362306a36Sopenharmony_ci		.types	= BIT(NL80211_IFTYPE_AP)
974462306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
974562306a36Sopenharmony_ci			| BIT(NL80211_IFTYPE_MESH_POINT)
974662306a36Sopenharmony_ci#endif
974762306a36Sopenharmony_ci	},
974862306a36Sopenharmony_ci};
974962306a36Sopenharmony_ci
975062306a36Sopenharmony_cistatic const struct ieee80211_iface_combination ath10k_10_4_if_comb[] = {
975162306a36Sopenharmony_ci	{
975262306a36Sopenharmony_ci		.limits = ath10k_10_4_if_limits,
975362306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_10_4_if_limits),
975462306a36Sopenharmony_ci		.max_interfaces = 16,
975562306a36Sopenharmony_ci		.num_different_channels = 1,
975662306a36Sopenharmony_ci		.beacon_int_infra_match = true,
975762306a36Sopenharmony_ci		.beacon_int_min_gcd = 1,
975862306a36Sopenharmony_ci#ifdef CONFIG_ATH10K_DFS_CERTIFIED
975962306a36Sopenharmony_ci		.radar_detect_widths =	BIT(NL80211_CHAN_WIDTH_20_NOHT) |
976062306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_20) |
976162306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_40) |
976262306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_80) |
976362306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_80P80) |
976462306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_160),
976562306a36Sopenharmony_ci#endif
976662306a36Sopenharmony_ci	},
976762306a36Sopenharmony_ci};
976862306a36Sopenharmony_ci
976962306a36Sopenharmony_cistatic const struct
977062306a36Sopenharmony_ciieee80211_iface_combination ath10k_10_4_bcn_int_if_comb[] = {
977162306a36Sopenharmony_ci	{
977262306a36Sopenharmony_ci		.limits = ath10k_10_4_if_limits,
977362306a36Sopenharmony_ci		.n_limits = ARRAY_SIZE(ath10k_10_4_if_limits),
977462306a36Sopenharmony_ci		.max_interfaces = 16,
977562306a36Sopenharmony_ci		.num_different_channels = 1,
977662306a36Sopenharmony_ci		.beacon_int_infra_match = true,
977762306a36Sopenharmony_ci		.beacon_int_min_gcd = 100,
977862306a36Sopenharmony_ci#ifdef CONFIG_ATH10K_DFS_CERTIFIED
977962306a36Sopenharmony_ci		.radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
978062306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_20) |
978162306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_40) |
978262306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_80) |
978362306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_80P80) |
978462306a36Sopenharmony_ci					BIT(NL80211_CHAN_WIDTH_160),
978562306a36Sopenharmony_ci#endif
978662306a36Sopenharmony_ci	},
978762306a36Sopenharmony_ci};
978862306a36Sopenharmony_ci
978962306a36Sopenharmony_cistatic void ath10k_get_arvif_iter(void *data, u8 *mac,
979062306a36Sopenharmony_ci				  struct ieee80211_vif *vif)
979162306a36Sopenharmony_ci{
979262306a36Sopenharmony_ci	struct ath10k_vif_iter *arvif_iter = data;
979362306a36Sopenharmony_ci	struct ath10k_vif *arvif = (void *)vif->drv_priv;
979462306a36Sopenharmony_ci
979562306a36Sopenharmony_ci	if (arvif->vdev_id == arvif_iter->vdev_id)
979662306a36Sopenharmony_ci		arvif_iter->arvif = arvif;
979762306a36Sopenharmony_ci}
979862306a36Sopenharmony_ci
979962306a36Sopenharmony_cistruct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
980062306a36Sopenharmony_ci{
980162306a36Sopenharmony_ci	struct ath10k_vif_iter arvif_iter;
980262306a36Sopenharmony_ci
980362306a36Sopenharmony_ci	memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter));
980462306a36Sopenharmony_ci	arvif_iter.vdev_id = vdev_id;
980562306a36Sopenharmony_ci
980662306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(ar->hw,
980762306a36Sopenharmony_ci						   ATH10K_ITER_RESUME_FLAGS,
980862306a36Sopenharmony_ci						   ath10k_get_arvif_iter,
980962306a36Sopenharmony_ci						   &arvif_iter);
981062306a36Sopenharmony_ci	if (!arvif_iter.arvif) {
981162306a36Sopenharmony_ci		ath10k_warn(ar, "No VIF found for vdev %d\n", vdev_id);
981262306a36Sopenharmony_ci		return NULL;
981362306a36Sopenharmony_ci	}
981462306a36Sopenharmony_ci
981562306a36Sopenharmony_ci	return arvif_iter.arvif;
981662306a36Sopenharmony_ci}
981762306a36Sopenharmony_ci
981862306a36Sopenharmony_ci#define WRD_METHOD "WRDD"
981962306a36Sopenharmony_ci#define WRDD_WIFI  (0x07)
982062306a36Sopenharmony_ci
982162306a36Sopenharmony_cistatic u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd)
982262306a36Sopenharmony_ci{
982362306a36Sopenharmony_ci	union acpi_object *mcc_pkg;
982462306a36Sopenharmony_ci	union acpi_object *domain_type;
982562306a36Sopenharmony_ci	union acpi_object *mcc_value;
982662306a36Sopenharmony_ci	u32 i;
982762306a36Sopenharmony_ci
982862306a36Sopenharmony_ci	if (wrdd->type != ACPI_TYPE_PACKAGE ||
982962306a36Sopenharmony_ci	    wrdd->package.count < 2 ||
983062306a36Sopenharmony_ci	    wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
983162306a36Sopenharmony_ci	    wrdd->package.elements[0].integer.value != 0) {
983262306a36Sopenharmony_ci		ath10k_warn(ar, "ignoring malformed/unsupported wrdd structure\n");
983362306a36Sopenharmony_ci		return 0;
983462306a36Sopenharmony_ci	}
983562306a36Sopenharmony_ci
983662306a36Sopenharmony_ci	for (i = 1; i < wrdd->package.count; ++i) {
983762306a36Sopenharmony_ci		mcc_pkg = &wrdd->package.elements[i];
983862306a36Sopenharmony_ci
983962306a36Sopenharmony_ci		if (mcc_pkg->type != ACPI_TYPE_PACKAGE)
984062306a36Sopenharmony_ci			continue;
984162306a36Sopenharmony_ci		if (mcc_pkg->package.count < 2)
984262306a36Sopenharmony_ci			continue;
984362306a36Sopenharmony_ci		if (mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
984462306a36Sopenharmony_ci		    mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
984562306a36Sopenharmony_ci			continue;
984662306a36Sopenharmony_ci
984762306a36Sopenharmony_ci		domain_type = &mcc_pkg->package.elements[0];
984862306a36Sopenharmony_ci		if (domain_type->integer.value != WRDD_WIFI)
984962306a36Sopenharmony_ci			continue;
985062306a36Sopenharmony_ci
985162306a36Sopenharmony_ci		mcc_value = &mcc_pkg->package.elements[1];
985262306a36Sopenharmony_ci		return mcc_value->integer.value;
985362306a36Sopenharmony_ci	}
985462306a36Sopenharmony_ci	return 0;
985562306a36Sopenharmony_ci}
985662306a36Sopenharmony_ci
985762306a36Sopenharmony_cistatic int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
985862306a36Sopenharmony_ci{
985962306a36Sopenharmony_ci	acpi_handle root_handle;
986062306a36Sopenharmony_ci	acpi_handle handle;
986162306a36Sopenharmony_ci	struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
986262306a36Sopenharmony_ci	acpi_status status;
986362306a36Sopenharmony_ci	u32 alpha2_code;
986462306a36Sopenharmony_ci	char alpha2[3];
986562306a36Sopenharmony_ci
986662306a36Sopenharmony_ci	root_handle = ACPI_HANDLE(ar->dev);
986762306a36Sopenharmony_ci	if (!root_handle)
986862306a36Sopenharmony_ci		return -EOPNOTSUPP;
986962306a36Sopenharmony_ci
987062306a36Sopenharmony_ci	status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
987162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
987262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_BOOT,
987362306a36Sopenharmony_ci			   "failed to get wrd method %d\n", status);
987462306a36Sopenharmony_ci		return -EIO;
987562306a36Sopenharmony_ci	}
987662306a36Sopenharmony_ci
987762306a36Sopenharmony_ci	status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
987862306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
987962306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_BOOT,
988062306a36Sopenharmony_ci			   "failed to call wrdc %d\n", status);
988162306a36Sopenharmony_ci		return -EIO;
988262306a36Sopenharmony_ci	}
988362306a36Sopenharmony_ci
988462306a36Sopenharmony_ci	alpha2_code = ath10k_mac_wrdd_get_mcc(ar, wrdd.pointer);
988562306a36Sopenharmony_ci	kfree(wrdd.pointer);
988662306a36Sopenharmony_ci	if (!alpha2_code)
988762306a36Sopenharmony_ci		return -EIO;
988862306a36Sopenharmony_ci
988962306a36Sopenharmony_ci	alpha2[0] = (alpha2_code >> 8) & 0xff;
989062306a36Sopenharmony_ci	alpha2[1] = (alpha2_code >> 0) & 0xff;
989162306a36Sopenharmony_ci	alpha2[2] = '\0';
989262306a36Sopenharmony_ci
989362306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT,
989462306a36Sopenharmony_ci		   "regulatory hint from WRDD (alpha2-code): %s\n", alpha2);
989562306a36Sopenharmony_ci
989662306a36Sopenharmony_ci	*rd = ath_regd_find_country_by_name(alpha2);
989762306a36Sopenharmony_ci	if (*rd == 0xffff)
989862306a36Sopenharmony_ci		return -EIO;
989962306a36Sopenharmony_ci
990062306a36Sopenharmony_ci	*rd |= COUNTRY_ERD_FLAG;
990162306a36Sopenharmony_ci	return 0;
990262306a36Sopenharmony_ci}
990362306a36Sopenharmony_ci
990462306a36Sopenharmony_cistatic int ath10k_mac_init_rd(struct ath10k *ar)
990562306a36Sopenharmony_ci{
990662306a36Sopenharmony_ci	int ret;
990762306a36Sopenharmony_ci	u16 rd;
990862306a36Sopenharmony_ci
990962306a36Sopenharmony_ci	ret = ath10k_mac_get_wrdd_regulatory(ar, &rd);
991062306a36Sopenharmony_ci	if (ret) {
991162306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_BOOT,
991262306a36Sopenharmony_ci			   "fallback to eeprom programmed regulatory settings\n");
991362306a36Sopenharmony_ci		rd = ar->hw_eeprom_rd;
991462306a36Sopenharmony_ci	}
991562306a36Sopenharmony_ci
991662306a36Sopenharmony_ci	ar->ath_common.regulatory.current_rd = rd;
991762306a36Sopenharmony_ci	return 0;
991862306a36Sopenharmony_ci}
991962306a36Sopenharmony_ci
992062306a36Sopenharmony_ciint ath10k_mac_register(struct ath10k *ar)
992162306a36Sopenharmony_ci{
992262306a36Sopenharmony_ci	static const u32 cipher_suites[] = {
992362306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_WEP40,
992462306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_WEP104,
992562306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_TKIP,
992662306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_CCMP,
992762306a36Sopenharmony_ci
992862306a36Sopenharmony_ci		/* Do not add hardware supported ciphers before this line.
992962306a36Sopenharmony_ci		 * Allow software encryption for all chips. Don't forget to
993062306a36Sopenharmony_ci		 * update n_cipher_suites below.
993162306a36Sopenharmony_ci		 */
993262306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_AES_CMAC,
993362306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_BIP_CMAC_256,
993462306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_BIP_GMAC_128,
993562306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_BIP_GMAC_256,
993662306a36Sopenharmony_ci
993762306a36Sopenharmony_ci		/* Only QCA99x0 and QCA4019 variants support GCMP-128, GCMP-256
993862306a36Sopenharmony_ci		 * and CCMP-256 in hardware.
993962306a36Sopenharmony_ci		 */
994062306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_GCMP,
994162306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_GCMP_256,
994262306a36Sopenharmony_ci		WLAN_CIPHER_SUITE_CCMP_256,
994362306a36Sopenharmony_ci	};
994462306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
994562306a36Sopenharmony_ci	void *channels;
994662306a36Sopenharmony_ci	int ret;
994762306a36Sopenharmony_ci
994862306a36Sopenharmony_ci	if (!is_valid_ether_addr(ar->mac_addr)) {
994962306a36Sopenharmony_ci		ath10k_warn(ar, "invalid MAC address; choosing random\n");
995062306a36Sopenharmony_ci		eth_random_addr(ar->mac_addr);
995162306a36Sopenharmony_ci	}
995262306a36Sopenharmony_ci	SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
995362306a36Sopenharmony_ci
995462306a36Sopenharmony_ci	SET_IEEE80211_DEV(ar->hw, ar->dev);
995562306a36Sopenharmony_ci
995662306a36Sopenharmony_ci	BUILD_BUG_ON((ARRAY_SIZE(ath10k_2ghz_channels) +
995762306a36Sopenharmony_ci		      ARRAY_SIZE(ath10k_5ghz_channels)) !=
995862306a36Sopenharmony_ci		     ATH10K_NUM_CHANS);
995962306a36Sopenharmony_ci
996062306a36Sopenharmony_ci	if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
996162306a36Sopenharmony_ci		channels = kmemdup(ath10k_2ghz_channels,
996262306a36Sopenharmony_ci				   sizeof(ath10k_2ghz_channels),
996362306a36Sopenharmony_ci				   GFP_KERNEL);
996462306a36Sopenharmony_ci		if (!channels) {
996562306a36Sopenharmony_ci			ret = -ENOMEM;
996662306a36Sopenharmony_ci			goto err_free;
996762306a36Sopenharmony_ci		}
996862306a36Sopenharmony_ci
996962306a36Sopenharmony_ci		band = &ar->mac.sbands[NL80211_BAND_2GHZ];
997062306a36Sopenharmony_ci		band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
997162306a36Sopenharmony_ci		band->channels = channels;
997262306a36Sopenharmony_ci
997362306a36Sopenharmony_ci		if (ar->hw_params.cck_rate_map_rev2) {
997462306a36Sopenharmony_ci			band->n_bitrates = ath10k_g_rates_rev2_size;
997562306a36Sopenharmony_ci			band->bitrates = ath10k_g_rates_rev2;
997662306a36Sopenharmony_ci		} else {
997762306a36Sopenharmony_ci			band->n_bitrates = ath10k_g_rates_size;
997862306a36Sopenharmony_ci			band->bitrates = ath10k_g_rates;
997962306a36Sopenharmony_ci		}
998062306a36Sopenharmony_ci
998162306a36Sopenharmony_ci		ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = band;
998262306a36Sopenharmony_ci	}
998362306a36Sopenharmony_ci
998462306a36Sopenharmony_ci	if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
998562306a36Sopenharmony_ci		channels = kmemdup(ath10k_5ghz_channels,
998662306a36Sopenharmony_ci				   sizeof(ath10k_5ghz_channels),
998762306a36Sopenharmony_ci				   GFP_KERNEL);
998862306a36Sopenharmony_ci		if (!channels) {
998962306a36Sopenharmony_ci			ret = -ENOMEM;
999062306a36Sopenharmony_ci			goto err_free;
999162306a36Sopenharmony_ci		}
999262306a36Sopenharmony_ci
999362306a36Sopenharmony_ci		band = &ar->mac.sbands[NL80211_BAND_5GHZ];
999462306a36Sopenharmony_ci		band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels);
999562306a36Sopenharmony_ci		band->channels = channels;
999662306a36Sopenharmony_ci		band->n_bitrates = ath10k_a_rates_size;
999762306a36Sopenharmony_ci		band->bitrates = ath10k_a_rates;
999862306a36Sopenharmony_ci		ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
999962306a36Sopenharmony_ci	}
1000062306a36Sopenharmony_ci
1000162306a36Sopenharmony_ci	wiphy_read_of_freq_limits(ar->hw->wiphy);
1000262306a36Sopenharmony_ci	ath10k_mac_setup_ht_vht_cap(ar);
1000362306a36Sopenharmony_ci
1000462306a36Sopenharmony_ci	ar->hw->wiphy->interface_modes =
1000562306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_STATION) |
1000662306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_AP) |
1000762306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_MESH_POINT);
1000862306a36Sopenharmony_ci
1000962306a36Sopenharmony_ci	ar->hw->wiphy->available_antennas_rx = ar->cfg_rx_chainmask;
1001062306a36Sopenharmony_ci	ar->hw->wiphy->available_antennas_tx = ar->cfg_tx_chainmask;
1001162306a36Sopenharmony_ci
1001262306a36Sopenharmony_ci	if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->normal_mode_fw.fw_file.fw_features))
1001362306a36Sopenharmony_ci		ar->hw->wiphy->interface_modes |=
1001462306a36Sopenharmony_ci			BIT(NL80211_IFTYPE_P2P_DEVICE) |
1001562306a36Sopenharmony_ci			BIT(NL80211_IFTYPE_P2P_CLIENT) |
1001662306a36Sopenharmony_ci			BIT(NL80211_IFTYPE_P2P_GO);
1001762306a36Sopenharmony_ci
1001862306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, SIGNAL_DBM);
1001962306a36Sopenharmony_ci
1002062306a36Sopenharmony_ci	if (!test_bit(ATH10K_FW_FEATURE_NO_PS,
1002162306a36Sopenharmony_ci		      ar->running_fw->fw_file.fw_features)) {
1002262306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, SUPPORTS_PS);
1002362306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS);
1002462306a36Sopenharmony_ci	}
1002562306a36Sopenharmony_ci
1002662306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, MFP_CAPABLE);
1002762306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, REPORTS_TX_ACK_STATUS);
1002862306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, HAS_RATE_CONTROL);
1002962306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, AP_LINK_PS);
1003062306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, SPECTRUM_MGMT);
1003162306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT);
1003262306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, CONNECTION_MONITOR);
1003362306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, SUPPORTS_PER_STA_GTK);
1003462306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, WANT_MONITOR_VIF);
1003562306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA);
1003662306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
1003762306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
1003862306a36Sopenharmony_ci	ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
1003962306a36Sopenharmony_ci
1004062306a36Sopenharmony_ci	if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
1004162306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
1004262306a36Sopenharmony_ci
1004362306a36Sopenharmony_ci	ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
1004462306a36Sopenharmony_ci	ar->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
1004562306a36Sopenharmony_ci
1004662306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
1004762306a36Sopenharmony_ci		ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS;
1004862306a36Sopenharmony_ci
1004962306a36Sopenharmony_ci	if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
1005062306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION);
1005162306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW);
1005262306a36Sopenharmony_ci	}
1005362306a36Sopenharmony_ci
1005462306a36Sopenharmony_ci	ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
1005562306a36Sopenharmony_ci	ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
1005662306a36Sopenharmony_ci
1005762306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
1005862306a36Sopenharmony_ci		ar->hw->wiphy->max_sched_scan_ssids = WMI_PNO_MAX_SUPP_NETWORKS;
1005962306a36Sopenharmony_ci		ar->hw->wiphy->max_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
1006062306a36Sopenharmony_ci		ar->hw->wiphy->max_sched_scan_ie_len = WMI_PNO_MAX_IE_LENGTH;
1006162306a36Sopenharmony_ci		ar->hw->wiphy->max_sched_scan_plans = WMI_PNO_MAX_SCHED_SCAN_PLANS;
1006262306a36Sopenharmony_ci		ar->hw->wiphy->max_sched_scan_plan_interval =
1006362306a36Sopenharmony_ci			WMI_PNO_MAX_SCHED_SCAN_PLAN_INT;
1006462306a36Sopenharmony_ci		ar->hw->wiphy->max_sched_scan_plan_iterations =
1006562306a36Sopenharmony_ci			WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS;
1006662306a36Sopenharmony_ci		ar->hw->wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
1006762306a36Sopenharmony_ci	}
1006862306a36Sopenharmony_ci
1006962306a36Sopenharmony_ci	ar->hw->vif_data_size = sizeof(struct ath10k_vif);
1007062306a36Sopenharmony_ci	ar->hw->sta_data_size = sizeof(struct ath10k_sta);
1007162306a36Sopenharmony_ci	ar->hw->txq_data_size = sizeof(struct ath10k_txq);
1007262306a36Sopenharmony_ci
1007362306a36Sopenharmony_ci	ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
1007462306a36Sopenharmony_ci
1007562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
1007662306a36Sopenharmony_ci		ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
1007762306a36Sopenharmony_ci
1007862306a36Sopenharmony_ci		/* Firmware delivers WPS/P2P Probe Requests frames to driver so
1007962306a36Sopenharmony_ci		 * that userspace (e.g. wpa_supplicant/hostapd) can generate
1008062306a36Sopenharmony_ci		 * correct Probe Responses. This is more of a hack advert..
1008162306a36Sopenharmony_ci		 */
1008262306a36Sopenharmony_ci		ar->hw->wiphy->probe_resp_offload |=
1008362306a36Sopenharmony_ci			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
1008462306a36Sopenharmony_ci			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
1008562306a36Sopenharmony_ci			NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
1008662306a36Sopenharmony_ci	}
1008762306a36Sopenharmony_ci
1008862306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_TDLS, ar->wmi.svc_map) ||
1008962306a36Sopenharmony_ci	    test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, ar->wmi.svc_map)) {
1009062306a36Sopenharmony_ci		ar->hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
1009162306a36Sopenharmony_ci		if (test_bit(WMI_SERVICE_TDLS_WIDER_BANDWIDTH, ar->wmi.svc_map))
1009262306a36Sopenharmony_ci			ieee80211_hw_set(ar->hw, TDLS_WIDER_BW);
1009362306a36Sopenharmony_ci	}
1009462306a36Sopenharmony_ci
1009562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map))
1009662306a36Sopenharmony_ci		ieee80211_hw_set(ar->hw, SUPPORTS_TDLS_BUFFER_STA);
1009762306a36Sopenharmony_ci
1009862306a36Sopenharmony_ci	if (ath10k_frame_mode == ATH10K_HW_TXRX_ETHERNET) {
1009962306a36Sopenharmony_ci		if (ar->wmi.vdev_param->tx_encap_type !=
1010062306a36Sopenharmony_ci		    WMI_VDEV_PARAM_UNSUPPORTED)
1010162306a36Sopenharmony_ci			ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
1010262306a36Sopenharmony_ci	}
1010362306a36Sopenharmony_ci
1010462306a36Sopenharmony_ci	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
1010562306a36Sopenharmony_ci	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
1010662306a36Sopenharmony_ci	ar->hw->wiphy->max_remain_on_channel_duration = 5000;
1010762306a36Sopenharmony_ci
1010862306a36Sopenharmony_ci	ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
1010962306a36Sopenharmony_ci	ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
1011062306a36Sopenharmony_ci				   NL80211_FEATURE_AP_SCAN;
1011162306a36Sopenharmony_ci
1011262306a36Sopenharmony_ci	ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations;
1011362306a36Sopenharmony_ci
1011462306a36Sopenharmony_ci	ret = ath10k_wow_init(ar);
1011562306a36Sopenharmony_ci	if (ret) {
1011662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to init wow: %d\n", ret);
1011762306a36Sopenharmony_ci		goto err_free;
1011862306a36Sopenharmony_ci	}
1011962306a36Sopenharmony_ci
1012062306a36Sopenharmony_ci	wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
1012162306a36Sopenharmony_ci	wiphy_ext_feature_set(ar->hw->wiphy,
1012262306a36Sopenharmony_ci			      NL80211_EXT_FEATURE_SET_SCAN_DWELL);
1012362306a36Sopenharmony_ci	wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_AQL);
1012462306a36Sopenharmony_ci
1012562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, ar->wmi.svc_map) ||
1012662306a36Sopenharmony_ci	    test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ar->wmi.svc_map))
1012762306a36Sopenharmony_ci		wiphy_ext_feature_set(ar->hw->wiphy,
1012862306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
1012962306a36Sopenharmony_ci
1013062306a36Sopenharmony_ci	if (ath10k_peer_stats_enabled(ar) ||
1013162306a36Sopenharmony_ci	    test_bit(WMI_SERVICE_REPORT_AIRTIME, ar->wmi.svc_map))
1013262306a36Sopenharmony_ci		wiphy_ext_feature_set(ar->hw->wiphy,
1013362306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
1013462306a36Sopenharmony_ci
1013562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_RTT_RESPONDER_ROLE, ar->wmi.svc_map))
1013662306a36Sopenharmony_ci		wiphy_ext_feature_set(ar->hw->wiphy,
1013762306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER);
1013862306a36Sopenharmony_ci
1013962306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_TX_PWR_PER_PEER, ar->wmi.svc_map))
1014062306a36Sopenharmony_ci		wiphy_ext_feature_set(ar->hw->wiphy,
1014162306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_STA_TX_PWR);
1014262306a36Sopenharmony_ci
1014362306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->wmi.svc_map)) {
1014462306a36Sopenharmony_ci		ar->hw->wiphy->tid_config_support.vif |=
1014562306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_NOACK) |
1014662306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_RETRY_SHORT) |
1014762306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG) |
1014862306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL) |
1014962306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
1015062306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE);
1015162306a36Sopenharmony_ci
1015262306a36Sopenharmony_ci		if (test_bit(WMI_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
1015362306a36Sopenharmony_ci			     ar->wmi.svc_map)) {
1015462306a36Sopenharmony_ci			ar->hw->wiphy->tid_config_support.vif |=
1015562306a36Sopenharmony_ci				BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL);
1015662306a36Sopenharmony_ci		}
1015762306a36Sopenharmony_ci
1015862306a36Sopenharmony_ci		ar->hw->wiphy->tid_config_support.peer =
1015962306a36Sopenharmony_ci				ar->hw->wiphy->tid_config_support.vif;
1016062306a36Sopenharmony_ci		ar->hw->wiphy->max_data_retry_count = ATH10K_MAX_RETRY_COUNT;
1016162306a36Sopenharmony_ci	} else {
1016262306a36Sopenharmony_ci		ar->ops->set_tid_config = NULL;
1016362306a36Sopenharmony_ci	}
1016462306a36Sopenharmony_ci	/*
1016562306a36Sopenharmony_ci	 * on LL hardware queues are managed entirely by the FW
1016662306a36Sopenharmony_ci	 * so we only advertise to mac we can do the queues thing
1016762306a36Sopenharmony_ci	 */
1016862306a36Sopenharmony_ci	ar->hw->queues = IEEE80211_MAX_QUEUES;
1016962306a36Sopenharmony_ci
1017062306a36Sopenharmony_ci	/* vdev_ids are used as hw queue numbers. Make sure offchan tx queue is
1017162306a36Sopenharmony_ci	 * something that vdev_ids can't reach so that we don't stop the queue
1017262306a36Sopenharmony_ci	 * accidentally.
1017362306a36Sopenharmony_ci	 */
1017462306a36Sopenharmony_ci	ar->hw->offchannel_tx_hw_queue = IEEE80211_MAX_QUEUES - 1;
1017562306a36Sopenharmony_ci
1017662306a36Sopenharmony_ci	switch (ar->running_fw->fw_file.wmi_op_version) {
1017762306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_MAIN:
1017862306a36Sopenharmony_ci		ar->hw->wiphy->iface_combinations = ath10k_if_comb;
1017962306a36Sopenharmony_ci		ar->hw->wiphy->n_iface_combinations =
1018062306a36Sopenharmony_ci			ARRAY_SIZE(ath10k_if_comb);
1018162306a36Sopenharmony_ci		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
1018262306a36Sopenharmony_ci		break;
1018362306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_TLV:
1018462306a36Sopenharmony_ci		if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
1018562306a36Sopenharmony_ci			ar->hw->wiphy->iface_combinations =
1018662306a36Sopenharmony_ci				ath10k_tlv_qcs_if_comb;
1018762306a36Sopenharmony_ci			ar->hw->wiphy->n_iface_combinations =
1018862306a36Sopenharmony_ci				ARRAY_SIZE(ath10k_tlv_qcs_if_comb);
1018962306a36Sopenharmony_ci		} else {
1019062306a36Sopenharmony_ci			ar->hw->wiphy->iface_combinations = ath10k_tlv_if_comb;
1019162306a36Sopenharmony_ci			ar->hw->wiphy->n_iface_combinations =
1019262306a36Sopenharmony_ci				ARRAY_SIZE(ath10k_tlv_if_comb);
1019362306a36Sopenharmony_ci		}
1019462306a36Sopenharmony_ci		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
1019562306a36Sopenharmony_ci		break;
1019662306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_10_1:
1019762306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_10_2:
1019862306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_10_2_4:
1019962306a36Sopenharmony_ci		ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
1020062306a36Sopenharmony_ci		ar->hw->wiphy->n_iface_combinations =
1020162306a36Sopenharmony_ci			ARRAY_SIZE(ath10k_10x_if_comb);
1020262306a36Sopenharmony_ci		break;
1020362306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_10_4:
1020462306a36Sopenharmony_ci		ar->hw->wiphy->iface_combinations = ath10k_10_4_if_comb;
1020562306a36Sopenharmony_ci		ar->hw->wiphy->n_iface_combinations =
1020662306a36Sopenharmony_ci			ARRAY_SIZE(ath10k_10_4_if_comb);
1020762306a36Sopenharmony_ci		if (test_bit(WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT,
1020862306a36Sopenharmony_ci			     ar->wmi.svc_map)) {
1020962306a36Sopenharmony_ci			ar->hw->wiphy->iface_combinations =
1021062306a36Sopenharmony_ci				ath10k_10_4_bcn_int_if_comb;
1021162306a36Sopenharmony_ci			ar->hw->wiphy->n_iface_combinations =
1021262306a36Sopenharmony_ci				ARRAY_SIZE(ath10k_10_4_bcn_int_if_comb);
1021362306a36Sopenharmony_ci		}
1021462306a36Sopenharmony_ci		break;
1021562306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_UNSET:
1021662306a36Sopenharmony_ci	case ATH10K_FW_WMI_OP_VERSION_MAX:
1021762306a36Sopenharmony_ci		WARN_ON(1);
1021862306a36Sopenharmony_ci		ret = -EINVAL;
1021962306a36Sopenharmony_ci		goto err_free;
1022062306a36Sopenharmony_ci	}
1022162306a36Sopenharmony_ci
1022262306a36Sopenharmony_ci	if (ar->hw_params.dynamic_sar_support)
1022362306a36Sopenharmony_ci		ar->hw->wiphy->sar_capa = &ath10k_sar_capa;
1022462306a36Sopenharmony_ci
1022562306a36Sopenharmony_ci	if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
1022662306a36Sopenharmony_ci		ar->hw->netdev_features = NETIF_F_HW_CSUM;
1022762306a36Sopenharmony_ci
1022862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {
1022962306a36Sopenharmony_ci		/* Init ath dfs pattern detector */
1023062306a36Sopenharmony_ci		ar->ath_common.debug_mask = ATH_DBG_DFS;
1023162306a36Sopenharmony_ci		ar->dfs_detector = dfs_pattern_detector_init(&ar->ath_common,
1023262306a36Sopenharmony_ci							     NL80211_DFS_UNSET);
1023362306a36Sopenharmony_ci
1023462306a36Sopenharmony_ci		if (!ar->dfs_detector)
1023562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to initialise DFS pattern detector\n");
1023662306a36Sopenharmony_ci	}
1023762306a36Sopenharmony_ci
1023862306a36Sopenharmony_ci	ret = ath10k_mac_init_rd(ar);
1023962306a36Sopenharmony_ci	if (ret) {
1024062306a36Sopenharmony_ci		ath10k_err(ar, "failed to derive regdom: %d\n", ret);
1024162306a36Sopenharmony_ci		goto err_dfs_detector_exit;
1024262306a36Sopenharmony_ci	}
1024362306a36Sopenharmony_ci
1024462306a36Sopenharmony_ci	/* Disable set_coverage_class for chipsets that do not support it. */
1024562306a36Sopenharmony_ci	if (!ar->hw_params.hw_ops->set_coverage_class)
1024662306a36Sopenharmony_ci		ar->ops->set_coverage_class = NULL;
1024762306a36Sopenharmony_ci
1024862306a36Sopenharmony_ci	ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
1024962306a36Sopenharmony_ci			    ath10k_reg_notifier);
1025062306a36Sopenharmony_ci	if (ret) {
1025162306a36Sopenharmony_ci		ath10k_err(ar, "failed to initialise regulatory: %i\n", ret);
1025262306a36Sopenharmony_ci		goto err_dfs_detector_exit;
1025362306a36Sopenharmony_ci	}
1025462306a36Sopenharmony_ci
1025562306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
1025662306a36Sopenharmony_ci		ar->hw->wiphy->features |=
1025762306a36Sopenharmony_ci			NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
1025862306a36Sopenharmony_ci	}
1025962306a36Sopenharmony_ci
1026062306a36Sopenharmony_ci	ar->hw->wiphy->cipher_suites = cipher_suites;
1026162306a36Sopenharmony_ci
1026262306a36Sopenharmony_ci	/* QCA988x and QCA6174 family chips do not support CCMP-256, GCMP-128
1026362306a36Sopenharmony_ci	 * and GCMP-256 ciphers in hardware. Fetch number of ciphers supported
1026462306a36Sopenharmony_ci	 * from chip specific hw_param table.
1026562306a36Sopenharmony_ci	 */
1026662306a36Sopenharmony_ci	if (!ar->hw_params.n_cipher_suites ||
1026762306a36Sopenharmony_ci	    ar->hw_params.n_cipher_suites > ARRAY_SIZE(cipher_suites)) {
1026862306a36Sopenharmony_ci		ath10k_err(ar, "invalid hw_params.n_cipher_suites %d\n",
1026962306a36Sopenharmony_ci			   ar->hw_params.n_cipher_suites);
1027062306a36Sopenharmony_ci		ar->hw_params.n_cipher_suites = 8;
1027162306a36Sopenharmony_ci	}
1027262306a36Sopenharmony_ci	ar->hw->wiphy->n_cipher_suites = ar->hw_params.n_cipher_suites;
1027362306a36Sopenharmony_ci
1027462306a36Sopenharmony_ci	wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1027562306a36Sopenharmony_ci
1027662306a36Sopenharmony_ci	ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
1027762306a36Sopenharmony_ci
1027862306a36Sopenharmony_ci	ret = ieee80211_register_hw(ar->hw);
1027962306a36Sopenharmony_ci	if (ret) {
1028062306a36Sopenharmony_ci		ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
1028162306a36Sopenharmony_ci		goto err_dfs_detector_exit;
1028262306a36Sopenharmony_ci	}
1028362306a36Sopenharmony_ci
1028462306a36Sopenharmony_ci	if (test_bit(WMI_SERVICE_PER_PACKET_SW_ENCRYPT, ar->wmi.svc_map)) {
1028562306a36Sopenharmony_ci		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
1028662306a36Sopenharmony_ci		ar->hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
1028762306a36Sopenharmony_ci	}
1028862306a36Sopenharmony_ci
1028962306a36Sopenharmony_ci	if (!ath_is_world_regd(&ar->ath_common.reg_world_copy) &&
1029062306a36Sopenharmony_ci	    !ath_is_world_regd(&ar->ath_common.regulatory)) {
1029162306a36Sopenharmony_ci		ret = regulatory_hint(ar->hw->wiphy,
1029262306a36Sopenharmony_ci				      ar->ath_common.regulatory.alpha2);
1029362306a36Sopenharmony_ci		if (ret)
1029462306a36Sopenharmony_ci			goto err_unregister;
1029562306a36Sopenharmony_ci	}
1029662306a36Sopenharmony_ci
1029762306a36Sopenharmony_ci	return 0;
1029862306a36Sopenharmony_ci
1029962306a36Sopenharmony_cierr_unregister:
1030062306a36Sopenharmony_ci	ieee80211_unregister_hw(ar->hw);
1030162306a36Sopenharmony_ci
1030262306a36Sopenharmony_cierr_dfs_detector_exit:
1030362306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
1030462306a36Sopenharmony_ci		ar->dfs_detector->exit(ar->dfs_detector);
1030562306a36Sopenharmony_ci
1030662306a36Sopenharmony_cierr_free:
1030762306a36Sopenharmony_ci	kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
1030862306a36Sopenharmony_ci	kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels);
1030962306a36Sopenharmony_ci
1031062306a36Sopenharmony_ci	SET_IEEE80211_DEV(ar->hw, NULL);
1031162306a36Sopenharmony_ci	return ret;
1031262306a36Sopenharmony_ci}
1031362306a36Sopenharmony_ci
1031462306a36Sopenharmony_civoid ath10k_mac_unregister(struct ath10k *ar)
1031562306a36Sopenharmony_ci{
1031662306a36Sopenharmony_ci	ieee80211_unregister_hw(ar->hw);
1031762306a36Sopenharmony_ci
1031862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
1031962306a36Sopenharmony_ci		ar->dfs_detector->exit(ar->dfs_detector);
1032062306a36Sopenharmony_ci
1032162306a36Sopenharmony_ci	kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
1032262306a36Sopenharmony_ci	kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels);
1032362306a36Sopenharmony_ci
1032462306a36Sopenharmony_ci	SET_IEEE80211_DEV(ar->hw, NULL);
1032562306a36Sopenharmony_ci}
10326