18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
98c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
108c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
138c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
148c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
198c2ecf20Sopenharmony_ci * General Public License for more details.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
228c2ecf20Sopenharmony_ci * in the file called COPYING.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Contact Information:
258c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
268c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * BSD LICENSE
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
318c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
328c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
338c2ecf20Sopenharmony_ci * All rights reserved.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
368c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
378c2ecf20Sopenharmony_ci * are met:
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
408c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
418c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
428c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
438c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
448c2ecf20Sopenharmony_ci *    distribution.
458c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
468c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
478c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
508c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
518c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
528c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
538c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
548c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
558c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
568c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
578c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
588c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
598c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci *****************************************************************************/
628c2ecf20Sopenharmony_ci#include <linux/kernel.h>
638c2ecf20Sopenharmony_ci#include <linux/slab.h>
648c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
658c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
668c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
678c2ecf20Sopenharmony_ci#include <linux/ip.h>
688c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
698c2ecf20Sopenharmony_ci#include <linux/time.h>
708c2ecf20Sopenharmony_ci#include <net/mac80211.h>
718c2ecf20Sopenharmony_ci#include <net/ieee80211_radiotap.h>
728c2ecf20Sopenharmony_ci#include <net/tcp.h>
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#include "iwl-drv.h"
758c2ecf20Sopenharmony_ci#include "iwl-op-mode.h"
768c2ecf20Sopenharmony_ci#include "iwl-io.h"
778c2ecf20Sopenharmony_ci#include "mvm.h"
788c2ecf20Sopenharmony_ci#include "sta.h"
798c2ecf20Sopenharmony_ci#include "time-event.h"
808c2ecf20Sopenharmony_ci#include "iwl-eeprom-parse.h"
818c2ecf20Sopenharmony_ci#include "iwl-phy-db.h"
828c2ecf20Sopenharmony_ci#include "testmode.h"
838c2ecf20Sopenharmony_ci#include "fw/error-dump.h"
848c2ecf20Sopenharmony_ci#include "iwl-prph.h"
858c2ecf20Sopenharmony_ci#include "iwl-nvm-parse.h"
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_limit iwl_mvm_limits[] = {
888c2ecf20Sopenharmony_ci	{
898c2ecf20Sopenharmony_ci		.max = 1,
908c2ecf20Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_STATION),
918c2ecf20Sopenharmony_ci	},
928c2ecf20Sopenharmony_ci	{
938c2ecf20Sopenharmony_ci		.max = 1,
948c2ecf20Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_AP) |
958c2ecf20Sopenharmony_ci			BIT(NL80211_IFTYPE_P2P_CLIENT) |
968c2ecf20Sopenharmony_ci			BIT(NL80211_IFTYPE_P2P_GO),
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci	{
998c2ecf20Sopenharmony_ci		.max = 1,
1008c2ecf20Sopenharmony_ci		.types = BIT(NL80211_IFTYPE_P2P_DEVICE),
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
1058c2ecf20Sopenharmony_ci	{
1068c2ecf20Sopenharmony_ci		.num_different_channels = 2,
1078c2ecf20Sopenharmony_ci		.max_interfaces = 3,
1088c2ecf20Sopenharmony_ci		.limits = iwl_mvm_limits,
1098c2ecf20Sopenharmony_ci		.n_limits = ARRAY_SIZE(iwl_mvm_limits),
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
1148c2ecf20Sopenharmony_ci/*
1158c2ecf20Sopenharmony_ci * Use the reserved field to indicate magic values.
1168c2ecf20Sopenharmony_ci * these values will only be used internally by the driver,
1178c2ecf20Sopenharmony_ci * and won't make it to the fw (reserved will be 0).
1188c2ecf20Sopenharmony_ci * BC_FILTER_MAGIC_IP - configure the val of this attribute to
1198c2ecf20Sopenharmony_ci *	be the vif's ip address. in case there is not a single
1208c2ecf20Sopenharmony_ci *	ip address (0, or more than 1), this attribute will
1218c2ecf20Sopenharmony_ci *	be skipped.
1228c2ecf20Sopenharmony_ci * BC_FILTER_MAGIC_MAC - set the val of this attribute to
1238c2ecf20Sopenharmony_ci *	the LSB bytes of the vif's mac address
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cienum {
1268c2ecf20Sopenharmony_ci	BC_FILTER_MAGIC_NONE = 0,
1278c2ecf20Sopenharmony_ci	BC_FILTER_MAGIC_IP,
1288c2ecf20Sopenharmony_ci	BC_FILTER_MAGIC_MAC,
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
1328c2ecf20Sopenharmony_ci	{
1338c2ecf20Sopenharmony_ci		/* arp */
1348c2ecf20Sopenharmony_ci		.discard = 0,
1358c2ecf20Sopenharmony_ci		.frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
1368c2ecf20Sopenharmony_ci		.attrs = {
1378c2ecf20Sopenharmony_ci			{
1388c2ecf20Sopenharmony_ci				/* frame type - arp, hw type - ethernet */
1398c2ecf20Sopenharmony_ci				.offset_type =
1408c2ecf20Sopenharmony_ci					BCAST_FILTER_OFFSET_PAYLOAD_START,
1418c2ecf20Sopenharmony_ci				.offset = sizeof(rfc1042_header),
1428c2ecf20Sopenharmony_ci				.val = cpu_to_be32(0x08060001),
1438c2ecf20Sopenharmony_ci				.mask = cpu_to_be32(0xffffffff),
1448c2ecf20Sopenharmony_ci			},
1458c2ecf20Sopenharmony_ci			{
1468c2ecf20Sopenharmony_ci				/* arp dest ip */
1478c2ecf20Sopenharmony_ci				.offset_type =
1488c2ecf20Sopenharmony_ci					BCAST_FILTER_OFFSET_PAYLOAD_START,
1498c2ecf20Sopenharmony_ci				.offset = sizeof(rfc1042_header) + 2 +
1508c2ecf20Sopenharmony_ci					  sizeof(struct arphdr) +
1518c2ecf20Sopenharmony_ci					  ETH_ALEN + sizeof(__be32) +
1528c2ecf20Sopenharmony_ci					  ETH_ALEN,
1538c2ecf20Sopenharmony_ci				.mask = cpu_to_be32(0xffffffff),
1548c2ecf20Sopenharmony_ci				/* mark it as special field */
1558c2ecf20Sopenharmony_ci				.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
1568c2ecf20Sopenharmony_ci			},
1578c2ecf20Sopenharmony_ci		},
1588c2ecf20Sopenharmony_ci	},
1598c2ecf20Sopenharmony_ci	{
1608c2ecf20Sopenharmony_ci		/* dhcp offer bcast */
1618c2ecf20Sopenharmony_ci		.discard = 0,
1628c2ecf20Sopenharmony_ci		.frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
1638c2ecf20Sopenharmony_ci		.attrs = {
1648c2ecf20Sopenharmony_ci			{
1658c2ecf20Sopenharmony_ci				/* udp dest port - 68 (bootp client)*/
1668c2ecf20Sopenharmony_ci				.offset_type = BCAST_FILTER_OFFSET_IP_END,
1678c2ecf20Sopenharmony_ci				.offset = offsetof(struct udphdr, dest),
1688c2ecf20Sopenharmony_ci				.val = cpu_to_be32(0x00440000),
1698c2ecf20Sopenharmony_ci				.mask = cpu_to_be32(0xffff0000),
1708c2ecf20Sopenharmony_ci			},
1718c2ecf20Sopenharmony_ci			{
1728c2ecf20Sopenharmony_ci				/* dhcp - lsb bytes of client hw address */
1738c2ecf20Sopenharmony_ci				.offset_type = BCAST_FILTER_OFFSET_IP_END,
1748c2ecf20Sopenharmony_ci				.offset = 38,
1758c2ecf20Sopenharmony_ci				.mask = cpu_to_be32(0xffffffff),
1768c2ecf20Sopenharmony_ci				/* mark it as special field */
1778c2ecf20Sopenharmony_ci				.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
1788c2ecf20Sopenharmony_ci			},
1798c2ecf20Sopenharmony_ci		},
1808c2ecf20Sopenharmony_ci	},
1818c2ecf20Sopenharmony_ci	/* last filter must be empty */
1828c2ecf20Sopenharmony_ci	{},
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci#endif
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = {
1878c2ecf20Sopenharmony_ci	.max_peers = IWL_MVM_TOF_MAX_APS,
1888c2ecf20Sopenharmony_ci	.report_ap_tsf = 1,
1898c2ecf20Sopenharmony_ci	.randomize_mac_addr = 1,
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	.ftm = {
1928c2ecf20Sopenharmony_ci		.supported = 1,
1938c2ecf20Sopenharmony_ci		.asap = 1,
1948c2ecf20Sopenharmony_ci		.non_asap = 1,
1958c2ecf20Sopenharmony_ci		.request_lci = 1,
1968c2ecf20Sopenharmony_ci		.request_civicloc = 1,
1978c2ecf20Sopenharmony_ci		.trigger_based = 1,
1988c2ecf20Sopenharmony_ci		.non_trigger_based = 1,
1998c2ecf20Sopenharmony_ci		.max_bursts_exponent = -1, /* all supported */
2008c2ecf20Sopenharmony_ci		.max_ftms_per_burst = 0, /* no limits */
2018c2ecf20Sopenharmony_ci		.bandwidths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
2028c2ecf20Sopenharmony_ci			      BIT(NL80211_CHAN_WIDTH_20) |
2038c2ecf20Sopenharmony_ci			      BIT(NL80211_CHAN_WIDTH_40) |
2048c2ecf20Sopenharmony_ci			      BIT(NL80211_CHAN_WIDTH_80),
2058c2ecf20Sopenharmony_ci		.preambles = BIT(NL80211_PREAMBLE_LEGACY) |
2068c2ecf20Sopenharmony_ci			     BIT(NL80211_PREAMBLE_HT) |
2078c2ecf20Sopenharmony_ci			     BIT(NL80211_PREAMBLE_VHT) |
2088c2ecf20Sopenharmony_ci			     BIT(NL80211_PREAMBLE_HE),
2098c2ecf20Sopenharmony_ci	},
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
2138c2ecf20Sopenharmony_ci				 enum set_key_cmd cmd,
2148c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
2158c2ecf20Sopenharmony_ci				 struct ieee80211_sta *sta,
2168c2ecf20Sopenharmony_ci				 struct ieee80211_key_conf *key);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int i;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
2238c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_PHY_CTX; i++) {
2248c2ecf20Sopenharmony_ci		mvm->phy_ctxts[i].id = i;
2258c2ecf20Sopenharmony_ci		mvm->phy_ctxts[i].ref = 0;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistruct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
2308c2ecf20Sopenharmony_ci						  const char *alpha2,
2318c2ecf20Sopenharmony_ci						  enum iwl_mcc_source src_id,
2328c2ecf20Sopenharmony_ci						  bool *changed)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct ieee80211_regdomain *regd = NULL;
2358c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
2368c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
2378c2ecf20Sopenharmony_ci	struct iwl_mcc_update_resp *resp;
2388c2ecf20Sopenharmony_ci	u8 resp_ver;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
2458c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(resp)) {
2468c2ecf20Sopenharmony_ci		IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
2478c2ecf20Sopenharmony_ci			      PTR_ERR_OR_ZERO(resp));
2488c2ecf20Sopenharmony_ci		goto out;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (changed) {
2528c2ecf20Sopenharmony_ci		u32 status = le32_to_cpu(resp->status);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		*changed = (status == MCC_RESP_NEW_CHAN_PROFILE ||
2558c2ecf20Sopenharmony_ci			    status == MCC_RESP_ILLEGAL);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci	resp_ver = iwl_fw_lookup_notif_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
2588c2ecf20Sopenharmony_ci					   MCC_UPDATE_CMD, 0);
2598c2ecf20Sopenharmony_ci	IWL_DEBUG_LAR(mvm, "MCC update response version: %d\n", resp_ver);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
2628c2ecf20Sopenharmony_ci				      __le32_to_cpu(resp->n_channels),
2638c2ecf20Sopenharmony_ci				      resp->channels,
2648c2ecf20Sopenharmony_ci				      __le16_to_cpu(resp->mcc),
2658c2ecf20Sopenharmony_ci				      __le16_to_cpu(resp->geo_info),
2668c2ecf20Sopenharmony_ci				      __le16_to_cpu(resp->cap), resp_ver);
2678c2ecf20Sopenharmony_ci	/* Store the return source id */
2688c2ecf20Sopenharmony_ci	src_id = resp->source_id;
2698c2ecf20Sopenharmony_ci	kfree(resp);
2708c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(regd)) {
2718c2ecf20Sopenharmony_ci		IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
2728c2ecf20Sopenharmony_ci			      PTR_ERR_OR_ZERO(regd));
2738c2ecf20Sopenharmony_ci		goto out;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
2778c2ecf20Sopenharmony_ci		      regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
2788c2ecf20Sopenharmony_ci	mvm->lar_regdom_set = true;
2798c2ecf20Sopenharmony_ci	mvm->mcc_src = src_id;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ciout:
2828c2ecf20Sopenharmony_ci	return regd;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_civoid iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	bool changed;
2888c2ecf20Sopenharmony_ci	struct ieee80211_regdomain *regd;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (!iwl_mvm_is_lar_supported(mvm))
2918c2ecf20Sopenharmony_ci		return;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	regd = iwl_mvm_get_current_regdomain(mvm, &changed);
2948c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(regd)) {
2958c2ecf20Sopenharmony_ci		/* only update the regulatory core if changed */
2968c2ecf20Sopenharmony_ci		if (changed)
2978c2ecf20Sopenharmony_ci			regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		kfree(regd);
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistruct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
3048c2ecf20Sopenharmony_ci							  bool *changed)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
3078c2ecf20Sopenharmony_ci				     iwl_mvm_is_wifi_mcc_supported(mvm) ?
3088c2ecf20Sopenharmony_ci				     MCC_SOURCE_GET_CURRENT :
3098c2ecf20Sopenharmony_ci				     MCC_SOURCE_OLD_FW, changed);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ciint iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	enum iwl_mcc_source used_src;
3158c2ecf20Sopenharmony_ci	struct ieee80211_regdomain *regd;
3168c2ecf20Sopenharmony_ci	int ret;
3178c2ecf20Sopenharmony_ci	bool changed;
3188c2ecf20Sopenharmony_ci	const struct ieee80211_regdomain *r =
3198c2ecf20Sopenharmony_ci			rtnl_dereference(mvm->hw->wiphy->regd);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (!r)
3228c2ecf20Sopenharmony_ci		return -ENOENT;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* save the last source in case we overwrite it below */
3258c2ecf20Sopenharmony_ci	used_src = mvm->mcc_src;
3268c2ecf20Sopenharmony_ci	if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
3278c2ecf20Sopenharmony_ci		/* Notify the firmware we support wifi location updates */
3288c2ecf20Sopenharmony_ci		regd = iwl_mvm_get_current_regdomain(mvm, NULL);
3298c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(regd))
3308c2ecf20Sopenharmony_ci			kfree(regd);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Now set our last stored MCC and source */
3348c2ecf20Sopenharmony_ci	regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
3358c2ecf20Sopenharmony_ci				     &changed);
3368c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(regd))
3378c2ecf20Sopenharmony_ci		return -EIO;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* update cfg80211 if the regdomain was changed */
3408c2ecf20Sopenharmony_ci	if (changed)
3418c2ecf20Sopenharmony_ci		ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
3428c2ecf20Sopenharmony_ci	else
3438c2ecf20Sopenharmony_ci		ret = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	kfree(regd);
3468c2ecf20Sopenharmony_ci	return ret;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic const u8 he_if_types_ext_capa_sta[] = {
3508c2ecf20Sopenharmony_ci	 [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
3518c2ecf20Sopenharmony_ci	 [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
3528c2ecf20Sopenharmony_ci	 [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic const struct wiphy_iftype_ext_capab he_iftypes_ext_capa[] = {
3568c2ecf20Sopenharmony_ci	{
3578c2ecf20Sopenharmony_ci		.iftype = NL80211_IFTYPE_STATION,
3588c2ecf20Sopenharmony_ci		.extended_capabilities = he_if_types_ext_capa_sta,
3598c2ecf20Sopenharmony_ci		.extended_capabilities_mask = he_if_types_ext_capa_sta,
3608c2ecf20Sopenharmony_ci		.extended_capabilities_len = sizeof(he_if_types_ext_capa_sta),
3618c2ecf20Sopenharmony_ci	},
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic int
3658c2ecf20Sopenharmony_ciiwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
3688c2ecf20Sopenharmony_ci	*tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
3698c2ecf20Sopenharmony_ci	*rx_ant = iwl_mvm_get_valid_rx_ant(mvm);
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciint iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = mvm->hw;
3768c2ecf20Sopenharmony_ci	int num_mac, ret, i;
3778c2ecf20Sopenharmony_ci	static const u32 mvm_ciphers[] = {
3788c2ecf20Sopenharmony_ci		WLAN_CIPHER_SUITE_WEP40,
3798c2ecf20Sopenharmony_ci		WLAN_CIPHER_SUITE_WEP104,
3808c2ecf20Sopenharmony_ci		WLAN_CIPHER_SUITE_TKIP,
3818c2ecf20Sopenharmony_ci		WLAN_CIPHER_SUITE_CCMP,
3828c2ecf20Sopenharmony_ci	};
3838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3848c2ecf20Sopenharmony_ci	bool unified = fw_has_capa(&mvm->fw->ucode_capa,
3858c2ecf20Sopenharmony_ci				   IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
3868c2ecf20Sopenharmony_ci#endif
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* Tell mac80211 our characteristics */
3898c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
3908c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SPECTRUM_MGMT);
3918c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
3928c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
3938c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_PS);
3948c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
3958c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
3968c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, TIMING_BEACON_ONLY);
3978c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, CONNECTION_MONITOR);
3988c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, CHANCTX_STA_CSA);
3998c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
4008c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
4018c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
4028c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
4038c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
4048c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
4058c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
4068c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, STA_MMPDU_TXQ);
4078c2ecf20Sopenharmony_ci	/*
4088c2ecf20Sopenharmony_ci	 * On older devices, enabling TX A-MSDU occasionally leads to
4098c2ecf20Sopenharmony_ci	 * something getting messed up, the command read from the FIFO
4108c2ecf20Sopenharmony_ci	 * gets out of sync and isn't a TX command, so that we have an
4118c2ecf20Sopenharmony_ci	 * assert EDC.
4128c2ecf20Sopenharmony_ci	 *
4138c2ecf20Sopenharmony_ci	 * It's not clear where the bug is, but since we didn't used to
4148c2ecf20Sopenharmony_ci	 * support A-MSDU until moving the mac80211 iTXQs, just leave it
4158c2ecf20Sopenharmony_ci	 * for older devices. We also don't see this issue on any newer
4168c2ecf20Sopenharmony_ci	 * devices.
4178c2ecf20Sopenharmony_ci	 */
4188c2ecf20Sopenharmony_ci	if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000)
4198c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, TX_AMSDU);
4208c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, TX_FRAG_LIST);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (iwl_mvm_has_tlc_offload(mvm)) {
4238c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
4248c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, HAS_RATE_CONTROL);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (iwl_mvm_has_new_rx_api(mvm))
4288c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
4318c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_STA_PM_NOTIF)) {
4328c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, AP_LINK_PS);
4338c2ecf20Sopenharmony_ci	} else if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) {
4348c2ecf20Sopenharmony_ci		/*
4358c2ecf20Sopenharmony_ci		 * we absolutely need this for the new TX API since that comes
4368c2ecf20Sopenharmony_ci		 * with many more queues than the current code can deal with
4378c2ecf20Sopenharmony_ci		 * for station powersave
4388c2ecf20Sopenharmony_ci		 */
4398c2ecf20Sopenharmony_ci		return -EINVAL;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (mvm->trans->num_rx_queues > 1)
4438c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, USES_RSS);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (mvm->trans->max_skb_frags)
4468c2ecf20Sopenharmony_ci		hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	hw->queues = IEEE80211_MAX_QUEUES;
4498c2ecf20Sopenharmony_ci	hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
4508c2ecf20Sopenharmony_ci	hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
4518c2ecf20Sopenharmony_ci				    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
4528c2ecf20Sopenharmony_ci	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
4538c2ecf20Sopenharmony_ci		IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	hw->radiotap_timestamp.units_pos =
4568c2ecf20Sopenharmony_ci		IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US |
4578c2ecf20Sopenharmony_ci		IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ;
4588c2ecf20Sopenharmony_ci	/* this is the case for CCK frames, it's better (only 8) for OFDM */
4598c2ecf20Sopenharmony_ci	hw->radiotap_timestamp.accuracy = 22;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (!iwl_mvm_has_tlc_offload(mvm))
4628c2ecf20Sopenharmony_ci		hw->rate_control_algorithm = RS_NAME;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
4658c2ecf20Sopenharmony_ci	hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
4668c2ecf20Sopenharmony_ci	hw->max_tx_fragments = mvm->trans->max_skb_frags;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6);
4698c2ecf20Sopenharmony_ci	memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
4708c2ecf20Sopenharmony_ci	hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers);
4718c2ecf20Sopenharmony_ci	hw->wiphy->cipher_suites = mvm->ciphers;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (iwl_mvm_has_new_rx_api(mvm)) {
4748c2ecf20Sopenharmony_ci		mvm->ciphers[hw->wiphy->n_cipher_suites] =
4758c2ecf20Sopenharmony_ci			WLAN_CIPHER_SUITE_GCMP;
4768c2ecf20Sopenharmony_ci		hw->wiphy->n_cipher_suites++;
4778c2ecf20Sopenharmony_ci		mvm->ciphers[hw->wiphy->n_cipher_suites] =
4788c2ecf20Sopenharmony_ci			WLAN_CIPHER_SUITE_GCMP_256;
4798c2ecf20Sopenharmony_ci		hw->wiphy->n_cipher_suites++;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (iwlwifi_mod_params.swcrypto)
4838c2ecf20Sopenharmony_ci		IWL_ERR(mvm,
4848c2ecf20Sopenharmony_ci			"iwlmvm doesn't allow to disable HW crypto, check swcrypto module parameter\n");
4858c2ecf20Sopenharmony_ci	if (!iwlwifi_mod_params.bt_coex_active)
4868c2ecf20Sopenharmony_ci		IWL_ERR(mvm,
4878c2ecf20Sopenharmony_ci			"iwlmvm doesn't allow to disable BT Coex, check bt_coex_active module parameter\n");
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, MFP_CAPABLE);
4908c2ecf20Sopenharmony_ci	mvm->ciphers[hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
4918c2ecf20Sopenharmony_ci	hw->wiphy->n_cipher_suites++;
4928c2ecf20Sopenharmony_ci	if (iwl_mvm_has_new_rx_api(mvm)) {
4938c2ecf20Sopenharmony_ci		mvm->ciphers[hw->wiphy->n_cipher_suites] =
4948c2ecf20Sopenharmony_ci			WLAN_CIPHER_SUITE_BIP_GMAC_128;
4958c2ecf20Sopenharmony_ci		hw->wiphy->n_cipher_suites++;
4968c2ecf20Sopenharmony_ci		mvm->ciphers[hw->wiphy->n_cipher_suites] =
4978c2ecf20Sopenharmony_ci			WLAN_CIPHER_SUITE_BIP_GMAC_256;
4988c2ecf20Sopenharmony_ci		hw->wiphy->n_cipher_suites++;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/* currently FW API supports only one optional cipher scheme */
5028c2ecf20Sopenharmony_ci	if (mvm->fw->cs[0].cipher) {
5038c2ecf20Sopenharmony_ci		const struct iwl_fw_cipher_scheme *fwcs = &mvm->fw->cs[0];
5048c2ecf20Sopenharmony_ci		struct ieee80211_cipher_scheme *cs = &mvm->cs[0];
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		mvm->hw->n_cipher_schemes = 1;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		cs->cipher = le32_to_cpu(fwcs->cipher);
5098c2ecf20Sopenharmony_ci		cs->iftype = BIT(NL80211_IFTYPE_STATION);
5108c2ecf20Sopenharmony_ci		cs->hdr_len = fwcs->hdr_len;
5118c2ecf20Sopenharmony_ci		cs->pn_len = fwcs->pn_len;
5128c2ecf20Sopenharmony_ci		cs->pn_off = fwcs->pn_off;
5138c2ecf20Sopenharmony_ci		cs->key_idx_off = fwcs->key_idx_off;
5148c2ecf20Sopenharmony_ci		cs->key_idx_mask = fwcs->key_idx_mask;
5158c2ecf20Sopenharmony_ci		cs->key_idx_shift = fwcs->key_idx_shift;
5168c2ecf20Sopenharmony_ci		cs->mic_len = fwcs->mic_len;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		mvm->hw->cipher_schemes = mvm->cs;
5198c2ecf20Sopenharmony_ci		mvm->ciphers[hw->wiphy->n_cipher_suites] = cs->cipher;
5208c2ecf20Sopenharmony_ci		hw->wiphy->n_cipher_suites++;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
5248c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) {
5258c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
5268c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER);
5278c2ecf20Sopenharmony_ci		hw->wiphy->pmsr_capa = &iwl_mvm_pmsr_capa;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
5318c2ecf20Sopenharmony_ci	hw->wiphy->features |=
5328c2ecf20Sopenharmony_ci		NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
5338c2ecf20Sopenharmony_ci		NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
5348c2ecf20Sopenharmony_ci		NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	hw->sta_data_size = sizeof(struct iwl_mvm_sta);
5378c2ecf20Sopenharmony_ci	hw->vif_data_size = sizeof(struct iwl_mvm_vif);
5388c2ecf20Sopenharmony_ci	hw->chanctx_data_size = sizeof(u16);
5398c2ecf20Sopenharmony_ci	hw->txq_data_size = sizeof(struct iwl_mvm_txq);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5428c2ecf20Sopenharmony_ci		BIT(NL80211_IFTYPE_P2P_CLIENT) |
5438c2ecf20Sopenharmony_ci		BIT(NL80211_IFTYPE_AP) |
5448c2ecf20Sopenharmony_ci		BIT(NL80211_IFTYPE_P2P_GO) |
5458c2ecf20Sopenharmony_ci		BIT(NL80211_IFTYPE_P2P_DEVICE) |
5468c2ecf20Sopenharmony_ci		BIT(NL80211_IFTYPE_ADHOC);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
5498c2ecf20Sopenharmony_ci	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* The new Tx API does not allow to pass the key or keyid of a MPDU to
5528c2ecf20Sopenharmony_ci	 * the hw, preventing us to control which key(id) to use per MPDU.
5538c2ecf20Sopenharmony_ci	 * Till that's fixed we can't use Extended Key ID for the newer cards.
5548c2ecf20Sopenharmony_ci	 */
5558c2ecf20Sopenharmony_ci	if (!iwl_mvm_has_new_tx_api(mvm))
5568c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
5578c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_EXT_KEY_ID);
5588c2ecf20Sopenharmony_ci	hw->wiphy->features |= NL80211_FEATURE_HT_IBSS;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
5618c2ecf20Sopenharmony_ci	if (iwl_mvm_is_lar_supported(mvm))
5628c2ecf20Sopenharmony_ci		hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
5638c2ecf20Sopenharmony_ci	else
5648c2ecf20Sopenharmony_ci		hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
5658c2ecf20Sopenharmony_ci					       REGULATORY_DISABLE_BEACON_HINTS;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
5688c2ecf20Sopenharmony_ci	hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
5718c2ecf20Sopenharmony_ci	hw->wiphy->n_iface_combinations =
5728c2ecf20Sopenharmony_ci		ARRAY_SIZE(iwl_mvm_iface_combinations);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	hw->wiphy->max_remain_on_channel_duration = 10000;
5758c2ecf20Sopenharmony_ci	hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Extract MAC address */
5788c2ecf20Sopenharmony_ci	memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
5798c2ecf20Sopenharmony_ci	hw->wiphy->addresses = mvm->addresses;
5808c2ecf20Sopenharmony_ci	hw->wiphy->n_addresses = 1;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Extract additional MAC addresses if available */
5838c2ecf20Sopenharmony_ci	num_mac = (mvm->nvm_data->n_hw_addrs > 1) ?
5848c2ecf20Sopenharmony_ci		min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	for (i = 1; i < num_mac; i++) {
5878c2ecf20Sopenharmony_ci		memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr,
5888c2ecf20Sopenharmony_ci		       ETH_ALEN);
5898c2ecf20Sopenharmony_ci		mvm->addresses[i].addr[5]++;
5908c2ecf20Sopenharmony_ci		hw->wiphy->n_addresses++;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	iwl_mvm_reset_phy_ctxts(mvm);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK);
6008c2ecf20Sopenharmony_ci	BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
6018c2ecf20Sopenharmony_ci		     IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
6048c2ecf20Sopenharmony_ci		mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
6058c2ecf20Sopenharmony_ci	else
6068c2ecf20Sopenharmony_ci		mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (mvm->nvm_data->bands[NL80211_BAND_2GHZ].n_channels)
6098c2ecf20Sopenharmony_ci		hw->wiphy->bands[NL80211_BAND_2GHZ] =
6108c2ecf20Sopenharmony_ci			&mvm->nvm_data->bands[NL80211_BAND_2GHZ];
6118c2ecf20Sopenharmony_ci	if (mvm->nvm_data->bands[NL80211_BAND_5GHZ].n_channels) {
6128c2ecf20Sopenharmony_ci		hw->wiphy->bands[NL80211_BAND_5GHZ] =
6138c2ecf20Sopenharmony_ci			&mvm->nvm_data->bands[NL80211_BAND_5GHZ];
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		if (fw_has_capa(&mvm->fw->ucode_capa,
6168c2ecf20Sopenharmony_ci				IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
6178c2ecf20Sopenharmony_ci		    fw_has_api(&mvm->fw->ucode_capa,
6188c2ecf20Sopenharmony_ci			       IWL_UCODE_TLV_API_LQ_SS_PARAMS))
6198c2ecf20Sopenharmony_ci			hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap |=
6208c2ecf20Sopenharmony_ci				IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	hw->wiphy->hw_version = mvm->trans->hw_id;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
6268c2ecf20Sopenharmony_ci		hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
6278c2ecf20Sopenharmony_ci	else
6288c2ecf20Sopenharmony_ci		hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_reqs = 1;
6318c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
6328c2ecf20Sopenharmony_ci	hw->wiphy->max_match_sets = iwl_umac_scan_get_max_profiles(mvm->fw);
6338c2ecf20Sopenharmony_ci	/* we create the 802.11 header and zero length SSID IE. */
6348c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_ie_len =
6358c2ecf20Sopenharmony_ci		SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
6368c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS;
6378c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_plan_interval = U16_MAX;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/*
6408c2ecf20Sopenharmony_ci	 * the firmware uses u8 for num of iterations, but 0xff is saved for
6418c2ecf20Sopenharmony_ci	 * infinite loop, so the maximum number of iterations is actually 254.
6428c2ecf20Sopenharmony_ci	 */
6438c2ecf20Sopenharmony_ci	hw->wiphy->max_sched_scan_plan_iterations = 254;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
6468c2ecf20Sopenharmony_ci			       NL80211_FEATURE_LOW_PRIORITY_SCAN |
6478c2ecf20Sopenharmony_ci			       NL80211_FEATURE_P2P_GO_OPPPS |
6488c2ecf20Sopenharmony_ci			       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
6498c2ecf20Sopenharmony_ci			       NL80211_FEATURE_DYNAMIC_SMPS |
6508c2ecf20Sopenharmony_ci			       NL80211_FEATURE_STATIC_SMPS |
6518c2ecf20Sopenharmony_ci			       NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
6548c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT))
6558c2ecf20Sopenharmony_ci		hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
6568c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
6578c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT))
6588c2ecf20Sopenharmony_ci		hw->wiphy->features |= NL80211_FEATURE_QUIET;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
6618c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
6628c2ecf20Sopenharmony_ci		hw->wiphy->features |=
6638c2ecf20Sopenharmony_ci			NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
6668c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
6678c2ecf20Sopenharmony_ci		hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
6708c2ecf20Sopenharmony_ci				  WOWLAN_KEK_KCK_MATERIAL,
6718c2ecf20Sopenharmony_ci				  IWL_FW_CMD_VER_UNKNOWN) == 3)
6728c2ecf20Sopenharmony_ci		hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (fw_has_api(&mvm->fw->ucode_capa,
6758c2ecf20Sopenharmony_ci		       IWL_UCODE_TLV_API_SCAN_TSF_REPORT)) {
6768c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6778c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_SCAN_START_TIME);
6788c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6798c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_BSS_PARENT_TSF);
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (iwl_mvm_is_oce_supported(mvm)) {
6838c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6848c2ecf20Sopenharmony_ci			NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP);
6858c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6868c2ecf20Sopenharmony_ci			NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME);
6878c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6888c2ecf20Sopenharmony_ci			NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);
6898c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
6908c2ecf20Sopenharmony_ci			NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (mvm->nvm_data->sku_cap_11ax_enable &&
6948c2ecf20Sopenharmony_ci	    !iwlwifi_mod_params.disable_11ax) {
6958c2ecf20Sopenharmony_ci		hw->wiphy->iftype_ext_capab = he_iftypes_ext_capa;
6968c2ecf20Sopenharmony_ci		hw->wiphy->num_iftype_ext_capab =
6978c2ecf20Sopenharmony_ci			ARRAY_SIZE(he_iftypes_ext_capa);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
7008c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
7068c2ecf20Sopenharmony_ci	if ((unified || mvm->fw->img[IWL_UCODE_WOWLAN].num_sec) &&
7078c2ecf20Sopenharmony_ci	    mvm->trans->ops->d3_suspend &&
7088c2ecf20Sopenharmony_ci	    mvm->trans->ops->d3_resume &&
7098c2ecf20Sopenharmony_ci	    device_can_wakeup(mvm->trans->dev)) {
7108c2ecf20Sopenharmony_ci		mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
7118c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_DISCONNECT |
7128c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_EAP_IDENTITY_REQ |
7138c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_RFKILL_RELEASE |
7148c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_NET_DETECT;
7158c2ecf20Sopenharmony_ci		mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
7168c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_GTK_REKEY_FAILURE |
7178c2ecf20Sopenharmony_ci				     WIPHY_WOWLAN_4WAY_HANDSHAKE;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
7208c2ecf20Sopenharmony_ci		mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
7218c2ecf20Sopenharmony_ci		mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
7228c2ecf20Sopenharmony_ci		mvm->wowlan.max_nd_match_sets =
7238c2ecf20Sopenharmony_ci			iwl_umac_scan_get_max_profiles(mvm->fw);
7248c2ecf20Sopenharmony_ci		hw->wiphy->wowlan = &mvm->wowlan;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci#endif
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
7298c2ecf20Sopenharmony_ci	/* assign default bcast filtering configuration */
7308c2ecf20Sopenharmony_ci	mvm->bcast_filters = iwl_mvm_default_bcast_filters;
7318c2ecf20Sopenharmony_ci#endif
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	ret = iwl_mvm_leds_init(mvm);
7348c2ecf20Sopenharmony_ci	if (ret)
7358c2ecf20Sopenharmony_ci		return ret;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
7388c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) {
7398c2ecf20Sopenharmony_ci		IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
7408c2ecf20Sopenharmony_ci		hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
7418c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, TDLS_WIDER_BW);
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
7458c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) {
7468c2ecf20Sopenharmony_ci		IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
7478c2ecf20Sopenharmony_ci		hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	hw->netdev_features |= mvm->cfg->features;
7518c2ecf20Sopenharmony_ci	if (!iwl_mvm_is_csum_supported(mvm)) {
7528c2ecf20Sopenharmony_ci		hw->netdev_features &= ~(IWL_TX_CSUM_NETIF_FLAGS |
7538c2ecf20Sopenharmony_ci					 NETIF_F_RXCSUM);
7548c2ecf20Sopenharmony_ci		/* We may support SW TX CSUM */
7558c2ecf20Sopenharmony_ci		if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
7568c2ecf20Sopenharmony_ci			hw->netdev_features |= IWL_TX_CSUM_NETIF_FLAGS;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (mvm->cfg->vht_mu_mimo_supported)
7608c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
7618c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_PROTECTED_TWT))
7648c2ecf20Sopenharmony_ci		wiphy_ext_feature_set(hw->wiphy,
7658c2ecf20Sopenharmony_ci				      NL80211_EXT_FEATURE_PROTECTED_TWT);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	hw->wiphy->available_antennas_tx = iwl_mvm_get_valid_tx_ant(mvm);
7688c2ecf20Sopenharmony_ci	hw->wiphy->available_antennas_rx = iwl_mvm_get_valid_rx_ant(mvm);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ret = ieee80211_register_hw(mvm->hw);
7718c2ecf20Sopenharmony_ci	if (ret) {
7728c2ecf20Sopenharmony_ci		iwl_mvm_leds_exit(mvm);
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return ret;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic void iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
7798c2ecf20Sopenharmony_ci			   struct ieee80211_sta *sta)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	if (likely(sta)) {
7828c2ecf20Sopenharmony_ci		if (likely(iwl_mvm_tx_skb_sta(mvm, skb, sta) == 0))
7838c2ecf20Sopenharmony_ci			return;
7848c2ecf20Sopenharmony_ci	} else {
7858c2ecf20Sopenharmony_ci		if (likely(iwl_mvm_tx_skb_non_sta(mvm, skb) == 0))
7868c2ecf20Sopenharmony_ci			return;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	ieee80211_free_txskb(mvm->hw, skb);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
7938c2ecf20Sopenharmony_ci			   struct ieee80211_tx_control *control,
7948c2ecf20Sopenharmony_ci			   struct sk_buff *skb)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
7978c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta = control->sta;
7988c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
7998c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = (void *)skb->data;
8008c2ecf20Sopenharmony_ci	bool offchannel = IEEE80211_SKB_CB(skb)->flags &
8018c2ecf20Sopenharmony_ci		IEEE80211_TX_CTL_TX_OFFCHAN;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (iwl_mvm_is_radio_killed(mvm)) {
8048c2ecf20Sopenharmony_ci		IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
8058c2ecf20Sopenharmony_ci		goto drop;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (offchannel &&
8098c2ecf20Sopenharmony_ci	    !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
8108c2ecf20Sopenharmony_ci	    !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
8118c2ecf20Sopenharmony_ci		goto drop;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* treat non-bufferable MMPDUs on AP interfaces as broadcast */
8148c2ecf20Sopenharmony_ci	if ((info->control.vif->type == NL80211_IFTYPE_AP ||
8158c2ecf20Sopenharmony_ci	     info->control.vif->type == NL80211_IFTYPE_ADHOC) &&
8168c2ecf20Sopenharmony_ci	    ieee80211_is_mgmt(hdr->frame_control) &&
8178c2ecf20Sopenharmony_ci	    !ieee80211_is_bufferable_mmpdu(hdr->frame_control))
8188c2ecf20Sopenharmony_ci		sta = NULL;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/* If there is no sta, and it's not offchannel - send through AP */
8218c2ecf20Sopenharmony_ci	if (!sta && info->control.vif->type == NL80211_IFTYPE_STATION &&
8228c2ecf20Sopenharmony_ci	    !offchannel) {
8238c2ecf20Sopenharmony_ci		struct iwl_mvm_vif *mvmvif =
8248c2ecf20Sopenharmony_ci			iwl_mvm_vif_from_mac80211(info->control.vif);
8258c2ecf20Sopenharmony_ci		u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		if (ap_sta_id < mvm->fw->ucode_capa.num_stations) {
8288c2ecf20Sopenharmony_ci			/* mac80211 holds rcu read lock */
8298c2ecf20Sopenharmony_ci			sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]);
8308c2ecf20Sopenharmony_ci			if (IS_ERR_OR_NULL(sta))
8318c2ecf20Sopenharmony_ci				goto drop;
8328c2ecf20Sopenharmony_ci		}
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	iwl_mvm_tx_skb(mvm, skb, sta);
8368c2ecf20Sopenharmony_ci	return;
8378c2ecf20Sopenharmony_ci drop:
8388c2ecf20Sopenharmony_ci	ieee80211_free_txskb(hw, skb);
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_civoid iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
8448c2ecf20Sopenharmony_ci	struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
8458c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/*
8488c2ecf20Sopenharmony_ci	 * No need for threads to be pending here, they can leave the first
8498c2ecf20Sopenharmony_ci	 * taker all the work.
8508c2ecf20Sopenharmony_ci	 *
8518c2ecf20Sopenharmony_ci	 * mvmtxq->tx_request logic:
8528c2ecf20Sopenharmony_ci	 *
8538c2ecf20Sopenharmony_ci	 * If 0, no one is currently TXing, set to 1 to indicate current thread
8548c2ecf20Sopenharmony_ci	 * will now start TX and other threads should quit.
8558c2ecf20Sopenharmony_ci	 *
8568c2ecf20Sopenharmony_ci	 * If 1, another thread is currently TXing, set to 2 to indicate to
8578c2ecf20Sopenharmony_ci	 * that thread that there was another request. Since that request may
8588c2ecf20Sopenharmony_ci	 * have raced with the check whether the queue is empty, the TXing
8598c2ecf20Sopenharmony_ci	 * thread should check the queue's status one more time before leaving.
8608c2ecf20Sopenharmony_ci	 * This check is done in order to not leave any TX hanging in the queue
8618c2ecf20Sopenharmony_ci	 * until the next TX invocation (which may not even happen).
8628c2ecf20Sopenharmony_ci	 *
8638c2ecf20Sopenharmony_ci	 * If 2, another thread is currently TXing, and it will already double
8648c2ecf20Sopenharmony_ci	 * check the queue, so do nothing.
8658c2ecf20Sopenharmony_ci	 */
8668c2ecf20Sopenharmony_ci	if (atomic_fetch_add_unless(&mvmtxq->tx_request, 1, 2))
8678c2ecf20Sopenharmony_ci		return;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	rcu_read_lock();
8708c2ecf20Sopenharmony_ci	do {
8718c2ecf20Sopenharmony_ci		while (likely(!mvmtxq->stopped &&
8728c2ecf20Sopenharmony_ci			      (mvm->trans->system_pm_mode ==
8738c2ecf20Sopenharmony_ci			       IWL_PLAT_PM_MODE_DISABLED))) {
8748c2ecf20Sopenharmony_ci			skb = ieee80211_tx_dequeue(hw, txq);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci			if (!skb) {
8778c2ecf20Sopenharmony_ci				if (txq->sta)
8788c2ecf20Sopenharmony_ci					IWL_DEBUG_TX(mvm,
8798c2ecf20Sopenharmony_ci						     "TXQ of sta %pM tid %d is now empty\n",
8808c2ecf20Sopenharmony_ci						     txq->sta->addr,
8818c2ecf20Sopenharmony_ci						     txq->tid);
8828c2ecf20Sopenharmony_ci				break;
8838c2ecf20Sopenharmony_ci			}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci			iwl_mvm_tx_skb(mvm, skb, txq->sta);
8868c2ecf20Sopenharmony_ci		}
8878c2ecf20Sopenharmony_ci	} while (atomic_dec_return(&mvmtxq->tx_request));
8888c2ecf20Sopenharmony_ci	rcu_read_unlock();
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
8928c2ecf20Sopenharmony_ci				      struct ieee80211_txq *txq)
8938c2ecf20Sopenharmony_ci{
8948c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
8958c2ecf20Sopenharmony_ci	struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	/*
8988c2ecf20Sopenharmony_ci	 * Please note that racing is handled very carefully here:
8998c2ecf20Sopenharmony_ci	 * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is
9008c2ecf20Sopenharmony_ci	 * deleted afterwards.
9018c2ecf20Sopenharmony_ci	 * This means that if:
9028c2ecf20Sopenharmony_ci	 * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list):
9038c2ecf20Sopenharmony_ci	 *	queue is allocated and we can TX.
9048c2ecf20Sopenharmony_ci	 * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list):
9058c2ecf20Sopenharmony_ci	 *	a race, should defer the frame.
9068c2ecf20Sopenharmony_ci	 * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list):
9078c2ecf20Sopenharmony_ci	 *	need to allocate the queue and defer the frame.
9088c2ecf20Sopenharmony_ci	 * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list):
9098c2ecf20Sopenharmony_ci	 *	queue is already scheduled for allocation, no need to allocate,
9108c2ecf20Sopenharmony_ci	 *	should defer the frame.
9118c2ecf20Sopenharmony_ci	 */
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/* If the queue is allocated TX and return. */
9148c2ecf20Sopenharmony_ci	if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) {
9158c2ecf20Sopenharmony_ci		/*
9168c2ecf20Sopenharmony_ci		 * Check that list is empty to avoid a race where txq_id is
9178c2ecf20Sopenharmony_ci		 * already updated, but the queue allocation work wasn't
9188c2ecf20Sopenharmony_ci		 * finished
9198c2ecf20Sopenharmony_ci		 */
9208c2ecf20Sopenharmony_ci		if (unlikely(txq->sta && !list_empty(&mvmtxq->list)))
9218c2ecf20Sopenharmony_ci			return;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci		iwl_mvm_mac_itxq_xmit(hw, txq);
9248c2ecf20Sopenharmony_ci		return;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* The list is being deleted only after the queue is fully allocated. */
9288c2ecf20Sopenharmony_ci	if (!list_empty(&mvmtxq->list))
9298c2ecf20Sopenharmony_ci		return;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
9328c2ecf20Sopenharmony_ci	schedule_work(&mvm->add_stream_wk);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...)		\
9368c2ecf20Sopenharmony_ci	do {								\
9378c2ecf20Sopenharmony_ci		if (!(le16_to_cpu(_tid_bm) & BIT(_tid)))		\
9388c2ecf20Sopenharmony_ci			break;						\
9398c2ecf20Sopenharmony_ci		iwl_fw_dbg_collect_trig(&(_mvm)->fwrt, _trig, _fmt);	\
9408c2ecf20Sopenharmony_ci	} while (0)
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic void
9438c2ecf20Sopenharmony_ciiwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
9448c2ecf20Sopenharmony_ci			    struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn,
9458c2ecf20Sopenharmony_ci			    enum ieee80211_ampdu_mlme_action action)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_tlv *trig;
9488c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_ba *ba_trig;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, ieee80211_vif_to_wdev(vif),
9518c2ecf20Sopenharmony_ci				     FW_DBG_TRIGGER_BA);
9528c2ecf20Sopenharmony_ci	if (!trig)
9538c2ecf20Sopenharmony_ci		return;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	ba_trig = (void *)trig->data;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	switch (action) {
9588c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL: {
9598c2ecf20Sopenharmony_ci		struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
9608c2ecf20Sopenharmony_ci		struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid,
9638c2ecf20Sopenharmony_ci				 "TX AGG START: MAC %pM tid %d ssn %d\n",
9648c2ecf20Sopenharmony_ci				 sta->addr, tid, tid_data->ssn);
9658c2ecf20Sopenharmony_ci		break;
9668c2ecf20Sopenharmony_ci		}
9678c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
9688c2ecf20Sopenharmony_ci		CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid,
9698c2ecf20Sopenharmony_ci				 "TX AGG STOP: MAC %pM tid %d\n",
9708c2ecf20Sopenharmony_ci				 sta->addr, tid);
9718c2ecf20Sopenharmony_ci		break;
9728c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
9738c2ecf20Sopenharmony_ci		CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid,
9748c2ecf20Sopenharmony_ci				 "RX AGG START: MAC %pM tid %d ssn %d\n",
9758c2ecf20Sopenharmony_ci				 sta->addr, tid, rx_ba_ssn);
9768c2ecf20Sopenharmony_ci		break;
9778c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
9788c2ecf20Sopenharmony_ci		CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid,
9798c2ecf20Sopenharmony_ci				 "RX AGG STOP: MAC %pM tid %d\n",
9808c2ecf20Sopenharmony_ci				 sta->addr, tid);
9818c2ecf20Sopenharmony_ci		break;
9828c2ecf20Sopenharmony_ci	default:
9838c2ecf20Sopenharmony_ci		break;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
9888c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
9898c2ecf20Sopenharmony_ci				    struct ieee80211_ampdu_params *params)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
9928c2ecf20Sopenharmony_ci	int ret;
9938c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta = params->sta;
9948c2ecf20Sopenharmony_ci	enum ieee80211_ampdu_mlme_action action = params->action;
9958c2ecf20Sopenharmony_ci	u16 tid = params->tid;
9968c2ecf20Sopenharmony_ci	u16 *ssn = &params->ssn;
9978c2ecf20Sopenharmony_ci	u16 buf_size = params->buf_size;
9988c2ecf20Sopenharmony_ci	bool amsdu = params->amsdu;
9998c2ecf20Sopenharmony_ci	u16 timeout = params->timeout;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
10028c2ecf20Sopenharmony_ci		     sta->addr, tid, action);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (!(mvm->nvm_data->sku_cap_11n_enable))
10058c2ecf20Sopenharmony_ci		return -EACCES;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	switch (action) {
10108c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
10118c2ecf20Sopenharmony_ci		if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id ==
10128c2ecf20Sopenharmony_ci				iwl_mvm_sta_from_mac80211(sta)->sta_id) {
10138c2ecf20Sopenharmony_ci			struct iwl_mvm_vif *mvmvif;
10148c2ecf20Sopenharmony_ci			u16 macid = iwl_mvm_vif_from_mac80211(vif)->id;
10158c2ecf20Sopenharmony_ci			struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid];
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci			mdata->opened_rx_ba_sessions = true;
10188c2ecf20Sopenharmony_ci			mvmvif = iwl_mvm_vif_from_mac80211(vif);
10198c2ecf20Sopenharmony_ci			cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk);
10208c2ecf20Sopenharmony_ci		}
10218c2ecf20Sopenharmony_ci		if (!iwl_enable_rx_ampdu()) {
10228c2ecf20Sopenharmony_ci			ret = -EINVAL;
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		}
10258c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true, buf_size,
10268c2ecf20Sopenharmony_ci					 timeout);
10278c2ecf20Sopenharmony_ci		break;
10288c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
10298c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false, buf_size,
10308c2ecf20Sopenharmony_ci					 timeout);
10318c2ecf20Sopenharmony_ci		break;
10328c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_START:
10338c2ecf20Sopenharmony_ci		if (!iwl_enable_tx_ampdu()) {
10348c2ecf20Sopenharmony_ci			ret = -EINVAL;
10358c2ecf20Sopenharmony_ci			break;
10368c2ecf20Sopenharmony_ci		}
10378c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
10388c2ecf20Sopenharmony_ci		break;
10398c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
10408c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH:
10438c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
10448c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
10458c2ecf20Sopenharmony_ci		break;
10468c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL:
10478c2ecf20Sopenharmony_ci		ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid,
10488c2ecf20Sopenharmony_ci					      buf_size, amsdu);
10498c2ecf20Sopenharmony_ci		break;
10508c2ecf20Sopenharmony_ci	default:
10518c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
10528c2ecf20Sopenharmony_ci		ret = -EINVAL;
10538c2ecf20Sopenharmony_ci		break;
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (!ret) {
10578c2ecf20Sopenharmony_ci		u16 rx_ba_ssn = 0;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		if (action == IEEE80211_AMPDU_RX_START)
10608c2ecf20Sopenharmony_ci			rx_ba_ssn = *ssn;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci		iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid,
10638c2ecf20Sopenharmony_ci					    rx_ba_ssn, action);
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	return ret;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
10718c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = data;
10748c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	mvmvif->uploaded = false;
10778c2ecf20Sopenharmony_ci	mvmvif->ap_sta_id = IWL_MVM_INVALID_STA;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	spin_lock_bh(&mvm->time_event_lock);
10808c2ecf20Sopenharmony_ci	iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
10818c2ecf20Sopenharmony_ci	spin_unlock_bh(&mvm->time_event_lock);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	mvmvif->phy_ctxt = NULL;
10848c2ecf20Sopenharmony_ci	memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
10858c2ecf20Sopenharmony_ci	memset(&mvmvif->probe_resp_data, 0, sizeof(mvmvif->probe_resp_data));
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_cistatic void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	iwl_mvm_stop_device(mvm);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	mvm->cur_aid = 0;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	mvm->scan_status = 0;
10958c2ecf20Sopenharmony_ci	mvm->ps_disabled = false;
10968c2ecf20Sopenharmony_ci	mvm->rfkill_safe_init_done = false;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	/* just in case one was running */
10998c2ecf20Sopenharmony_ci	iwl_mvm_cleanup_roc_te(mvm);
11008c2ecf20Sopenharmony_ci	ieee80211_remain_on_channel_expired(mvm->hw);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	iwl_mvm_ftm_restart(mvm);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/*
11058c2ecf20Sopenharmony_ci	 * cleanup all interfaces, even inactive ones, as some might have
11068c2ecf20Sopenharmony_ci	 * gone down during the HW restart
11078c2ecf20Sopenharmony_ci	 */
11088c2ecf20Sopenharmony_ci	ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	mvm->p2p_device_vif = NULL;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	iwl_mvm_reset_phy_ctxts(mvm);
11138c2ecf20Sopenharmony_ci	memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
11148c2ecf20Sopenharmony_ci	memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
11158c2ecf20Sopenharmony_ci	memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	ieee80211_wake_queues(mvm->hw);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	mvm->vif_count = 0;
11208c2ecf20Sopenharmony_ci	mvm->rx_ba_sessions = 0;
11218c2ecf20Sopenharmony_ci	mvm->fwrt.dump.conf = FW_DBG_INVALID;
11228c2ecf20Sopenharmony_ci	mvm->monitor_on = false;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* keep statistics ticking */
11258c2ecf20Sopenharmony_ci	iwl_mvm_accu_radio_stats(mvm);
11268c2ecf20Sopenharmony_ci}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ciint __iwl_mvm_mac_start(struct iwl_mvm *mvm)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	int ret;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) {
11358c2ecf20Sopenharmony_ci		/*
11368c2ecf20Sopenharmony_ci		 * Now convert the HW_RESTART_REQUESTED flag to IN_HW_RESTART
11378c2ecf20Sopenharmony_ci		 * so later code will - from now on - see that we're doing it.
11388c2ecf20Sopenharmony_ci		 */
11398c2ecf20Sopenharmony_ci		set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
11408c2ecf20Sopenharmony_ci		clear_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status);
11418c2ecf20Sopenharmony_ci		/* Clean up some internal and mac80211 state on restart */
11428c2ecf20Sopenharmony_ci		iwl_mvm_restart_cleanup(mvm);
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci	ret = iwl_mvm_up(mvm);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT,
11478c2ecf20Sopenharmony_ci			       NULL);
11488c2ecf20Sopenharmony_ci	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_PERIODIC,
11498c2ecf20Sopenharmony_ci			       NULL);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
11528c2ecf20Sopenharmony_ci		/* Something went wrong - we need to finish some cleanup
11538c2ecf20Sopenharmony_ci		 * that normally iwl_mvm_mac_restart_complete() below
11548c2ecf20Sopenharmony_ci		 * would do.
11558c2ecf20Sopenharmony_ci		 */
11568c2ecf20Sopenharmony_ci		clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	return ret;
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_start(struct ieee80211_hw *hw)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
11658c2ecf20Sopenharmony_ci	int ret;
11668c2ecf20Sopenharmony_ci	int retry, max_retry = 0;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/* we are starting the mac not in error flow, and restart is enabled */
11718c2ecf20Sopenharmony_ci	if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) &&
11728c2ecf20Sopenharmony_ci	    iwlwifi_mod_params.fw_restart) {
11738c2ecf20Sopenharmony_ci		max_retry = IWL_MAX_INIT_RETRY;
11748c2ecf20Sopenharmony_ci		/*
11758c2ecf20Sopenharmony_ci		 * This will prevent mac80211 recovery flows to trigger during
11768c2ecf20Sopenharmony_ci		 * init failures
11778c2ecf20Sopenharmony_ci		 */
11788c2ecf20Sopenharmony_ci		set_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
11798c2ecf20Sopenharmony_ci	}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	for (retry = 0; retry <= max_retry; retry++) {
11828c2ecf20Sopenharmony_ci		ret = __iwl_mvm_mac_start(mvm);
11838c2ecf20Sopenharmony_ci		if (!ret)
11848c2ecf20Sopenharmony_ci			break;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "mac start retry %d\n", retry);
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci	clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	return ret;
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	int ret;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	ret = iwl_mvm_update_quotas(mvm, true, NULL);
12048c2ecf20Sopenharmony_ci	if (ret)
12058c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
12068c2ecf20Sopenharmony_ci			ret);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_END_OF_RECOVERY);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	/*
12118c2ecf20Sopenharmony_ci	 * If we have TDLS peers, remove them. We don't know the last seqno/PN
12128c2ecf20Sopenharmony_ci	 * of packets the FW sent out, so we must reconnect.
12138c2ecf20Sopenharmony_ci	 */
12148c2ecf20Sopenharmony_ci	iwl_mvm_teardown_tdls_peers(mvm);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic void
12208c2ecf20Sopenharmony_ciiwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
12218c2ecf20Sopenharmony_ci			      enum ieee80211_reconfig_type reconfig_type)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	switch (reconfig_type) {
12268c2ecf20Sopenharmony_ci	case IEEE80211_RECONFIG_TYPE_RESTART:
12278c2ecf20Sopenharmony_ci		iwl_mvm_restart_complete(mvm);
12288c2ecf20Sopenharmony_ci		break;
12298c2ecf20Sopenharmony_ci	case IEEE80211_RECONFIG_TYPE_SUSPEND:
12308c2ecf20Sopenharmony_ci		break;
12318c2ecf20Sopenharmony_ci	}
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_civoid __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	iwl_mvm_ftm_initiator_smooth_stop(mvm);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	/* firmware counters are obviously reset now, but we shouldn't
12418c2ecf20Sopenharmony_ci	 * partially track so also clear the fw_reset_accu counters.
12428c2ecf20Sopenharmony_ci	 */
12438c2ecf20Sopenharmony_ci	memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	/* async_handlers_wk is now blocked */
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, ADD_STA, 0) < 12)
12488c2ecf20Sopenharmony_ci		iwl_mvm_rm_aux_sta(mvm);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	iwl_mvm_stop_device(mvm);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	iwl_mvm_async_handlers_purge(mvm);
12538c2ecf20Sopenharmony_ci	/* async_handlers_list is empty and will stay empty: HW is stopped */
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	/*
12568c2ecf20Sopenharmony_ci	 * Clear IN_HW_RESTART and HW_RESTART_REQUESTED flag when stopping the
12578c2ecf20Sopenharmony_ci	 * hw (as restart_complete() won't be called in this case) and mac80211
12588c2ecf20Sopenharmony_ci	 * won't execute the restart.
12598c2ecf20Sopenharmony_ci	 * But make sure to cleanup interfaces that have gone down before/during
12608c2ecf20Sopenharmony_ci	 * HW restart was requested.
12618c2ecf20Sopenharmony_ci	 */
12628c2ecf20Sopenharmony_ci	if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
12638c2ecf20Sopenharmony_ci	    test_and_clear_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
12648c2ecf20Sopenharmony_ci			       &mvm->status))
12658c2ecf20Sopenharmony_ci		ieee80211_iterate_interfaces(mvm->hw, 0,
12668c2ecf20Sopenharmony_ci					     iwl_mvm_cleanup_iterator, mvm);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	/* We shouldn't have any UIDs still set.  Loop over all the UIDs to
12698c2ecf20Sopenharmony_ci	 * make sure there's nothing left there and warn if any is found.
12708c2ecf20Sopenharmony_ci	 */
12718c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
12728c2ecf20Sopenharmony_ci		int i;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci		for (i = 0; i < mvm->max_scans; i++) {
12758c2ecf20Sopenharmony_ci			if (WARN_ONCE(mvm->scan_uid_status[i],
12768c2ecf20Sopenharmony_ci				      "UMAC scan UID %d status was not cleaned\n",
12778c2ecf20Sopenharmony_ci				      i))
12788c2ecf20Sopenharmony_ci				mvm->scan_uid_status[i] = 0;
12798c2ecf20Sopenharmony_ci		}
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	flush_work(&mvm->async_handlers_wk);
12888c2ecf20Sopenharmony_ci	flush_work(&mvm->add_stream_wk);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/*
12918c2ecf20Sopenharmony_ci	 * Lock and clear the firmware running bit here already, so that
12928c2ecf20Sopenharmony_ci	 * new commands coming in elsewhere, e.g. from debugfs, will not
12938c2ecf20Sopenharmony_ci	 * be able to proceed. This is important here because one of those
12948c2ecf20Sopenharmony_ci	 * debugfs files causes the firmware dump to be triggered, and if we
12958c2ecf20Sopenharmony_ci	 * don't stop debugfs accesses before canceling that it could be
12968c2ecf20Sopenharmony_ci	 * retriggered after we flush it but before we've cleared the bit.
12978c2ecf20Sopenharmony_ci	 */
12988c2ecf20Sopenharmony_ci	clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork);
13018c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&mvm->scan_timeout_dwork);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	/*
13048c2ecf20Sopenharmony_ci	 * The work item could be running or queued if the
13058c2ecf20Sopenharmony_ci	 * ROC time event stops just as we get here.
13068c2ecf20Sopenharmony_ci	 */
13078c2ecf20Sopenharmony_ci	flush_work(&mvm->roc_done_wk);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
13108c2ecf20Sopenharmony_ci	__iwl_mvm_mac_stop(mvm);
13118c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/*
13148c2ecf20Sopenharmony_ci	 * The worker might have been waiting for the mutex, let it run and
13158c2ecf20Sopenharmony_ci	 * discover that its list is now empty.
13168c2ecf20Sopenharmony_ci	 */
13178c2ecf20Sopenharmony_ci	cancel_work_sync(&mvm->async_handlers_wk);
13188c2ecf20Sopenharmony_ci}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_cistatic struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
13218c2ecf20Sopenharmony_ci{
13228c2ecf20Sopenharmony_ci	u16 i;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_PHY_CTX; i++)
13278c2ecf20Sopenharmony_ci		if (!mvm->phy_ctxts[i].ref)
13288c2ecf20Sopenharmony_ci			return &mvm->phy_ctxts[i];
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	IWL_ERR(mvm, "No available PHY context\n");
13318c2ecf20Sopenharmony_ci	return NULL;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_cistatic int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
13358c2ecf20Sopenharmony_ci				s16 tx_power)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	int len;
13388c2ecf20Sopenharmony_ci	struct iwl_dev_tx_power_cmd cmd = {
13398c2ecf20Sopenharmony_ci		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC),
13408c2ecf20Sopenharmony_ci		.common.mac_context_id =
13418c2ecf20Sopenharmony_ci			cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
13428c2ecf20Sopenharmony_ci		.common.pwr_restriction = cpu_to_le16(8 * tx_power),
13438c2ecf20Sopenharmony_ci	};
13448c2ecf20Sopenharmony_ci	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP,
13458c2ecf20Sopenharmony_ci					   REDUCE_TX_POWER_CMD,
13468c2ecf20Sopenharmony_ci					   IWL_FW_CMD_VER_UNKNOWN);
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
13498c2ecf20Sopenharmony_ci		cmd.common.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	if (cmd_ver == 6)
13528c2ecf20Sopenharmony_ci		len = sizeof(cmd.v6);
13538c2ecf20Sopenharmony_ci	else if (fw_has_api(&mvm->fw->ucode_capa,
13548c2ecf20Sopenharmony_ci			    IWL_UCODE_TLV_API_REDUCE_TX_POWER))
13558c2ecf20Sopenharmony_ci		len = sizeof(cmd.v5);
13568c2ecf20Sopenharmony_ci	else if (fw_has_capa(&mvm->fw->ucode_capa,
13578c2ecf20Sopenharmony_ci			     IWL_UCODE_TLV_CAPA_TX_POWER_ACK))
13588c2ecf20Sopenharmony_ci		len = sizeof(cmd.v4);
13598c2ecf20Sopenharmony_ci	else
13608c2ecf20Sopenharmony_ci		len = sizeof(cmd.v3);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	/* all structs have the same common part, add it */
13638c2ecf20Sopenharmony_ci	len += sizeof(cmd.common);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
13668c2ecf20Sopenharmony_ci}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_cistatic int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
13698c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif)
13708c2ecf20Sopenharmony_ci{
13718c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
13728c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
13738c2ecf20Sopenharmony_ci	int ret;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	if (mvmvif->csa_failed) {
13788c2ecf20Sopenharmony_ci		mvmvif->csa_failed = false;
13798c2ecf20Sopenharmony_ci		ret = -EIO;
13808c2ecf20Sopenharmony_ci		goto out_unlock;
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION) {
13848c2ecf20Sopenharmony_ci		struct iwl_mvm_sta *mvmsta;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		mvmvif->csa_bcn_pending = false;
13878c2ecf20Sopenharmony_ci		mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
13888c2ecf20Sopenharmony_ci							  mvmvif->ap_sta_id);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci		if (WARN_ON(!mvmsta)) {
13918c2ecf20Sopenharmony_ci			ret = -EIO;
13928c2ecf20Sopenharmony_ci			goto out_unlock;
13938c2ecf20Sopenharmony_ci		}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci		if (!fw_has_capa(&mvm->fw->ucode_capa,
14008c2ecf20Sopenharmony_ci				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
14018c2ecf20Sopenharmony_ci			ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
14028c2ecf20Sopenharmony_ci			if (ret)
14038c2ecf20Sopenharmony_ci				goto out_unlock;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci			iwl_mvm_stop_session_protection(mvm, vif);
14068c2ecf20Sopenharmony_ci		}
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	mvmvif->ps_disabled = false;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	ret = iwl_mvm_power_update_ps(mvm);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ciout_unlock:
14148c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	return ret;
14178c2ecf20Sopenharmony_ci}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_cistatic void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
14208c2ecf20Sopenharmony_ci					 struct ieee80211_vif *vif)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
14238c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
14248c2ecf20Sopenharmony_ci	struct iwl_chan_switch_te_cmd cmd = {
14258c2ecf20Sopenharmony_ci		.mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
14268c2ecf20Sopenharmony_ci							  mvmvif->color)),
14278c2ecf20Sopenharmony_ci		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
14288c2ecf20Sopenharmony_ci	};
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "Abort CSA on mac %d\n", mvmvif->id);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
14338c2ecf20Sopenharmony_ci	if (!fw_has_capa(&mvm->fw->ucode_capa,
14348c2ecf20Sopenharmony_ci			 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))
14358c2ecf20Sopenharmony_ci		iwl_mvm_remove_csa_period(mvm, vif);
14368c2ecf20Sopenharmony_ci	else
14378c2ecf20Sopenharmony_ci		WARN_ON(iwl_mvm_send_cmd_pdu(mvm,
14388c2ecf20Sopenharmony_ci					     WIDE_ID(MAC_CONF_GROUP,
14398c2ecf20Sopenharmony_ci						     CHANNEL_SWITCH_TIME_EVENT_CMD),
14408c2ecf20Sopenharmony_ci					     0, sizeof(cmd), &cmd));
14418c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	WARN_ON(iwl_mvm_post_channel_switch(hw, vif));
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)
14478c2ecf20Sopenharmony_ci{
14488c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm;
14498c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif;
14508c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	mvmvif = container_of(wk, struct iwl_mvm_vif, csa_work.work);
14538c2ecf20Sopenharmony_ci	vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
14548c2ecf20Sopenharmony_ci	mvm = mvmvif->mvm;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	iwl_mvm_abort_channel_switch(mvm->hw, vif);
14578c2ecf20Sopenharmony_ci	ieee80211_chswitch_done(vif, false);
14588c2ecf20Sopenharmony_ci}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
14618c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
14648c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
14658c2ecf20Sopenharmony_ci	int ret;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	mvmvif->mvm = mvm;
14688c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	/*
14718c2ecf20Sopenharmony_ci	 * Not much to do here. The stack will not allow interface
14728c2ecf20Sopenharmony_ci	 * types or combinations that we didn't advertise, so we
14738c2ecf20Sopenharmony_ci	 * don't really have to check the types.
14748c2ecf20Sopenharmony_ci	 */
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	/* make sure that beacon statistics don't go backwards with FW reset */
14798c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
14808c2ecf20Sopenharmony_ci		mvmvif->beacon_stats.accu_num_beacons +=
14818c2ecf20Sopenharmony_ci			mvmvif->beacon_stats.num_beacons;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	/* Allocate resources for the MAC context, and add it to the fw  */
14848c2ecf20Sopenharmony_ci	ret = iwl_mvm_mac_ctxt_init(mvm, vif);
14858c2ecf20Sopenharmony_ci	if (ret)
14868c2ecf20Sopenharmony_ci		goto out_unlock;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	/* Counting number of interfaces is needed for legacy PM */
14918c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
14928c2ecf20Sopenharmony_ci		mvm->vif_count++;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	/*
14958c2ecf20Sopenharmony_ci	 * The AP binding flow can be done only after the beacon
14968c2ecf20Sopenharmony_ci	 * template is configured (which happens only in the mac80211
14978c2ecf20Sopenharmony_ci	 * start_ap() flow), and adding the broadcast station can happen
14988c2ecf20Sopenharmony_ci	 * only after the binding.
14998c2ecf20Sopenharmony_ci	 * In addition, since modifying the MAC before adding a bcast
15008c2ecf20Sopenharmony_ci	 * station is not allowed by the FW, delay the adding of MAC context to
15018c2ecf20Sopenharmony_ci	 * the point where we can also add the bcast station.
15028c2ecf20Sopenharmony_ci	 * In short: there's not much we can do at this point, other than
15038c2ecf20Sopenharmony_ci	 * allocating resources :)
15048c2ecf20Sopenharmony_ci	 */
15058c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP ||
15068c2ecf20Sopenharmony_ci	    vif->type == NL80211_IFTYPE_ADHOC) {
15078c2ecf20Sopenharmony_ci		ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
15088c2ecf20Sopenharmony_ci		if (ret) {
15098c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "Failed to allocate bcast sta\n");
15108c2ecf20Sopenharmony_ci			goto out_release;
15118c2ecf20Sopenharmony_ci		}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci		/*
15148c2ecf20Sopenharmony_ci		 * Only queue for this station is the mcast queue,
15158c2ecf20Sopenharmony_ci		 * which shouldn't be in TFD mask anyway
15168c2ecf20Sopenharmony_ci		 */
15178c2ecf20Sopenharmony_ci		ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta,
15188c2ecf20Sopenharmony_ci					       0, vif->type,
15198c2ecf20Sopenharmony_ci					       IWL_STA_MULTICAST);
15208c2ecf20Sopenharmony_ci		if (ret)
15218c2ecf20Sopenharmony_ci			goto out_release;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci		iwl_mvm_vif_dbgfs_register(mvm, vif);
15248c2ecf20Sopenharmony_ci		goto out_unlock;
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	mvmvif->features |= hw->netdev_features;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	ret = iwl_mvm_mac_ctxt_add(mvm, vif);
15308c2ecf20Sopenharmony_ci	if (ret)
15318c2ecf20Sopenharmony_ci		goto out_release;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	ret = iwl_mvm_power_update_mac(mvm);
15348c2ecf20Sopenharmony_ci	if (ret)
15358c2ecf20Sopenharmony_ci		goto out_remove_mac;
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	/* beacon filtering */
15388c2ecf20Sopenharmony_ci	ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
15398c2ecf20Sopenharmony_ci	if (ret)
15408c2ecf20Sopenharmony_ci		goto out_remove_mac;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	if (!mvm->bf_allowed_vif &&
15438c2ecf20Sopenharmony_ci	    vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
15448c2ecf20Sopenharmony_ci		mvm->bf_allowed_vif = mvmvif;
15458c2ecf20Sopenharmony_ci		vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
15468c2ecf20Sopenharmony_ci				     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
15478c2ecf20Sopenharmony_ci	}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	/*
15508c2ecf20Sopenharmony_ci	 * P2P_DEVICE interface does not have a channel context assigned to it,
15518c2ecf20Sopenharmony_ci	 * so a dedicated PHY context is allocated to it and the corresponding
15528c2ecf20Sopenharmony_ci	 * MAC context is bound to it at this stage.
15538c2ecf20Sopenharmony_ci	 */
15548c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
15578c2ecf20Sopenharmony_ci		if (!mvmvif->phy_ctxt) {
15588c2ecf20Sopenharmony_ci			ret = -ENOSPC;
15598c2ecf20Sopenharmony_ci			goto out_free_bf;
15608c2ecf20Sopenharmony_ci		}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
15638c2ecf20Sopenharmony_ci		ret = iwl_mvm_binding_add_vif(mvm, vif);
15648c2ecf20Sopenharmony_ci		if (ret)
15658c2ecf20Sopenharmony_ci			goto out_unref_phy;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif);
15688c2ecf20Sopenharmony_ci		if (ret)
15698c2ecf20Sopenharmony_ci			goto out_unbind;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci		/* Save a pointer to p2p device vif, so it can later be used to
15728c2ecf20Sopenharmony_ci		 * update the p2p device MAC when a GO is started/stopped */
15738c2ecf20Sopenharmony_ci		mvm->p2p_device_vif = vif;
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	iwl_mvm_tcm_add_vif(mvm, vif);
15778c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&mvmvif->csa_work,
15788c2ecf20Sopenharmony_ci			  iwl_mvm_channel_switch_disconnect_wk);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR)
15818c2ecf20Sopenharmony_ci		mvm->monitor_on = true;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	iwl_mvm_vif_dbgfs_register(mvm, vif);
15848c2ecf20Sopenharmony_ci	goto out_unlock;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci out_unbind:
15878c2ecf20Sopenharmony_ci	iwl_mvm_binding_remove_vif(mvm, vif);
15888c2ecf20Sopenharmony_ci out_unref_phy:
15898c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
15908c2ecf20Sopenharmony_ci out_free_bf:
15918c2ecf20Sopenharmony_ci	if (mvm->bf_allowed_vif == mvmvif) {
15928c2ecf20Sopenharmony_ci		mvm->bf_allowed_vif = NULL;
15938c2ecf20Sopenharmony_ci		vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
15948c2ecf20Sopenharmony_ci				       IEEE80211_VIF_SUPPORTS_CQM_RSSI);
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci out_remove_mac:
15978c2ecf20Sopenharmony_ci	mvmvif->phy_ctxt = NULL;
15988c2ecf20Sopenharmony_ci	iwl_mvm_mac_ctxt_remove(mvm, vif);
15998c2ecf20Sopenharmony_ci out_release:
16008c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
16018c2ecf20Sopenharmony_ci		mvm->vif_count--;
16028c2ecf20Sopenharmony_ci out_unlock:
16038c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	return ret;
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_cistatic void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
16098c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
16128c2ecf20Sopenharmony_ci		/*
16138c2ecf20Sopenharmony_ci		 * Flush the ROC worker which will flush the OFFCHANNEL queue.
16148c2ecf20Sopenharmony_ci		 * We assume here that all the packets sent to the OFFCHANNEL
16158c2ecf20Sopenharmony_ci		 * queue are sent in ROC session.
16168c2ecf20Sopenharmony_ci		 */
16178c2ecf20Sopenharmony_ci		flush_work(&mvm->roc_done_wk);
16188c2ecf20Sopenharmony_ci	}
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
16228c2ecf20Sopenharmony_ci					 struct ieee80211_vif *vif)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
16258c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
16268c2ecf20Sopenharmony_ci	struct iwl_probe_resp_data *probe_data;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	iwl_mvm_prepare_mac_removal(mvm, vif);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	if (!(vif->type == NL80211_IFTYPE_AP ||
16318c2ecf20Sopenharmony_ci	      vif->type == NL80211_IFTYPE_ADHOC))
16328c2ecf20Sopenharmony_ci		iwl_mvm_tcm_rm_vif(mvm, vif);
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	probe_data = rcu_dereference_protected(mvmvif->probe_resp_data,
16378c2ecf20Sopenharmony_ci					       lockdep_is_held(&mvm->mutex));
16388c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL);
16398c2ecf20Sopenharmony_ci	if (probe_data)
16408c2ecf20Sopenharmony_ci		kfree_rcu(probe_data, rcu_head);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	if (mvm->bf_allowed_vif == mvmvif) {
16438c2ecf20Sopenharmony_ci		mvm->bf_allowed_vif = NULL;
16448c2ecf20Sopenharmony_ci		vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
16458c2ecf20Sopenharmony_ci				       IEEE80211_VIF_SUPPORTS_CQM_RSSI);
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if (vif->bss_conf.ftm_responder)
16498c2ecf20Sopenharmony_ci		memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats));
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	iwl_mvm_vif_dbgfs_clean(mvm, vif);
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	/*
16548c2ecf20Sopenharmony_ci	 * For AP/GO interface, the tear down of the resources allocated to the
16558c2ecf20Sopenharmony_ci	 * interface is be handled as part of the stop_ap flow.
16568c2ecf20Sopenharmony_ci	 */
16578c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP ||
16588c2ecf20Sopenharmony_ci	    vif->type == NL80211_IFTYPE_ADHOC) {
16598c2ecf20Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE
16608c2ecf20Sopenharmony_ci		if (vif == mvm->noa_vif) {
16618c2ecf20Sopenharmony_ci			mvm->noa_vif = NULL;
16628c2ecf20Sopenharmony_ci			mvm->noa_duration = 0;
16638c2ecf20Sopenharmony_ci		}
16648c2ecf20Sopenharmony_ci#endif
16658c2ecf20Sopenharmony_ci		iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta);
16668c2ecf20Sopenharmony_ci		iwl_mvm_dealloc_bcast_sta(mvm, vif);
16678c2ecf20Sopenharmony_ci		goto out_release;
16688c2ecf20Sopenharmony_ci	}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
16718c2ecf20Sopenharmony_ci		mvm->p2p_device_vif = NULL;
16728c2ecf20Sopenharmony_ci		iwl_mvm_rm_p2p_bcast_sta(mvm, vif);
16738c2ecf20Sopenharmony_ci		iwl_mvm_binding_remove_vif(mvm, vif);
16748c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
16758c2ecf20Sopenharmony_ci		mvmvif->phy_ctxt = NULL;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
16798c2ecf20Sopenharmony_ci		mvm->vif_count--;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
16828c2ecf20Sopenharmony_ci	iwl_mvm_mac_ctxt_remove(mvm, vif);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR)
16878c2ecf20Sopenharmony_ci		mvm->monitor_on = false;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ciout_release:
16908c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
16918c2ecf20Sopenharmony_ci}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
16948c2ecf20Sopenharmony_ci{
16958c2ecf20Sopenharmony_ci	return 0;
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_cistruct iwl_mvm_mc_iter_data {
16998c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm;
17008c2ecf20Sopenharmony_ci	int port_id;
17018c2ecf20Sopenharmony_ci};
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
17048c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif)
17058c2ecf20Sopenharmony_ci{
17068c2ecf20Sopenharmony_ci	struct iwl_mvm_mc_iter_data *data = _data;
17078c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = data->mvm;
17088c2ecf20Sopenharmony_ci	struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
17098c2ecf20Sopenharmony_ci	struct iwl_host_cmd hcmd = {
17108c2ecf20Sopenharmony_ci		.id = MCAST_FILTER_CMD,
17118c2ecf20Sopenharmony_ci		.flags = CMD_ASYNC,
17128c2ecf20Sopenharmony_ci		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
17138c2ecf20Sopenharmony_ci	};
17148c2ecf20Sopenharmony_ci	int ret, len;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	/* if we don't have free ports, mcast frames will be dropped */
17178c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
17188c2ecf20Sopenharmony_ci		return;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION ||
17218c2ecf20Sopenharmony_ci	    !vif->bss_conf.assoc)
17228c2ecf20Sopenharmony_ci		return;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	cmd->port_id = data->port_id++;
17258c2ecf20Sopenharmony_ci	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
17268c2ecf20Sopenharmony_ci	len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	hcmd.len[0] = len;
17298c2ecf20Sopenharmony_ci	hcmd.data[0] = cmd;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	ret = iwl_mvm_send_cmd(mvm, &hcmd);
17328c2ecf20Sopenharmony_ci	if (ret)
17338c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
17348c2ecf20Sopenharmony_ci}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_cistatic void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
17378c2ecf20Sopenharmony_ci{
17388c2ecf20Sopenharmony_ci	struct iwl_mvm_mc_iter_data iter_data = {
17398c2ecf20Sopenharmony_ci		.mvm = mvm,
17408c2ecf20Sopenharmony_ci	};
17418c2ecf20Sopenharmony_ci	int ret;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
17468c2ecf20Sopenharmony_ci		return;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(
17498c2ecf20Sopenharmony_ci		mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
17508c2ecf20Sopenharmony_ci		iwl_mvm_mc_iface_iterator, &iter_data);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	/*
17538c2ecf20Sopenharmony_ci	 * Send a (synchronous) ech command so that we wait for the
17548c2ecf20Sopenharmony_ci	 * multiple asynchronous MCAST_FILTER_CMD commands sent by
17558c2ecf20Sopenharmony_ci	 * the interface iterator. Otherwise, we might get here over
17568c2ecf20Sopenharmony_ci	 * and over again (by userspace just sending a lot of these)
17578c2ecf20Sopenharmony_ci	 * and the CPU can send them faster than the firmware can
17588c2ecf20Sopenharmony_ci	 * process them.
17598c2ecf20Sopenharmony_ci	 * Note that the CPU is still faster - but with this we'll
17608c2ecf20Sopenharmony_ci	 * actually send fewer commands overall because the CPU will
17618c2ecf20Sopenharmony_ci	 * not schedule the work in mac80211 as frequently if it's
17628c2ecf20Sopenharmony_ci	 * still running when rescheduled (possibly multiple times).
17638c2ecf20Sopenharmony_ci	 */
17648c2ecf20Sopenharmony_ci	ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);
17658c2ecf20Sopenharmony_ci	if (ret)
17668c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to synchronize multicast groups update\n");
17678c2ecf20Sopenharmony_ci}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_cistatic u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
17708c2ecf20Sopenharmony_ci				     struct netdev_hw_addr_list *mc_list)
17718c2ecf20Sopenharmony_ci{
17728c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
17738c2ecf20Sopenharmony_ci	struct iwl_mcast_filter_cmd *cmd;
17748c2ecf20Sopenharmony_ci	struct netdev_hw_addr *addr;
17758c2ecf20Sopenharmony_ci	int addr_count;
17768c2ecf20Sopenharmony_ci	bool pass_all;
17778c2ecf20Sopenharmony_ci	int len;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	addr_count = netdev_hw_addr_list_count(mc_list);
17808c2ecf20Sopenharmony_ci	pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES ||
17818c2ecf20Sopenharmony_ci		   IWL_MVM_FW_MCAST_FILTER_PASS_ALL;
17828c2ecf20Sopenharmony_ci	if (pass_all)
17838c2ecf20Sopenharmony_ci		addr_count = 0;
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
17868c2ecf20Sopenharmony_ci	cmd = kzalloc(len, GFP_ATOMIC);
17878c2ecf20Sopenharmony_ci	if (!cmd)
17888c2ecf20Sopenharmony_ci		return 0;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	if (pass_all) {
17918c2ecf20Sopenharmony_ci		cmd->pass_all = 1;
17928c2ecf20Sopenharmony_ci		return (u64)(unsigned long)cmd;
17938c2ecf20Sopenharmony_ci	}
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	netdev_hw_addr_list_for_each(addr, mc_list) {
17968c2ecf20Sopenharmony_ci		IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
17978c2ecf20Sopenharmony_ci				   cmd->count, addr->addr);
17988c2ecf20Sopenharmony_ci		memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
17998c2ecf20Sopenharmony_ci		       addr->addr, ETH_ALEN);
18008c2ecf20Sopenharmony_ci		cmd->count++;
18018c2ecf20Sopenharmony_ci	}
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	return (u64)(unsigned long)cmd;
18048c2ecf20Sopenharmony_ci}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_cistatic void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
18078c2ecf20Sopenharmony_ci				     unsigned int changed_flags,
18088c2ecf20Sopenharmony_ci				     unsigned int *total_flags,
18098c2ecf20Sopenharmony_ci				     u64 multicast)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
18128c2ecf20Sopenharmony_ci	struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	/* replace previous configuration */
18178c2ecf20Sopenharmony_ci	kfree(mvm->mcast_filter_cmd);
18188c2ecf20Sopenharmony_ci	mvm->mcast_filter_cmd = cmd;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	if (!cmd)
18218c2ecf20Sopenharmony_ci		goto out;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	if (changed_flags & FIF_ALLMULTI)
18248c2ecf20Sopenharmony_ci		cmd->pass_all = !!(*total_flags & FIF_ALLMULTI);
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (cmd->pass_all)
18278c2ecf20Sopenharmony_ci		cmd->count = 0;
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	iwl_mvm_recalc_multicast(mvm);
18308c2ecf20Sopenharmony_ciout:
18318c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
18328c2ecf20Sopenharmony_ci	*total_flags = 0;
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_cistatic void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,
18368c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif,
18378c2ecf20Sopenharmony_ci					unsigned int filter_flags,
18388c2ecf20Sopenharmony_ci					unsigned int changed_flags)
18398c2ecf20Sopenharmony_ci{
18408c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	/* We support only filter for probe requests */
18438c2ecf20Sopenharmony_ci	if (!(changed_flags & FIF_PROBE_REQ))
18448c2ecf20Sopenharmony_ci		return;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	/* Supported only for p2p client interfaces */
18478c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
18488c2ecf20Sopenharmony_ci	    !vif->p2p)
18498c2ecf20Sopenharmony_ci		return;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
18528c2ecf20Sopenharmony_ci	iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
18538c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
18578c2ecf20Sopenharmony_cistruct iwl_bcast_iter_data {
18588c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm;
18598c2ecf20Sopenharmony_ci	struct iwl_bcast_filter_cmd *cmd;
18608c2ecf20Sopenharmony_ci	u8 current_filter;
18618c2ecf20Sopenharmony_ci};
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_cistatic void
18648c2ecf20Sopenharmony_ciiwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
18658c2ecf20Sopenharmony_ci			 const struct iwl_fw_bcast_filter *in_filter,
18668c2ecf20Sopenharmony_ci			 struct iwl_fw_bcast_filter *out_filter)
18678c2ecf20Sopenharmony_ci{
18688c2ecf20Sopenharmony_ci	struct iwl_fw_bcast_filter_attr *attr;
18698c2ecf20Sopenharmony_ci	int i;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	memcpy(out_filter, in_filter, sizeof(*out_filter));
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
18748c2ecf20Sopenharmony_ci		attr = &out_filter->attrs[i];
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci		if (!attr->mask)
18778c2ecf20Sopenharmony_ci			break;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci		switch (attr->reserved1) {
18808c2ecf20Sopenharmony_ci		case cpu_to_le16(BC_FILTER_MAGIC_IP):
18818c2ecf20Sopenharmony_ci			if (vif->bss_conf.arp_addr_cnt != 1) {
18828c2ecf20Sopenharmony_ci				attr->mask = 0;
18838c2ecf20Sopenharmony_ci				continue;
18848c2ecf20Sopenharmony_ci			}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci			attr->val = vif->bss_conf.arp_addr_list[0];
18878c2ecf20Sopenharmony_ci			break;
18888c2ecf20Sopenharmony_ci		case cpu_to_le16(BC_FILTER_MAGIC_MAC):
18898c2ecf20Sopenharmony_ci			attr->val = *(__be32 *)&vif->addr[2];
18908c2ecf20Sopenharmony_ci			break;
18918c2ecf20Sopenharmony_ci		default:
18928c2ecf20Sopenharmony_ci			break;
18938c2ecf20Sopenharmony_ci		}
18948c2ecf20Sopenharmony_ci		attr->reserved1 = 0;
18958c2ecf20Sopenharmony_ci		out_filter->num_attrs++;
18968c2ecf20Sopenharmony_ci	}
18978c2ecf20Sopenharmony_ci}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_cistatic void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
19008c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif)
19018c2ecf20Sopenharmony_ci{
19028c2ecf20Sopenharmony_ci	struct iwl_bcast_iter_data *data = _data;
19038c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = data->mvm;
19048c2ecf20Sopenharmony_ci	struct iwl_bcast_filter_cmd *cmd = data->cmd;
19058c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
19068c2ecf20Sopenharmony_ci	struct iwl_fw_bcast_mac *bcast_mac;
19078c2ecf20Sopenharmony_ci	int i;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
19108c2ecf20Sopenharmony_ci		return;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	bcast_mac = &cmd->macs[mvmvif->id];
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	/*
19158c2ecf20Sopenharmony_ci	 * enable filtering only for associated stations, but not for P2P
19168c2ecf20Sopenharmony_ci	 * Clients
19178c2ecf20Sopenharmony_ci	 */
19188c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
19198c2ecf20Sopenharmony_ci	    !vif->bss_conf.assoc)
19208c2ecf20Sopenharmony_ci		return;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	bcast_mac->default_discard = 1;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	/* copy all configured filters */
19258c2ecf20Sopenharmony_ci	for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
19268c2ecf20Sopenharmony_ci		/*
19278c2ecf20Sopenharmony_ci		 * Make sure we don't exceed our filters limit.
19288c2ecf20Sopenharmony_ci		 * if there is still a valid filter to be configured,
19298c2ecf20Sopenharmony_ci		 * be on the safe side and just allow bcast for this mac.
19308c2ecf20Sopenharmony_ci		 */
19318c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(data->current_filter >=
19328c2ecf20Sopenharmony_ci				 ARRAY_SIZE(cmd->filters))) {
19338c2ecf20Sopenharmony_ci			bcast_mac->default_discard = 0;
19348c2ecf20Sopenharmony_ci			bcast_mac->attached_filters = 0;
19358c2ecf20Sopenharmony_ci			break;
19368c2ecf20Sopenharmony_ci		}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci		iwl_mvm_set_bcast_filter(vif,
19398c2ecf20Sopenharmony_ci					 &mvm->bcast_filters[i],
19408c2ecf20Sopenharmony_ci					 &cmd->filters[data->current_filter]);
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci		/* skip current filter if it contains no attributes */
19438c2ecf20Sopenharmony_ci		if (!cmd->filters[data->current_filter].num_attrs)
19448c2ecf20Sopenharmony_ci			continue;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci		/* attach the filter to current mac */
19478c2ecf20Sopenharmony_ci		bcast_mac->attached_filters |=
19488c2ecf20Sopenharmony_ci				cpu_to_le16(BIT(data->current_filter));
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci		data->current_filter++;
19518c2ecf20Sopenharmony_ci	}
19528c2ecf20Sopenharmony_ci}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_cibool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
19558c2ecf20Sopenharmony_ci				    struct iwl_bcast_filter_cmd *cmd)
19568c2ecf20Sopenharmony_ci{
19578c2ecf20Sopenharmony_ci	struct iwl_bcast_iter_data iter_data = {
19588c2ecf20Sopenharmony_ci		.mvm = mvm,
19598c2ecf20Sopenharmony_ci		.cmd = cmd,
19608c2ecf20Sopenharmony_ci	};
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
19638c2ecf20Sopenharmony_ci		return false;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
19668c2ecf20Sopenharmony_ci	cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
19678c2ecf20Sopenharmony_ci	cmd->max_macs = ARRAY_SIZE(cmd->macs);
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
19708c2ecf20Sopenharmony_ci	/* use debugfs filters/macs if override is configured */
19718c2ecf20Sopenharmony_ci	if (mvm->dbgfs_bcast_filtering.override) {
19728c2ecf20Sopenharmony_ci		memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
19738c2ecf20Sopenharmony_ci		       sizeof(cmd->filters));
19748c2ecf20Sopenharmony_ci		memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
19758c2ecf20Sopenharmony_ci		       sizeof(cmd->macs));
19768c2ecf20Sopenharmony_ci		return true;
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci#endif
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	/* if no filters are configured, do nothing */
19818c2ecf20Sopenharmony_ci	if (!mvm->bcast_filters)
19828c2ecf20Sopenharmony_ci		return false;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	/* configure and attach these filters for each associated sta vif */
19858c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces(
19868c2ecf20Sopenharmony_ci		mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
19878c2ecf20Sopenharmony_ci		iwl_mvm_bcast_filter_iterator, &iter_data);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	return true;
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_cistatic int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
19938c2ecf20Sopenharmony_ci{
19948c2ecf20Sopenharmony_ci	struct iwl_bcast_filter_cmd cmd;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
19978c2ecf20Sopenharmony_ci		return 0;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
20008c2ecf20Sopenharmony_ci		return 0;
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
20038c2ecf20Sopenharmony_ci				    sizeof(cmd), &cmd);
20048c2ecf20Sopenharmony_ci}
20058c2ecf20Sopenharmony_ci#else
20068c2ecf20Sopenharmony_cistatic inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	return 0;
20098c2ecf20Sopenharmony_ci}
20108c2ecf20Sopenharmony_ci#endif
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_cistatic int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm,
20138c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	struct iwl_mu_group_mgmt_cmd cmd = {};
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	memcpy(cmd.membership_status, vif->bss_conf.mu_group.membership,
20188c2ecf20Sopenharmony_ci	       WLAN_MEMBERSHIP_LEN);
20198c2ecf20Sopenharmony_ci	memcpy(cmd.user_position, vif->bss_conf.mu_group.position,
20208c2ecf20Sopenharmony_ci	       WLAN_USER_POSITION_LEN);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm,
20238c2ecf20Sopenharmony_ci				    WIDE_ID(DATA_PATH_GROUP,
20248c2ecf20Sopenharmony_ci					    UPDATE_MU_GROUPS_CMD),
20258c2ecf20Sopenharmony_ci				    0, sizeof(cmd), &cmd);
20268c2ecf20Sopenharmony_ci}
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_cistatic void iwl_mvm_mu_mimo_iface_iterator(void *_data, u8 *mac,
20298c2ecf20Sopenharmony_ci					   struct ieee80211_vif *vif)
20308c2ecf20Sopenharmony_ci{
20318c2ecf20Sopenharmony_ci	if (vif->mu_mimo_owner) {
20328c2ecf20Sopenharmony_ci		struct iwl_mu_group_mgmt_notif *notif = _data;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci		/*
20358c2ecf20Sopenharmony_ci		 * MU-MIMO Group Id action frame is little endian. We treat
20368c2ecf20Sopenharmony_ci		 * the data received from firmware as if it came from the
20378c2ecf20Sopenharmony_ci		 * action frame, so no conversion is needed.
20388c2ecf20Sopenharmony_ci		 */
20398c2ecf20Sopenharmony_ci		ieee80211_update_mu_groups(vif,
20408c2ecf20Sopenharmony_ci					   (u8 *)&notif->membership_status,
20418c2ecf20Sopenharmony_ci					   (u8 *)&notif->user_position);
20428c2ecf20Sopenharmony_ci	}
20438c2ecf20Sopenharmony_ci}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_civoid iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm,
20468c2ecf20Sopenharmony_ci			       struct iwl_rx_cmd_buffer *rxb)
20478c2ecf20Sopenharmony_ci{
20488c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
20498c2ecf20Sopenharmony_ci	struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(
20528c2ecf20Sopenharmony_ci			mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
20538c2ecf20Sopenharmony_ci			iwl_mvm_mu_mimo_iface_iterator, notif);
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_cistatic u8 iwl_mvm_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci	u8 byte_num = ppe_pos_bit / 8;
20598c2ecf20Sopenharmony_ci	u8 bit_num = ppe_pos_bit % 8;
20608c2ecf20Sopenharmony_ci	u8 residue_bits;
20618c2ecf20Sopenharmony_ci	u8 res;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	if (bit_num <= 5)
20648c2ecf20Sopenharmony_ci		return (ppe[byte_num] >> bit_num) &
20658c2ecf20Sopenharmony_ci		       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1);
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	/*
20688c2ecf20Sopenharmony_ci	 * If bit_num > 5, we have to combine bits with next byte.
20698c2ecf20Sopenharmony_ci	 * Calculate how many bits we need to take from current byte (called
20708c2ecf20Sopenharmony_ci	 * here "residue_bits"), and add them to bits from next byte.
20718c2ecf20Sopenharmony_ci	 */
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	residue_bits = 8 - bit_num;
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	res = (ppe[byte_num + 1] &
20768c2ecf20Sopenharmony_ci	       (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) <<
20778c2ecf20Sopenharmony_ci	      residue_bits;
20788c2ecf20Sopenharmony_ci	res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1);
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	return res;
20818c2ecf20Sopenharmony_ci}
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_cistatic void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
20848c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif, u8 sta_id)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
20878c2ecf20Sopenharmony_ci	struct iwl_he_sta_context_cmd sta_ctxt_cmd = {
20888c2ecf20Sopenharmony_ci		.sta_id = sta_id,
20898c2ecf20Sopenharmony_ci		.tid_limit = IWL_MAX_TID_COUNT,
20908c2ecf20Sopenharmony_ci		.bss_color = vif->bss_conf.he_bss_color.color,
20918c2ecf20Sopenharmony_ci		.htc_trig_based_pkt_ext = vif->bss_conf.htc_trig_based_pkt_ext,
20928c2ecf20Sopenharmony_ci		.frame_time_rts_th =
20938c2ecf20Sopenharmony_ci			cpu_to_le16(vif->bss_conf.frame_time_rts_th),
20948c2ecf20Sopenharmony_ci	};
20958c2ecf20Sopenharmony_ci	int size = fw_has_api(&mvm->fw->ucode_capa,
20968c2ecf20Sopenharmony_ci			      IWL_UCODE_TLV_API_MBSSID_HE) ?
20978c2ecf20Sopenharmony_ci		   sizeof(sta_ctxt_cmd) :
20988c2ecf20Sopenharmony_ci		   sizeof(struct iwl_he_sta_context_cmd_v1);
20998c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
21008c2ecf20Sopenharmony_ci	u32 flags;
21018c2ecf20Sopenharmony_ci	int i;
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	rcu_read_lock();
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_ctxt_cmd.sta_id]);
21068c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(sta)) {
21078c2ecf20Sopenharmony_ci		rcu_read_unlock();
21088c2ecf20Sopenharmony_ci		WARN(1, "Can't find STA to configure HE\n");
21098c2ecf20Sopenharmony_ci		return;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	if (!sta->he_cap.has_he) {
21138c2ecf20Sopenharmony_ci		rcu_read_unlock();
21148c2ecf20Sopenharmony_ci		return;
21158c2ecf20Sopenharmony_ci	}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	flags = 0;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	/* Block 26-tone RU OFDMA transmissions */
21208c2ecf20Sopenharmony_ci	if (mvmvif->he_ru_2mhz_block)
21218c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_RU_2MHZ_BLOCK;
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	/* HTC flags */
21248c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[0] &
21258c2ecf20Sopenharmony_ci	    IEEE80211_HE_MAC_CAP0_HTC_HE)
21268c2ecf20Sopenharmony_ci		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_SUPPORT);
21278c2ecf20Sopenharmony_ci	if ((sta->he_cap.he_cap_elem.mac_cap_info[1] &
21288c2ecf20Sopenharmony_ci	      IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) ||
21298c2ecf20Sopenharmony_ci	    (sta->he_cap.he_cap_elem.mac_cap_info[2] &
21308c2ecf20Sopenharmony_ci	      IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) {
21318c2ecf20Sopenharmony_ci		u8 link_adap =
21328c2ecf20Sopenharmony_ci			((sta->he_cap.he_cap_elem.mac_cap_info[2] &
21338c2ecf20Sopenharmony_ci			  IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) +
21348c2ecf20Sopenharmony_ci			 (sta->he_cap.he_cap_elem.mac_cap_info[1] &
21358c2ecf20Sopenharmony_ci			  IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION);
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci		if (link_adap == 2)
21388c2ecf20Sopenharmony_ci			sta_ctxt_cmd.htc_flags |=
21398c2ecf20Sopenharmony_ci				cpu_to_le32(IWL_HE_HTC_LINK_ADAP_UNSOLICITED);
21408c2ecf20Sopenharmony_ci		else if (link_adap == 3)
21418c2ecf20Sopenharmony_ci			sta_ctxt_cmd.htc_flags |=
21428c2ecf20Sopenharmony_ci				cpu_to_le32(IWL_HE_HTC_LINK_ADAP_BOTH);
21438c2ecf20Sopenharmony_ci	}
21448c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
21458c2ecf20Sopenharmony_ci		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BSR_SUPP);
21468c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[3] &
21478c2ecf20Sopenharmony_ci	    IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
21488c2ecf20Sopenharmony_ci		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_OMI_SUPP);
21498c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
21508c2ecf20Sopenharmony_ci		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP);
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	/*
21538c2ecf20Sopenharmony_ci	 * Initialize the PPE thresholds to "None" (7), as described in Table
21548c2ecf20Sopenharmony_ci	 * 9-262ac of 80211.ax/D3.0.
21558c2ecf20Sopenharmony_ci	 */
21568c2ecf20Sopenharmony_ci	memset(&sta_ctxt_cmd.pkt_ext, 7, sizeof(sta_ctxt_cmd.pkt_ext));
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	/* If PPE Thresholds exist, parse them into a FW-familiar format. */
21598c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.phy_cap_info[6] &
21608c2ecf20Sopenharmony_ci	    IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
21618c2ecf20Sopenharmony_ci		u8 nss = (sta->he_cap.ppe_thres[0] &
21628c2ecf20Sopenharmony_ci			  IEEE80211_PPE_THRES_NSS_MASK) + 1;
21638c2ecf20Sopenharmony_ci		u8 ru_index_bitmap =
21648c2ecf20Sopenharmony_ci			(sta->he_cap.ppe_thres[0] &
21658c2ecf20Sopenharmony_ci			 IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK) >>
21668c2ecf20Sopenharmony_ci			IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS;
21678c2ecf20Sopenharmony_ci		u8 *ppe = &sta->he_cap.ppe_thres[0];
21688c2ecf20Sopenharmony_ci		u8 ppe_pos_bit = 7; /* Starting after PPE header */
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci		/*
21718c2ecf20Sopenharmony_ci		 * FW currently supports only nss == MAX_HE_SUPP_NSS
21728c2ecf20Sopenharmony_ci		 *
21738c2ecf20Sopenharmony_ci		 * If nss > MAX: we can ignore values we don't support
21748c2ecf20Sopenharmony_ci		 * If nss < MAX: we can set zeros in other streams
21758c2ecf20Sopenharmony_ci		 */
21768c2ecf20Sopenharmony_ci		if (nss > MAX_HE_SUPP_NSS) {
21778c2ecf20Sopenharmony_ci			IWL_INFO(mvm, "Got NSS = %d - trimming to %d\n", nss,
21788c2ecf20Sopenharmony_ci				 MAX_HE_SUPP_NSS);
21798c2ecf20Sopenharmony_ci			nss = MAX_HE_SUPP_NSS;
21808c2ecf20Sopenharmony_ci		}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci		for (i = 0; i < nss; i++) {
21838c2ecf20Sopenharmony_ci			u8 ru_index_tmp = ru_index_bitmap << 1;
21848c2ecf20Sopenharmony_ci			u8 bw;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci			for (bw = 0; bw < MAX_HE_CHANNEL_BW_INDX; bw++) {
21878c2ecf20Sopenharmony_ci				ru_index_tmp >>= 1;
21888c2ecf20Sopenharmony_ci				if (!(ru_index_tmp & 1))
21898c2ecf20Sopenharmony_ci					continue;
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci				sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][1] =
21928c2ecf20Sopenharmony_ci					iwl_mvm_he_get_ppe_val(ppe,
21938c2ecf20Sopenharmony_ci							       ppe_pos_bit);
21948c2ecf20Sopenharmony_ci				ppe_pos_bit +=
21958c2ecf20Sopenharmony_ci					IEEE80211_PPE_THRES_INFO_PPET_SIZE;
21968c2ecf20Sopenharmony_ci				sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0] =
21978c2ecf20Sopenharmony_ci					iwl_mvm_he_get_ppe_val(ppe,
21988c2ecf20Sopenharmony_ci							       ppe_pos_bit);
21998c2ecf20Sopenharmony_ci				ppe_pos_bit +=
22008c2ecf20Sopenharmony_ci					IEEE80211_PPE_THRES_INFO_PPET_SIZE;
22018c2ecf20Sopenharmony_ci			}
22028c2ecf20Sopenharmony_ci		}
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_PACKET_EXT;
22058c2ecf20Sopenharmony_ci	} else if ((sta->he_cap.he_cap_elem.phy_cap_info[9] &
22068c2ecf20Sopenharmony_ci		    IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK) !=
22078c2ecf20Sopenharmony_ci		  IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED) {
22088c2ecf20Sopenharmony_ci		int low_th = -1;
22098c2ecf20Sopenharmony_ci		int high_th = -1;
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci		/* Take the PPE thresholds from the nominal padding info */
22128c2ecf20Sopenharmony_ci		switch (sta->he_cap.he_cap_elem.phy_cap_info[9] &
22138c2ecf20Sopenharmony_ci			IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK) {
22148c2ecf20Sopenharmony_ci		case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US:
22158c2ecf20Sopenharmony_ci			low_th = IWL_HE_PKT_EXT_NONE;
22168c2ecf20Sopenharmony_ci			high_th = IWL_HE_PKT_EXT_NONE;
22178c2ecf20Sopenharmony_ci			break;
22188c2ecf20Sopenharmony_ci		case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US:
22198c2ecf20Sopenharmony_ci			low_th = IWL_HE_PKT_EXT_BPSK;
22208c2ecf20Sopenharmony_ci			high_th = IWL_HE_PKT_EXT_NONE;
22218c2ecf20Sopenharmony_ci			break;
22228c2ecf20Sopenharmony_ci		case IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US:
22238c2ecf20Sopenharmony_ci			low_th = IWL_HE_PKT_EXT_NONE;
22248c2ecf20Sopenharmony_ci			high_th = IWL_HE_PKT_EXT_BPSK;
22258c2ecf20Sopenharmony_ci			break;
22268c2ecf20Sopenharmony_ci		}
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci		/* Set the PPE thresholds accordingly */
22298c2ecf20Sopenharmony_ci		if (low_th >= 0 && high_th >= 0) {
22308c2ecf20Sopenharmony_ci			struct iwl_he_pkt_ext *pkt_ext =
22318c2ecf20Sopenharmony_ci				(struct iwl_he_pkt_ext *)&sta_ctxt_cmd.pkt_ext;
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci			for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
22348c2ecf20Sopenharmony_ci				u8 bw;
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci				for (bw = 0; bw < MAX_HE_CHANNEL_BW_INDX;
22378c2ecf20Sopenharmony_ci				     bw++) {
22388c2ecf20Sopenharmony_ci					pkt_ext->pkt_ext_qam_th[i][bw][0] =
22398c2ecf20Sopenharmony_ci						low_th;
22408c2ecf20Sopenharmony_ci					pkt_ext->pkt_ext_qam_th[i][bw][1] =
22418c2ecf20Sopenharmony_ci						high_th;
22428c2ecf20Sopenharmony_ci				}
22438c2ecf20Sopenharmony_ci			}
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci			flags |= STA_CTXT_HE_PACKET_EXT;
22468c2ecf20Sopenharmony_ci		}
22478c2ecf20Sopenharmony_ci	}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[2] &
22508c2ecf20Sopenharmony_ci	    IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP)
22518c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_32BIT_BA_BITMAP;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	if (sta->he_cap.he_cap_elem.mac_cap_info[2] &
22548c2ecf20Sopenharmony_ci	    IEEE80211_HE_MAC_CAP2_ACK_EN)
22558c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_ACK_ENABLED;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	rcu_read_unlock();
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	/* Mark MU EDCA as enabled, unless none detected on some AC */
22608c2ecf20Sopenharmony_ci	flags |= STA_CTXT_HE_MU_EDCA_CW;
22618c2ecf20Sopenharmony_ci	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
22628c2ecf20Sopenharmony_ci		struct ieee80211_he_mu_edca_param_ac_rec *mu_edca =
22638c2ecf20Sopenharmony_ci			&mvmvif->queue_params[i].mu_edca_param_rec;
22648c2ecf20Sopenharmony_ci		u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci		if (!mvmvif->queue_params[i].mu_edca) {
22678c2ecf20Sopenharmony_ci			flags &= ~STA_CTXT_HE_MU_EDCA_CW;
22688c2ecf20Sopenharmony_ci			break;
22698c2ecf20Sopenharmony_ci		}
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci		sta_ctxt_cmd.trig_based_txf[ac].cwmin =
22728c2ecf20Sopenharmony_ci			cpu_to_le16(mu_edca->ecw_min_max & 0xf);
22738c2ecf20Sopenharmony_ci		sta_ctxt_cmd.trig_based_txf[ac].cwmax =
22748c2ecf20Sopenharmony_ci			cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4);
22758c2ecf20Sopenharmony_ci		sta_ctxt_cmd.trig_based_txf[ac].aifsn =
22768c2ecf20Sopenharmony_ci			cpu_to_le16(mu_edca->aifsn);
22778c2ecf20Sopenharmony_ci		sta_ctxt_cmd.trig_based_txf[ac].mu_time =
22788c2ecf20Sopenharmony_ci			cpu_to_le16(mu_edca->mu_edca_timer);
22798c2ecf20Sopenharmony_ci	}
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	if (vif->bss_conf.uora_exists) {
22838c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_TRIG_RND_ALLOC;
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci		sta_ctxt_cmd.rand_alloc_ecwmin =
22868c2ecf20Sopenharmony_ci			vif->bss_conf.uora_ocw_range & 0x7;
22878c2ecf20Sopenharmony_ci		sta_ctxt_cmd.rand_alloc_ecwmax =
22888c2ecf20Sopenharmony_ci			(vif->bss_conf.uora_ocw_range >> 3) & 0x7;
22898c2ecf20Sopenharmony_ci	}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	if (vif->bss_conf.nontransmitted) {
22928c2ecf20Sopenharmony_ci		flags |= STA_CTXT_HE_REF_BSSID_VALID;
22938c2ecf20Sopenharmony_ci		ether_addr_copy(sta_ctxt_cmd.ref_bssid_addr,
22948c2ecf20Sopenharmony_ci				vif->bss_conf.transmitter_bssid);
22958c2ecf20Sopenharmony_ci		sta_ctxt_cmd.max_bssid_indicator =
22968c2ecf20Sopenharmony_ci			vif->bss_conf.bssid_indicator;
22978c2ecf20Sopenharmony_ci		sta_ctxt_cmd.bssid_index = vif->bss_conf.bssid_index;
22988c2ecf20Sopenharmony_ci		sta_ctxt_cmd.ema_ap = vif->bss_conf.ema_ap;
22998c2ecf20Sopenharmony_ci		sta_ctxt_cmd.profile_periodicity =
23008c2ecf20Sopenharmony_ci			vif->bss_conf.profile_periodicity;
23018c2ecf20Sopenharmony_ci	}
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	sta_ctxt_cmd.flags = cpu_to_le32(flags);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(STA_HE_CTXT_CMD,
23068c2ecf20Sopenharmony_ci						 DATA_PATH_GROUP, 0),
23078c2ecf20Sopenharmony_ci				 0, size, &sta_ctxt_cmd))
23088c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to config FW to work HE!\n");
23098c2ecf20Sopenharmony_ci}
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_cistatic void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
23128c2ecf20Sopenharmony_ci					     struct ieee80211_vif *vif,
23138c2ecf20Sopenharmony_ci					     struct ieee80211_bss_conf *bss_conf,
23148c2ecf20Sopenharmony_ci					     u32 changes)
23158c2ecf20Sopenharmony_ci{
23168c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
23178c2ecf20Sopenharmony_ci	int ret;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	/*
23208c2ecf20Sopenharmony_ci	 * Re-calculate the tsf id, as the leader-follower relations depend
23218c2ecf20Sopenharmony_ci	 * on the beacon interval, which was not known when the station
23228c2ecf20Sopenharmony_ci	 * interface was added.
23238c2ecf20Sopenharmony_ci	 */
23248c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) {
23258c2ecf20Sopenharmony_ci		if (vif->bss_conf.he_support &&
23268c2ecf20Sopenharmony_ci		    !iwlwifi_mod_params.disable_11ax)
23278c2ecf20Sopenharmony_ci			iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
23308c2ecf20Sopenharmony_ci	}
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	/* Update MU EDCA params */
23338c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_QOS && mvmvif->associated &&
23348c2ecf20Sopenharmony_ci	    bss_conf->assoc && vif->bss_conf.he_support &&
23358c2ecf20Sopenharmony_ci	    !iwlwifi_mod_params.disable_11ax)
23368c2ecf20Sopenharmony_ci		iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	/*
23398c2ecf20Sopenharmony_ci	 * If we're not associated yet, take the (new) BSSID before associating
23408c2ecf20Sopenharmony_ci	 * so the firmware knows. If we're already associated, then use the old
23418c2ecf20Sopenharmony_ci	 * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC
23428c2ecf20Sopenharmony_ci	 * branch for disassociation below.
23438c2ecf20Sopenharmony_ci	 */
23448c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BSSID && !mvmvif->associated)
23458c2ecf20Sopenharmony_ci		memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid);
23488c2ecf20Sopenharmony_ci	if (ret)
23498c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	/* after sending it once, adopt mac80211 data */
23528c2ecf20Sopenharmony_ci	memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
23538c2ecf20Sopenharmony_ci	mvmvif->associated = bss_conf->assoc;
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC) {
23568c2ecf20Sopenharmony_ci		if (bss_conf->assoc) {
23578c2ecf20Sopenharmony_ci			/* clear statistics to get clean beacon counter */
23588c2ecf20Sopenharmony_ci			iwl_mvm_request_statistics(mvm, true);
23598c2ecf20Sopenharmony_ci			memset(&mvmvif->beacon_stats, 0,
23608c2ecf20Sopenharmony_ci			       sizeof(mvmvif->beacon_stats));
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci			/* add quota for this interface */
23638c2ecf20Sopenharmony_ci			ret = iwl_mvm_update_quotas(mvm, true, NULL);
23648c2ecf20Sopenharmony_ci			if (ret) {
23658c2ecf20Sopenharmony_ci				IWL_ERR(mvm, "failed to update quotas\n");
23668c2ecf20Sopenharmony_ci				return;
23678c2ecf20Sopenharmony_ci			}
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci			if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
23708c2ecf20Sopenharmony_ci				     &mvm->status) &&
23718c2ecf20Sopenharmony_ci			    !fw_has_capa(&mvm->fw->ucode_capa,
23728c2ecf20Sopenharmony_ci					 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
23738c2ecf20Sopenharmony_ci				/*
23748c2ecf20Sopenharmony_ci				 * If we're restarting then the firmware will
23758c2ecf20Sopenharmony_ci				 * obviously have lost synchronisation with
23768c2ecf20Sopenharmony_ci				 * the AP. It will attempt to synchronise by
23778c2ecf20Sopenharmony_ci				 * itself, but we can make it more reliable by
23788c2ecf20Sopenharmony_ci				 * scheduling a session protection time event.
23798c2ecf20Sopenharmony_ci				 *
23808c2ecf20Sopenharmony_ci				 * The firmware needs to receive a beacon to
23818c2ecf20Sopenharmony_ci				 * catch up with synchronisation, use 110% of
23828c2ecf20Sopenharmony_ci				 * the beacon interval.
23838c2ecf20Sopenharmony_ci				 *
23848c2ecf20Sopenharmony_ci				 * Set a large maximum delay to allow for more
23858c2ecf20Sopenharmony_ci				 * than a single interface.
23868c2ecf20Sopenharmony_ci				 *
23878c2ecf20Sopenharmony_ci				 * For new firmware versions, rely on the
23888c2ecf20Sopenharmony_ci				 * firmware. This is relevant for DCM scenarios
23898c2ecf20Sopenharmony_ci				 * only anyway.
23908c2ecf20Sopenharmony_ci				 */
23918c2ecf20Sopenharmony_ci				u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
23928c2ecf20Sopenharmony_ci				iwl_mvm_protect_session(mvm, vif, dur, dur,
23938c2ecf20Sopenharmony_ci							5 * dur, false);
23948c2ecf20Sopenharmony_ci			}
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci			iwl_mvm_sf_update(mvm, vif, false);
23978c2ecf20Sopenharmony_ci			iwl_mvm_power_vif_assoc(mvm, vif);
23988c2ecf20Sopenharmony_ci			if (vif->p2p) {
23998c2ecf20Sopenharmony_ci				iwl_mvm_update_smps(mvm, vif,
24008c2ecf20Sopenharmony_ci						    IWL_MVM_SMPS_REQ_PROT,
24018c2ecf20Sopenharmony_ci						    IEEE80211_SMPS_DYNAMIC);
24028c2ecf20Sopenharmony_ci			}
24038c2ecf20Sopenharmony_ci		} else if (mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) {
24048c2ecf20Sopenharmony_ci			/*
24058c2ecf20Sopenharmony_ci			 * If update fails - SF might be running in associated
24068c2ecf20Sopenharmony_ci			 * mode while disassociated - which is forbidden.
24078c2ecf20Sopenharmony_ci			 */
24088c2ecf20Sopenharmony_ci			ret = iwl_mvm_sf_update(mvm, vif, false);
24098c2ecf20Sopenharmony_ci			WARN_ONCE(ret &&
24108c2ecf20Sopenharmony_ci				  !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
24118c2ecf20Sopenharmony_ci					    &mvm->status),
24128c2ecf20Sopenharmony_ci				  "Failed to update SF upon disassociation\n");
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci			/*
24158c2ecf20Sopenharmony_ci			 * If we get an assert during the connection (after the
24168c2ecf20Sopenharmony_ci			 * station has been added, but before the vif is set
24178c2ecf20Sopenharmony_ci			 * to associated), mac80211 will re-add the station and
24188c2ecf20Sopenharmony_ci			 * then configure the vif. Since the vif is not
24198c2ecf20Sopenharmony_ci			 * associated, we would remove the station here and
24208c2ecf20Sopenharmony_ci			 * this would fail the recovery.
24218c2ecf20Sopenharmony_ci			 */
24228c2ecf20Sopenharmony_ci			if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
24238c2ecf20Sopenharmony_ci				      &mvm->status)) {
24248c2ecf20Sopenharmony_ci				/*
24258c2ecf20Sopenharmony_ci				 * Remove AP station now that
24268c2ecf20Sopenharmony_ci				 * the MAC is unassoc
24278c2ecf20Sopenharmony_ci				 */
24288c2ecf20Sopenharmony_ci				ret = iwl_mvm_rm_sta_id(mvm, vif,
24298c2ecf20Sopenharmony_ci							mvmvif->ap_sta_id);
24308c2ecf20Sopenharmony_ci				if (ret)
24318c2ecf20Sopenharmony_ci					IWL_ERR(mvm,
24328c2ecf20Sopenharmony_ci						"failed to remove AP station\n");
24338c2ecf20Sopenharmony_ci
24348c2ecf20Sopenharmony_ci				mvmvif->ap_sta_id = IWL_MVM_INVALID_STA;
24358c2ecf20Sopenharmony_ci			}
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci			/* remove quota for this interface */
24388c2ecf20Sopenharmony_ci			ret = iwl_mvm_update_quotas(mvm, false, NULL);
24398c2ecf20Sopenharmony_ci			if (ret)
24408c2ecf20Sopenharmony_ci				IWL_ERR(mvm, "failed to update quotas\n");
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci			/* this will take the cleared BSSID from bss_conf */
24438c2ecf20Sopenharmony_ci			ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
24448c2ecf20Sopenharmony_ci			if (ret)
24458c2ecf20Sopenharmony_ci				IWL_ERR(mvm,
24468c2ecf20Sopenharmony_ci					"failed to update MAC %pM (clear after unassoc)\n",
24478c2ecf20Sopenharmony_ci					vif->addr);
24488c2ecf20Sopenharmony_ci		}
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci		/*
24518c2ecf20Sopenharmony_ci		 * The firmware tracks the MU-MIMO group on its own.
24528c2ecf20Sopenharmony_ci		 * However, on HW restart we should restore this data.
24538c2ecf20Sopenharmony_ci		 */
24548c2ecf20Sopenharmony_ci		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
24558c2ecf20Sopenharmony_ci		    (changes & BSS_CHANGED_MU_GROUPS) && vif->mu_mimo_owner) {
24568c2ecf20Sopenharmony_ci			ret = iwl_mvm_update_mu_groups(mvm, vif);
24578c2ecf20Sopenharmony_ci			if (ret)
24588c2ecf20Sopenharmony_ci				IWL_ERR(mvm,
24598c2ecf20Sopenharmony_ci					"failed to update VHT MU_MIMO groups\n");
24608c2ecf20Sopenharmony_ci		}
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci		iwl_mvm_recalc_multicast(mvm);
24638c2ecf20Sopenharmony_ci		iwl_mvm_configure_bcast_filter(mvm);
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci		/* reset rssi values */
24668c2ecf20Sopenharmony_ci		mvmvif->bf_data.ave_beacon_signal = 0;
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci		iwl_mvm_bt_coex_vif_change(mvm);
24698c2ecf20Sopenharmony_ci		iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
24708c2ecf20Sopenharmony_ci				    IEEE80211_SMPS_AUTOMATIC);
24718c2ecf20Sopenharmony_ci		if (fw_has_capa(&mvm->fw->ucode_capa,
24728c2ecf20Sopenharmony_ci				IWL_UCODE_TLV_CAPA_UMAC_SCAN))
24738c2ecf20Sopenharmony_ci			iwl_mvm_config_scan(mvm);
24748c2ecf20Sopenharmony_ci	}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_INFO) {
24778c2ecf20Sopenharmony_ci		/*
24788c2ecf20Sopenharmony_ci		 * We received a beacon from the associated AP so
24798c2ecf20Sopenharmony_ci		 * remove the session protection.
24808c2ecf20Sopenharmony_ci		 * A firmware with the new API will remove it automatically.
24818c2ecf20Sopenharmony_ci		 */
24828c2ecf20Sopenharmony_ci		if (!fw_has_capa(&mvm->fw->ucode_capa,
24838c2ecf20Sopenharmony_ci				 IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
24848c2ecf20Sopenharmony_ci			iwl_mvm_stop_session_protection(mvm, vif);
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci		iwl_mvm_sf_update(mvm, vif, false);
24878c2ecf20Sopenharmony_ci		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
24888c2ecf20Sopenharmony_ci	}
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS |
24918c2ecf20Sopenharmony_ci		       /*
24928c2ecf20Sopenharmony_ci			* Send power command on every beacon change,
24938c2ecf20Sopenharmony_ci			* because we may have not enabled beacon abort yet.
24948c2ecf20Sopenharmony_ci			*/
24958c2ecf20Sopenharmony_ci		       BSS_CHANGED_BEACON_INFO)) {
24968c2ecf20Sopenharmony_ci		ret = iwl_mvm_power_update_mac(mvm);
24978c2ecf20Sopenharmony_ci		if (ret)
24988c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "failed to update power mode\n");
24998c2ecf20Sopenharmony_ci	}
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_TXPOWER) {
25028c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
25038c2ecf20Sopenharmony_ci				bss_conf->txpower);
25048c2ecf20Sopenharmony_ci		iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
25058c2ecf20Sopenharmony_ci	}
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_CQM) {
25088c2ecf20Sopenharmony_ci		IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
25098c2ecf20Sopenharmony_ci		/* reset cqm events tracking */
25108c2ecf20Sopenharmony_ci		mvmvif->bf_data.last_cqm_event = 0;
25118c2ecf20Sopenharmony_ci		if (mvmvif->bf_data.bf_enabled) {
25128c2ecf20Sopenharmony_ci			ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
25138c2ecf20Sopenharmony_ci			if (ret)
25148c2ecf20Sopenharmony_ci				IWL_ERR(mvm,
25158c2ecf20Sopenharmony_ci					"failed to update CQM thresholds\n");
25168c2ecf20Sopenharmony_ci		}
25178c2ecf20Sopenharmony_ci	}
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_ARP_FILTER) {
25208c2ecf20Sopenharmony_ci		IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
25218c2ecf20Sopenharmony_ci		iwl_mvm_configure_bcast_filter(mvm);
25228c2ecf20Sopenharmony_ci	}
25238c2ecf20Sopenharmony_ci}
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_cistatic int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
25268c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif)
25278c2ecf20Sopenharmony_ci{
25288c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
25298c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
25308c2ecf20Sopenharmony_ci	int ret, i;
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci	/* Send the beacon template */
25358c2ecf20Sopenharmony_ci	ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
25368c2ecf20Sopenharmony_ci	if (ret)
25378c2ecf20Sopenharmony_ci		goto out_unlock;
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	/*
25408c2ecf20Sopenharmony_ci	 * Re-calculate the tsf id, as the leader-follower relations depend on
25418c2ecf20Sopenharmony_ci	 * the beacon interval, which was not known when the AP interface
25428c2ecf20Sopenharmony_ci	 * was added.
25438c2ecf20Sopenharmony_ci	 */
25448c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
25458c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	mvmvif->ap_assoc_sta_count = 0;
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci	/* Add the mac context */
25508c2ecf20Sopenharmony_ci	ret = iwl_mvm_mac_ctxt_add(mvm, vif);
25518c2ecf20Sopenharmony_ci	if (ret)
25528c2ecf20Sopenharmony_ci		goto out_unlock;
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	/* Perform the binding */
25558c2ecf20Sopenharmony_ci	ret = iwl_mvm_binding_add_vif(mvm, vif);
25568c2ecf20Sopenharmony_ci	if (ret)
25578c2ecf20Sopenharmony_ci		goto out_remove;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	/*
25608c2ecf20Sopenharmony_ci	 * This is not very nice, but the simplest:
25618c2ecf20Sopenharmony_ci	 * For older FWs adding the mcast sta before the bcast station may
25628c2ecf20Sopenharmony_ci	 * cause assert 0x2b00.
25638c2ecf20Sopenharmony_ci	 * This is fixed in later FW so make the order of removal depend on
25648c2ecf20Sopenharmony_ci	 * the TLV
25658c2ecf20Sopenharmony_ci	 */
25668c2ecf20Sopenharmony_ci	if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) {
25678c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_mcast_sta(mvm, vif);
25688c2ecf20Sopenharmony_ci		if (ret)
25698c2ecf20Sopenharmony_ci			goto out_unbind;
25708c2ecf20Sopenharmony_ci		/*
25718c2ecf20Sopenharmony_ci		 * Send the bcast station. At this stage the TBTT and DTIM time
25728c2ecf20Sopenharmony_ci		 * events are added and applied to the scheduler
25738c2ecf20Sopenharmony_ci		 */
25748c2ecf20Sopenharmony_ci		ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
25758c2ecf20Sopenharmony_ci		if (ret) {
25768c2ecf20Sopenharmony_ci			iwl_mvm_rm_mcast_sta(mvm, vif);
25778c2ecf20Sopenharmony_ci			goto out_unbind;
25788c2ecf20Sopenharmony_ci		}
25798c2ecf20Sopenharmony_ci	} else {
25808c2ecf20Sopenharmony_ci		/*
25818c2ecf20Sopenharmony_ci		 * Send the bcast station. At this stage the TBTT and DTIM time
25828c2ecf20Sopenharmony_ci		 * events are added and applied to the scheduler
25838c2ecf20Sopenharmony_ci		 */
25848c2ecf20Sopenharmony_ci		ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
25858c2ecf20Sopenharmony_ci		if (ret)
25868c2ecf20Sopenharmony_ci			goto out_unbind;
25878c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_mcast_sta(mvm, vif);
25888c2ecf20Sopenharmony_ci		if (ret) {
25898c2ecf20Sopenharmony_ci			iwl_mvm_send_rm_bcast_sta(mvm, vif);
25908c2ecf20Sopenharmony_ci			goto out_unbind;
25918c2ecf20Sopenharmony_ci		}
25928c2ecf20Sopenharmony_ci	}
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	/* must be set before quota calculations */
25958c2ecf20Sopenharmony_ci	mvmvif->ap_ibss_active = true;
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	/* send all the early keys to the device now */
25988c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) {
25998c2ecf20Sopenharmony_ci		struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i];
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci		if (!key)
26028c2ecf20Sopenharmony_ci			continue;
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci		mvmvif->ap_early_keys[i] = NULL;
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci		ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key);
26078c2ecf20Sopenharmony_ci		if (ret)
26088c2ecf20Sopenharmony_ci			goto out_quota_failed;
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
26128c2ecf20Sopenharmony_ci		iwl_mvm_vif_set_low_latency(mvmvif, true,
26138c2ecf20Sopenharmony_ci					    LOW_LATENCY_VIF_TYPE);
26148c2ecf20Sopenharmony_ci		iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);
26158c2ecf20Sopenharmony_ci	}
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_ci	/* power updated needs to be done before quotas */
26188c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	ret = iwl_mvm_update_quotas(mvm, false, NULL);
26218c2ecf20Sopenharmony_ci	if (ret)
26228c2ecf20Sopenharmony_ci		goto out_quota_failed;
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
26258c2ecf20Sopenharmony_ci	if (vif->p2p && mvm->p2p_device_vif)
26268c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_vif_change(mvm);
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	/* we don't support TDLS during DCM */
26318c2ecf20Sopenharmony_ci	if (iwl_mvm_phy_ctx_count(mvm) > 1)
26328c2ecf20Sopenharmony_ci		iwl_mvm_teardown_tdls_peers(mvm);
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	iwl_mvm_ftm_restart_responder(mvm, vif);
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	goto out_unlock;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ciout_quota_failed:
26398c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
26408c2ecf20Sopenharmony_ci	mvmvif->ap_ibss_active = false;
26418c2ecf20Sopenharmony_ci	iwl_mvm_send_rm_bcast_sta(mvm, vif);
26428c2ecf20Sopenharmony_ci	iwl_mvm_rm_mcast_sta(mvm, vif);
26438c2ecf20Sopenharmony_ciout_unbind:
26448c2ecf20Sopenharmony_ci	iwl_mvm_binding_remove_vif(mvm, vif);
26458c2ecf20Sopenharmony_ciout_remove:
26468c2ecf20Sopenharmony_ci	iwl_mvm_mac_ctxt_remove(mvm, vif);
26478c2ecf20Sopenharmony_ciout_unlock:
26488c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
26498c2ecf20Sopenharmony_ci	return ret;
26508c2ecf20Sopenharmony_ci}
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_cistatic void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
26538c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif)
26548c2ecf20Sopenharmony_ci{
26558c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
26568c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci	iwl_mvm_prepare_mac_removal(mvm, vif);
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	/* Handle AP stop while in CSA */
26638c2ecf20Sopenharmony_ci	if (rcu_access_pointer(mvm->csa_vif) == vif) {
26648c2ecf20Sopenharmony_ci		iwl_mvm_remove_time_event(mvm, mvmvif,
26658c2ecf20Sopenharmony_ci					  &mvmvif->time_event_data);
26668c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(mvm->csa_vif, NULL);
26678c2ecf20Sopenharmony_ci		mvmvif->csa_countdown = false;
26688c2ecf20Sopenharmony_ci	}
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
26718c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
26728c2ecf20Sopenharmony_ci		mvm->csa_tx_block_bcn_timeout = 0;
26738c2ecf20Sopenharmony_ci	}
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	mvmvif->ap_ibss_active = false;
26768c2ecf20Sopenharmony_ci	mvm->ap_last_beacon_gp2 = 0;
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
26798c2ecf20Sopenharmony_ci		iwl_mvm_vif_set_low_latency(mvmvif, false,
26808c2ecf20Sopenharmony_ci					    LOW_LATENCY_VIF_TYPE);
26818c2ecf20Sopenharmony_ci		iwl_mvm_send_low_latency_cmd(mvm, false,  mvmvif->id);
26828c2ecf20Sopenharmony_ci	}
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_vif_change(mvm);
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
26878c2ecf20Sopenharmony_ci	if (vif->p2p && mvm->p2p_device_vif)
26888c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	iwl_mvm_update_quotas(mvm, false, NULL);
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	iwl_mvm_ftm_responder_clear(mvm, vif);
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	/*
26958c2ecf20Sopenharmony_ci	 * This is not very nice, but the simplest:
26968c2ecf20Sopenharmony_ci	 * For older FWs removing the mcast sta before the bcast station may
26978c2ecf20Sopenharmony_ci	 * cause assert 0x2b00.
26988c2ecf20Sopenharmony_ci	 * This is fixed in later FW (which will stop beaconing when removing
26998c2ecf20Sopenharmony_ci	 * bcast station).
27008c2ecf20Sopenharmony_ci	 * So make the order of removal depend on the TLV
27018c2ecf20Sopenharmony_ci	 */
27028c2ecf20Sopenharmony_ci	if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE))
27038c2ecf20Sopenharmony_ci		iwl_mvm_rm_mcast_sta(mvm, vif);
27048c2ecf20Sopenharmony_ci	iwl_mvm_send_rm_bcast_sta(mvm, vif);
27058c2ecf20Sopenharmony_ci	if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE))
27068c2ecf20Sopenharmony_ci		iwl_mvm_rm_mcast_sta(mvm, vif);
27078c2ecf20Sopenharmony_ci	iwl_mvm_binding_remove_vif(mvm, vif);
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	iwl_mvm_mac_ctxt_remove(mvm, vif);
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
27148c2ecf20Sopenharmony_ci}
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_cistatic void
27178c2ecf20Sopenharmony_ciiwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
27188c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
27198c2ecf20Sopenharmony_ci				 struct ieee80211_bss_conf *bss_conf,
27208c2ecf20Sopenharmony_ci				 u32 changes)
27218c2ecf20Sopenharmony_ci{
27228c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci	/* Changes will be applied when the AP/IBSS is started */
27258c2ecf20Sopenharmony_ci	if (!mvmvif->ap_ibss_active)
27268c2ecf20Sopenharmony_ci		return;
27278c2ecf20Sopenharmony_ci
27288c2ecf20Sopenharmony_ci	if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
27298c2ecf20Sopenharmony_ci		       BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) &&
27308c2ecf20Sopenharmony_ci	    iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL))
27318c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	/* Need to send a new beacon template to the FW */
27348c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON &&
27358c2ecf20Sopenharmony_ci	    iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
27368c2ecf20Sopenharmony_ci		IWL_WARN(mvm, "Failed updating beacon data\n");
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_TXPOWER) {
27398c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
27408c2ecf20Sopenharmony_ci				bss_conf->txpower);
27418c2ecf20Sopenharmony_ci		iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
27428c2ecf20Sopenharmony_ci	}
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_FTM_RESPONDER) {
27458c2ecf20Sopenharmony_ci		int ret = iwl_mvm_ftm_start_responder(mvm, vif);
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci		if (ret)
27488c2ecf20Sopenharmony_ci			IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n",
27498c2ecf20Sopenharmony_ci				 ret);
27508c2ecf20Sopenharmony_ci	}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci}
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_cistatic void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
27558c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif,
27568c2ecf20Sopenharmony_ci				     struct ieee80211_bss_conf *bss_conf,
27578c2ecf20Sopenharmony_ci				     u32 changes)
27588c2ecf20Sopenharmony_ci{
27598c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
27648c2ecf20Sopenharmony_ci		iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	switch (vif->type) {
27678c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
27688c2ecf20Sopenharmony_ci		iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
27698c2ecf20Sopenharmony_ci		break;
27708c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
27718c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
27728c2ecf20Sopenharmony_ci		iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);
27738c2ecf20Sopenharmony_ci		break;
27748c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
27758c2ecf20Sopenharmony_ci		if (changes & BSS_CHANGED_MU_GROUPS)
27768c2ecf20Sopenharmony_ci			iwl_mvm_update_mu_groups(mvm, vif);
27778c2ecf20Sopenharmony_ci		break;
27788c2ecf20Sopenharmony_ci	default:
27798c2ecf20Sopenharmony_ci		/* shouldn't happen */
27808c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
27818c2ecf20Sopenharmony_ci	}
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
27848c2ecf20Sopenharmony_ci}
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
27878c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif,
27888c2ecf20Sopenharmony_ci			       struct ieee80211_scan_request *hw_req)
27898c2ecf20Sopenharmony_ci{
27908c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
27918c2ecf20Sopenharmony_ci	int ret;
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci	if (hw_req->req.n_channels == 0 ||
27948c2ecf20Sopenharmony_ci	    hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels)
27958c2ecf20Sopenharmony_ci		return -EINVAL;
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
27988c2ecf20Sopenharmony_ci	ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
27998c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_ci	return ret;
28028c2ecf20Sopenharmony_ci}
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
28058c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif)
28068c2ecf20Sopenharmony_ci{
28078c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
28108c2ecf20Sopenharmony_ci
28118c2ecf20Sopenharmony_ci	/* Due to a race condition, it's possible that mac80211 asks
28128c2ecf20Sopenharmony_ci	 * us to stop a hw_scan when it's already stopped.  This can
28138c2ecf20Sopenharmony_ci	 * happen, for instance, if we stopped the scan ourselves,
28148c2ecf20Sopenharmony_ci	 * called ieee80211_scan_completed() and the userspace called
28158c2ecf20Sopenharmony_ci	 * cancel scan scan before ieee80211_scan_work() could run.
28168c2ecf20Sopenharmony_ci	 * To handle that, simply return if the scan is not running.
28178c2ecf20Sopenharmony_ci	*/
28188c2ecf20Sopenharmony_ci	if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
28198c2ecf20Sopenharmony_ci		iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
28228c2ecf20Sopenharmony_ci}
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_cistatic void
28258c2ecf20Sopenharmony_ciiwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
28268c2ecf20Sopenharmony_ci				  struct ieee80211_sta *sta, u16 tids,
28278c2ecf20Sopenharmony_ci				  int num_frames,
28288c2ecf20Sopenharmony_ci				  enum ieee80211_frame_release_type reason,
28298c2ecf20Sopenharmony_ci				  bool more_data)
28308c2ecf20Sopenharmony_ci{
28318c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	/* Called when we need to transmit (a) frame(s) from mac80211 */
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
28368c2ecf20Sopenharmony_ci					  tids, more_data, false);
28378c2ecf20Sopenharmony_ci}
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_cistatic void
28408c2ecf20Sopenharmony_ciiwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
28418c2ecf20Sopenharmony_ci				    struct ieee80211_sta *sta, u16 tids,
28428c2ecf20Sopenharmony_ci				    int num_frames,
28438c2ecf20Sopenharmony_ci				    enum ieee80211_frame_release_type reason,
28448c2ecf20Sopenharmony_ci				    bool more_data)
28458c2ecf20Sopenharmony_ci{
28468c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_ci	/* Called when we need to transmit (a) frame(s) from agg or dqa queue */
28498c2ecf20Sopenharmony_ci
28508c2ecf20Sopenharmony_ci	iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
28518c2ecf20Sopenharmony_ci					  tids, more_data, true);
28528c2ecf20Sopenharmony_ci}
28538c2ecf20Sopenharmony_ci
28548c2ecf20Sopenharmony_cistatic void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
28558c2ecf20Sopenharmony_ci				     enum sta_notify_cmd cmd,
28568c2ecf20Sopenharmony_ci				     struct ieee80211_sta *sta)
28578c2ecf20Sopenharmony_ci{
28588c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
28598c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
28608c2ecf20Sopenharmony_ci	unsigned long txqs = 0, tids = 0;
28618c2ecf20Sopenharmony_ci	int tid;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	/*
28648c2ecf20Sopenharmony_ci	 * If we have TVQM then we get too high queue numbers - luckily
28658c2ecf20Sopenharmony_ci	 * we really shouldn't get here with that because such hardware
28668c2ecf20Sopenharmony_ci	 * should have firmware supporting buffer station offload.
28678c2ecf20Sopenharmony_ci	 */
28688c2ecf20Sopenharmony_ci	if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
28698c2ecf20Sopenharmony_ci		return;
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	spin_lock_bh(&mvmsta->lock);
28728c2ecf20Sopenharmony_ci	for (tid = 0; tid < ARRAY_SIZE(mvmsta->tid_data); tid++) {
28738c2ecf20Sopenharmony_ci		struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci		if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE)
28768c2ecf20Sopenharmony_ci			continue;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci		__set_bit(tid_data->txq_id, &txqs);
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci		if (iwl_mvm_tid_queued(mvm, tid_data) == 0)
28818c2ecf20Sopenharmony_ci			continue;
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_ci		__set_bit(tid, &tids);
28848c2ecf20Sopenharmony_ci	}
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	switch (cmd) {
28878c2ecf20Sopenharmony_ci	case STA_NOTIFY_SLEEP:
28888c2ecf20Sopenharmony_ci		for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
28898c2ecf20Sopenharmony_ci			ieee80211_sta_set_buffered(sta, tid, true);
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci		if (txqs)
28928c2ecf20Sopenharmony_ci			iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
28938c2ecf20Sopenharmony_ci		/*
28948c2ecf20Sopenharmony_ci		 * The fw updates the STA to be asleep. Tx packets on the Tx
28958c2ecf20Sopenharmony_ci		 * queues to this station will not be transmitted. The fw will
28968c2ecf20Sopenharmony_ci		 * send a Tx response with TX_STATUS_FAIL_DEST_PS.
28978c2ecf20Sopenharmony_ci		 */
28988c2ecf20Sopenharmony_ci		break;
28998c2ecf20Sopenharmony_ci	case STA_NOTIFY_AWAKE:
29008c2ecf20Sopenharmony_ci		if (WARN_ON(mvmsta->sta_id == IWL_MVM_INVALID_STA))
29018c2ecf20Sopenharmony_ci			break;
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_ci		if (txqs)
29048c2ecf20Sopenharmony_ci			iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
29058c2ecf20Sopenharmony_ci		iwl_mvm_sta_modify_ps_wake(mvm, sta);
29068c2ecf20Sopenharmony_ci		break;
29078c2ecf20Sopenharmony_ci	default:
29088c2ecf20Sopenharmony_ci		break;
29098c2ecf20Sopenharmony_ci	}
29108c2ecf20Sopenharmony_ci	spin_unlock_bh(&mvmsta->lock);
29118c2ecf20Sopenharmony_ci}
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
29148c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif,
29158c2ecf20Sopenharmony_ci				   enum sta_notify_cmd cmd,
29168c2ecf20Sopenharmony_ci				   struct ieee80211_sta *sta)
29178c2ecf20Sopenharmony_ci{
29188c2ecf20Sopenharmony_ci	__iwl_mvm_mac_sta_notify(hw, cmd, sta);
29198c2ecf20Sopenharmony_ci}
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_civoid iwl_mvm_sta_pm_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
29228c2ecf20Sopenharmony_ci{
29238c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
29248c2ecf20Sopenharmony_ci	struct iwl_mvm_pm_state_notification *notif = (void *)pkt->data;
29258c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
29268c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta;
29278c2ecf20Sopenharmony_ci	bool sleeping = (notif->type != IWL_MVM_PM_EVENT_AWAKE);
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci	if (WARN_ON(notif->sta_id >= mvm->fw->ucode_capa.num_stations))
29308c2ecf20Sopenharmony_ci		return;
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci	rcu_read_lock();
29338c2ecf20Sopenharmony_ci	sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]);
29348c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR_OR_NULL(sta))) {
29358c2ecf20Sopenharmony_ci		rcu_read_unlock();
29368c2ecf20Sopenharmony_ci		return;
29378c2ecf20Sopenharmony_ci	}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci	mvmsta = iwl_mvm_sta_from_mac80211(sta);
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	if (!mvmsta->vif ||
29428c2ecf20Sopenharmony_ci	    mvmsta->vif->type != NL80211_IFTYPE_AP) {
29438c2ecf20Sopenharmony_ci		rcu_read_unlock();
29448c2ecf20Sopenharmony_ci		return;
29458c2ecf20Sopenharmony_ci	}
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	if (mvmsta->sleeping != sleeping) {
29488c2ecf20Sopenharmony_ci		mvmsta->sleeping = sleeping;
29498c2ecf20Sopenharmony_ci		__iwl_mvm_mac_sta_notify(mvm->hw,
29508c2ecf20Sopenharmony_ci			sleeping ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE,
29518c2ecf20Sopenharmony_ci			sta);
29528c2ecf20Sopenharmony_ci		ieee80211_sta_ps_transition(sta, sleeping);
29538c2ecf20Sopenharmony_ci	}
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	if (sleeping) {
29568c2ecf20Sopenharmony_ci		switch (notif->type) {
29578c2ecf20Sopenharmony_ci		case IWL_MVM_PM_EVENT_AWAKE:
29588c2ecf20Sopenharmony_ci		case IWL_MVM_PM_EVENT_ASLEEP:
29598c2ecf20Sopenharmony_ci			break;
29608c2ecf20Sopenharmony_ci		case IWL_MVM_PM_EVENT_UAPSD:
29618c2ecf20Sopenharmony_ci			ieee80211_sta_uapsd_trigger(sta, IEEE80211_NUM_TIDS);
29628c2ecf20Sopenharmony_ci			break;
29638c2ecf20Sopenharmony_ci		case IWL_MVM_PM_EVENT_PS_POLL:
29648c2ecf20Sopenharmony_ci			ieee80211_sta_pspoll(sta);
29658c2ecf20Sopenharmony_ci			break;
29668c2ecf20Sopenharmony_ci		default:
29678c2ecf20Sopenharmony_ci			break;
29688c2ecf20Sopenharmony_ci		}
29698c2ecf20Sopenharmony_ci	}
29708c2ecf20Sopenharmony_ci
29718c2ecf20Sopenharmony_ci	rcu_read_unlock();
29728c2ecf20Sopenharmony_ci}
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_cistatic void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
29758c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
29768c2ecf20Sopenharmony_ci				       struct ieee80211_sta *sta)
29778c2ecf20Sopenharmony_ci{
29788c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
29798c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci	/*
29828c2ecf20Sopenharmony_ci	 * This is called before mac80211 does RCU synchronisation,
29838c2ecf20Sopenharmony_ci	 * so here we already invalidate our internal RCU-protected
29848c2ecf20Sopenharmony_ci	 * station pointer. The rest of the code will thus no longer
29858c2ecf20Sopenharmony_ci	 * be able to find the station this way, and we don't rely
29868c2ecf20Sopenharmony_ci	 * on further RCU synchronisation after the sta_state()
29878c2ecf20Sopenharmony_ci	 * callback deleted the station.
29888c2ecf20Sopenharmony_ci	 */
29898c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
29908c2ecf20Sopenharmony_ci	if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
29918c2ecf20Sopenharmony_ci		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
29928c2ecf20Sopenharmony_ci				   ERR_PTR(-ENOENT));
29938c2ecf20Sopenharmony_ci
29948c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
29958c2ecf20Sopenharmony_ci}
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_cistatic void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
29988c2ecf20Sopenharmony_ci				const u8 *bssid)
29998c2ecf20Sopenharmony_ci{
30008c2ecf20Sopenharmony_ci	int i;
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
30038c2ecf20Sopenharmony_ci		struct iwl_mvm_tcm_mac *mdata;
30048c2ecf20Sopenharmony_ci
30058c2ecf20Sopenharmony_ci		mdata = &mvm->tcm.data[iwl_mvm_vif_from_mac80211(vif)->id];
30068c2ecf20Sopenharmony_ci		ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
30078c2ecf20Sopenharmony_ci		mdata->opened_rx_ba_sessions = false;
30088c2ecf20Sopenharmony_ci	}
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
30118c2ecf20Sopenharmony_ci		return;
30128c2ecf20Sopenharmony_ci
30138c2ecf20Sopenharmony_ci	if (vif->p2p && !iwl_mvm_is_p2p_scm_uapsd_supported(mvm)) {
30148c2ecf20Sopenharmony_ci		vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
30158c2ecf20Sopenharmony_ci		return;
30168c2ecf20Sopenharmony_ci	}
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_ci	if (!vif->p2p &&
30198c2ecf20Sopenharmony_ci	    (iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_BSS)) {
30208c2ecf20Sopenharmony_ci		vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
30218c2ecf20Sopenharmony_ci		return;
30228c2ecf20Sopenharmony_ci	}
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++) {
30258c2ecf20Sopenharmony_ci		if (ether_addr_equal(mvm->uapsd_noagg_bssids[i].addr, bssid)) {
30268c2ecf20Sopenharmony_ci			vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
30278c2ecf20Sopenharmony_ci			return;
30288c2ecf20Sopenharmony_ci		}
30298c2ecf20Sopenharmony_ci	}
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci	vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
30328c2ecf20Sopenharmony_ci}
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_cistatic void
30358c2ecf20Sopenharmony_ciiwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm,
30368c2ecf20Sopenharmony_ci			   struct ieee80211_vif *vif, u8 *peer_addr,
30378c2ecf20Sopenharmony_ci			   enum nl80211_tdls_operation action)
30388c2ecf20Sopenharmony_ci{
30398c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_tlv *trig;
30408c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_tdls *tdls_trig;
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, ieee80211_vif_to_wdev(vif),
30438c2ecf20Sopenharmony_ci				     FW_DBG_TRIGGER_TDLS);
30448c2ecf20Sopenharmony_ci	if (!trig)
30458c2ecf20Sopenharmony_ci		return;
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci	tdls_trig = (void *)trig->data;
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ci	if (!(tdls_trig->action_bitmap & BIT(action)))
30508c2ecf20Sopenharmony_ci		return;
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci	if (tdls_trig->peer_mode &&
30538c2ecf20Sopenharmony_ci	    memcmp(tdls_trig->peer, peer_addr, ETH_ALEN) != 0)
30548c2ecf20Sopenharmony_ci		return;
30558c2ecf20Sopenharmony_ci
30568c2ecf20Sopenharmony_ci	iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
30578c2ecf20Sopenharmony_ci				"TDLS event occurred, peer %pM, action %d",
30588c2ecf20Sopenharmony_ci				peer_addr, action);
30598c2ecf20Sopenharmony_ci}
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_cistruct iwl_mvm_he_obss_narrow_bw_ru_data {
30628c2ecf20Sopenharmony_ci	bool tolerated;
30638c2ecf20Sopenharmony_ci};
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_cistatic void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
30668c2ecf20Sopenharmony_ci						    struct cfg80211_bss *bss,
30678c2ecf20Sopenharmony_ci						    void *_data)
30688c2ecf20Sopenharmony_ci{
30698c2ecf20Sopenharmony_ci	struct iwl_mvm_he_obss_narrow_bw_ru_data *data = _data;
30708c2ecf20Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
30718c2ecf20Sopenharmony_ci	const struct element *elem;
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	rcu_read_lock();
30748c2ecf20Sopenharmony_ci	ies = rcu_dereference(bss->ies);
30758c2ecf20Sopenharmony_ci	elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data,
30768c2ecf20Sopenharmony_ci				  ies->len);
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	if (!elem || elem->datalen < 10 ||
30798c2ecf20Sopenharmony_ci	    !(elem->data[10] &
30808c2ecf20Sopenharmony_ci	      WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) {
30818c2ecf20Sopenharmony_ci		data->tolerated = false;
30828c2ecf20Sopenharmony_ci	}
30838c2ecf20Sopenharmony_ci	rcu_read_unlock();
30848c2ecf20Sopenharmony_ci}
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_cistatic void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
30878c2ecf20Sopenharmony_ci					       struct ieee80211_vif *vif)
30888c2ecf20Sopenharmony_ci{
30898c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
30908c2ecf20Sopenharmony_ci	struct iwl_mvm_he_obss_narrow_bw_ru_data iter_data = {
30918c2ecf20Sopenharmony_ci		.tolerated = true,
30928c2ecf20Sopenharmony_ci	};
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR)) {
30958c2ecf20Sopenharmony_ci		mvmvif->he_ru_2mhz_block = false;
30968c2ecf20Sopenharmony_ci		return;
30978c2ecf20Sopenharmony_ci	}
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_ci	cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef,
31008c2ecf20Sopenharmony_ci			  iwl_mvm_check_he_obss_narrow_bw_ru_iter,
31018c2ecf20Sopenharmony_ci			  &iter_data);
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	/*
31048c2ecf20Sopenharmony_ci	 * If there is at least one AP on radar channel that cannot
31058c2ecf20Sopenharmony_ci	 * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU.
31068c2ecf20Sopenharmony_ci	 */
31078c2ecf20Sopenharmony_ci	mvmvif->he_ru_2mhz_block = !iter_data.tolerated;
31088c2ecf20Sopenharmony_ci}
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
31118c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
31128c2ecf20Sopenharmony_ci				 struct ieee80211_sta *sta,
31138c2ecf20Sopenharmony_ci				 enum ieee80211_sta_state old_state,
31148c2ecf20Sopenharmony_ci				 enum ieee80211_sta_state new_state)
31158c2ecf20Sopenharmony_ci{
31168c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
31178c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
31188c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
31198c2ecf20Sopenharmony_ci	int ret;
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
31228c2ecf20Sopenharmony_ci			   sta->addr, old_state, new_state);
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci	/* this would be a mac80211 bug ... but don't crash */
31258c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
31268c2ecf20Sopenharmony_ci		return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) ? 0 : -EINVAL;
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci	/*
31298c2ecf20Sopenharmony_ci	 * If we are in a STA removal flow and in DQA mode:
31308c2ecf20Sopenharmony_ci	 *
31318c2ecf20Sopenharmony_ci	 * This is after the sync_rcu part, so the queues have already been
31328c2ecf20Sopenharmony_ci	 * flushed. No more TXs on their way in mac80211's path, and no more in
31338c2ecf20Sopenharmony_ci	 * the queues.
31348c2ecf20Sopenharmony_ci	 * Also, we won't be getting any new TX frames for this station.
31358c2ecf20Sopenharmony_ci	 * What we might have are deferred TX frames that need to be taken care
31368c2ecf20Sopenharmony_ci	 * of.
31378c2ecf20Sopenharmony_ci	 *
31388c2ecf20Sopenharmony_ci	 * Drop any still-queued deferred-frame before removing the STA, and
31398c2ecf20Sopenharmony_ci	 * make sure the worker is no longer handling frames for this STA.
31408c2ecf20Sopenharmony_ci	 */
31418c2ecf20Sopenharmony_ci	if (old_state == IEEE80211_STA_NONE &&
31428c2ecf20Sopenharmony_ci	    new_state == IEEE80211_STA_NOTEXIST) {
31438c2ecf20Sopenharmony_ci		flush_work(&mvm->add_stream_wk);
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci		/*
31468c2ecf20Sopenharmony_ci		 * No need to make sure deferred TX indication is off since the
31478c2ecf20Sopenharmony_ci		 * worker will already remove it if it was on
31488c2ecf20Sopenharmony_ci		 */
31498c2ecf20Sopenharmony_ci	}
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
31528c2ecf20Sopenharmony_ci	/* track whether or not the station is associated */
31538c2ecf20Sopenharmony_ci	mvm_sta->sta_state = new_state;
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_ci	if (old_state == IEEE80211_STA_NOTEXIST &&
31568c2ecf20Sopenharmony_ci	    new_state == IEEE80211_STA_NONE) {
31578c2ecf20Sopenharmony_ci		/*
31588c2ecf20Sopenharmony_ci		 * Firmware bug - it'll crash if the beacon interval is less
31598c2ecf20Sopenharmony_ci		 * than 16. We can't avoid connecting at all, so refuse the
31608c2ecf20Sopenharmony_ci		 * station state change, this will cause mac80211 to abandon
31618c2ecf20Sopenharmony_ci		 * attempts to connect to this AP, and eventually wpa_s will
31628c2ecf20Sopenharmony_ci		 * blocklist the AP...
31638c2ecf20Sopenharmony_ci		 */
31648c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_STATION &&
31658c2ecf20Sopenharmony_ci		    vif->bss_conf.beacon_int < 16) {
31668c2ecf20Sopenharmony_ci			IWL_ERR(mvm,
31678c2ecf20Sopenharmony_ci				"AP %pM beacon interval is %d, refusing due to firmware bug!\n",
31688c2ecf20Sopenharmony_ci				sta->addr, vif->bss_conf.beacon_int);
31698c2ecf20Sopenharmony_ci			ret = -EINVAL;
31708c2ecf20Sopenharmony_ci			goto out_unlock;
31718c2ecf20Sopenharmony_ci		}
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_STATION)
31748c2ecf20Sopenharmony_ci			vif->bss_conf.he_support = sta->he_cap.has_he;
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci		if (sta->tdls &&
31778c2ecf20Sopenharmony_ci		    (vif->p2p ||
31788c2ecf20Sopenharmony_ci		     iwl_mvm_tdls_sta_count(mvm, NULL) ==
31798c2ecf20Sopenharmony_ci						IWL_MVM_TDLS_STA_COUNT ||
31808c2ecf20Sopenharmony_ci		     iwl_mvm_phy_ctx_count(mvm) > 1)) {
31818c2ecf20Sopenharmony_ci			IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
31828c2ecf20Sopenharmony_ci			ret = -EBUSY;
31838c2ecf20Sopenharmony_ci			goto out_unlock;
31848c2ecf20Sopenharmony_ci		}
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_sta(mvm, vif, sta);
31878c2ecf20Sopenharmony_ci		if (sta->tdls && ret == 0) {
31888c2ecf20Sopenharmony_ci			iwl_mvm_recalc_tdls_state(mvm, vif, true);
31898c2ecf20Sopenharmony_ci			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
31908c2ecf20Sopenharmony_ci						   NL80211_TDLS_SETUP);
31918c2ecf20Sopenharmony_ci		}
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_ci		sta->max_rc_amsdu_len = 1;
31948c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_NONE &&
31958c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_AUTH) {
31968c2ecf20Sopenharmony_ci		/*
31978c2ecf20Sopenharmony_ci		 * EBS may be disabled due to previous failures reported by FW.
31988c2ecf20Sopenharmony_ci		 * Reset EBS status here assuming environment has been changed.
31998c2ecf20Sopenharmony_ci		 */
32008c2ecf20Sopenharmony_ci		mvm->last_ebs_successful = true;
32018c2ecf20Sopenharmony_ci		iwl_mvm_check_uapsd(mvm, vif, sta->addr);
32028c2ecf20Sopenharmony_ci		ret = 0;
32038c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_AUTH &&
32048c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_ASSOC) {
32058c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_AP) {
32068c2ecf20Sopenharmony_ci			vif->bss_conf.he_support = sta->he_cap.has_he;
32078c2ecf20Sopenharmony_ci			mvmvif->ap_assoc_sta_count++;
32088c2ecf20Sopenharmony_ci			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
32098c2ecf20Sopenharmony_ci			if (vif->bss_conf.he_support &&
32108c2ecf20Sopenharmony_ci			    !iwlwifi_mod_params.disable_11ax)
32118c2ecf20Sopenharmony_ci				iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
32128c2ecf20Sopenharmony_ci		} else if (vif->type == NL80211_IFTYPE_STATION) {
32138c2ecf20Sopenharmony_ci			vif->bss_conf.he_support = sta->he_cap.has_he;
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci			mvmvif->he_ru_2mhz_block = false;
32168c2ecf20Sopenharmony_ci			if (sta->he_cap.has_he)
32178c2ecf20Sopenharmony_ci				iwl_mvm_check_he_obss_narrow_bw_ru(hw, vif);
32188c2ecf20Sopenharmony_ci
32198c2ecf20Sopenharmony_ci			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
32208c2ecf20Sopenharmony_ci		}
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
32238c2ecf20Sopenharmony_ci				     false);
32248c2ecf20Sopenharmony_ci		ret = iwl_mvm_update_sta(mvm, vif, sta);
32258c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_ASSOC &&
32268c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_AUTHORIZED) {
32278c2ecf20Sopenharmony_ci		ret = 0;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci		/* we don't support TDLS during DCM */
32308c2ecf20Sopenharmony_ci		if (iwl_mvm_phy_ctx_count(mvm) > 1)
32318c2ecf20Sopenharmony_ci			iwl_mvm_teardown_tdls_peers(mvm);
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci		if (sta->tdls)
32348c2ecf20Sopenharmony_ci			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
32358c2ecf20Sopenharmony_ci						   NL80211_TDLS_ENABLE_LINK);
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_ci		/* enable beacon filtering */
32388c2ecf20Sopenharmony_ci		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci		/*
32418c2ecf20Sopenharmony_ci		 * Now that the station is authorized, i.e., keys were already
32428c2ecf20Sopenharmony_ci		 * installed, need to indicate to the FW that
32438c2ecf20Sopenharmony_ci		 * multicast data frames can be forwarded to the driver
32448c2ecf20Sopenharmony_ci		 */
32458c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
32488c2ecf20Sopenharmony_ci				     true);
32498c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_AUTHORIZED &&
32508c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_ASSOC) {
32518c2ecf20Sopenharmony_ci		/* Multicast data frames are no longer allowed */
32528c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci		/* disable beacon filtering */
32558c2ecf20Sopenharmony_ci		ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
32568c2ecf20Sopenharmony_ci		WARN_ON(ret &&
32578c2ecf20Sopenharmony_ci			!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
32588c2ecf20Sopenharmony_ci				  &mvm->status));
32598c2ecf20Sopenharmony_ci		ret = 0;
32608c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_ASSOC &&
32618c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_AUTH) {
32628c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_AP) {
32638c2ecf20Sopenharmony_ci			mvmvif->ap_assoc_sta_count--;
32648c2ecf20Sopenharmony_ci			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
32658c2ecf20Sopenharmony_ci		}
32668c2ecf20Sopenharmony_ci		ret = 0;
32678c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_AUTH &&
32688c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_NONE) {
32698c2ecf20Sopenharmony_ci		ret = 0;
32708c2ecf20Sopenharmony_ci	} else if (old_state == IEEE80211_STA_NONE &&
32718c2ecf20Sopenharmony_ci		   new_state == IEEE80211_STA_NOTEXIST) {
32728c2ecf20Sopenharmony_ci		ret = iwl_mvm_rm_sta(mvm, vif, sta);
32738c2ecf20Sopenharmony_ci		if (sta->tdls) {
32748c2ecf20Sopenharmony_ci			iwl_mvm_recalc_tdls_state(mvm, vif, false);
32758c2ecf20Sopenharmony_ci			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
32768c2ecf20Sopenharmony_ci						   NL80211_TDLS_DISABLE_LINK);
32778c2ecf20Sopenharmony_ci		}
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ci		if (unlikely(ret &&
32808c2ecf20Sopenharmony_ci			     test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
32818c2ecf20Sopenharmony_ci				      &mvm->status)))
32828c2ecf20Sopenharmony_ci			ret = 0;
32838c2ecf20Sopenharmony_ci	} else {
32848c2ecf20Sopenharmony_ci		ret = -EIO;
32858c2ecf20Sopenharmony_ci	}
32868c2ecf20Sopenharmony_ci out_unlock:
32878c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	if (sta->tdls && ret == 0) {
32908c2ecf20Sopenharmony_ci		if (old_state == IEEE80211_STA_NOTEXIST &&
32918c2ecf20Sopenharmony_ci		    new_state == IEEE80211_STA_NONE)
32928c2ecf20Sopenharmony_ci			ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
32938c2ecf20Sopenharmony_ci		else if (old_state == IEEE80211_STA_NONE &&
32948c2ecf20Sopenharmony_ci			 new_state == IEEE80211_STA_NOTEXIST)
32958c2ecf20Sopenharmony_ci			ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
32968c2ecf20Sopenharmony_ci	}
32978c2ecf20Sopenharmony_ci
32988c2ecf20Sopenharmony_ci	return ret;
32998c2ecf20Sopenharmony_ci}
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
33028c2ecf20Sopenharmony_ci{
33038c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	mvm->rts_threshold = value;
33068c2ecf20Sopenharmony_ci
33078c2ecf20Sopenharmony_ci	return 0;
33088c2ecf20Sopenharmony_ci}
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_cistatic void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
33118c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif,
33128c2ecf20Sopenharmony_ci				  struct ieee80211_sta *sta, u32 changed)
33138c2ecf20Sopenharmony_ci{
33148c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
33158c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	if (changed & (IEEE80211_RC_BW_CHANGED |
33188c2ecf20Sopenharmony_ci		       IEEE80211_RC_SUPP_RATES_CHANGED |
33198c2ecf20Sopenharmony_ci		       IEEE80211_RC_NSS_CHANGED))
33208c2ecf20Sopenharmony_ci		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
33218c2ecf20Sopenharmony_ci				     true);
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION &&
33248c2ecf20Sopenharmony_ci	    changed & IEEE80211_RC_NSS_CHANGED)
33258c2ecf20Sopenharmony_ci		iwl_mvm_sf_update(mvm, vif, false);
33268c2ecf20Sopenharmony_ci}
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
33298c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif, u16 ac,
33308c2ecf20Sopenharmony_ci			       const struct ieee80211_tx_queue_params *params)
33318c2ecf20Sopenharmony_ci{
33328c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
33338c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_ci	mvmvif->queue_params[ac] = *params;
33368c2ecf20Sopenharmony_ci
33378c2ecf20Sopenharmony_ci	/*
33388c2ecf20Sopenharmony_ci	 * No need to update right away, we'll get BSS_CHANGED_QOS
33398c2ecf20Sopenharmony_ci	 * The exception is P2P_DEVICE interface which needs immediate update.
33408c2ecf20Sopenharmony_ci	 */
33418c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
33428c2ecf20Sopenharmony_ci		int ret;
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_ci		mutex_lock(&mvm->mutex);
33458c2ecf20Sopenharmony_ci		ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
33468c2ecf20Sopenharmony_ci		mutex_unlock(&mvm->mutex);
33478c2ecf20Sopenharmony_ci		return ret;
33488c2ecf20Sopenharmony_ci	}
33498c2ecf20Sopenharmony_ci	return 0;
33508c2ecf20Sopenharmony_ci}
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
33538c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
33548c2ecf20Sopenharmony_ci				       u16 req_duration)
33558c2ecf20Sopenharmony_ci{
33568c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
33578c2ecf20Sopenharmony_ci	u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
33588c2ecf20Sopenharmony_ci	u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS;
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	if (req_duration > duration)
33618c2ecf20Sopenharmony_ci		duration = req_duration;
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
33648c2ecf20Sopenharmony_ci	/* Try really hard to protect the session and hear a beacon
33658c2ecf20Sopenharmony_ci	 * The new session protection command allows us to protect the
33668c2ecf20Sopenharmony_ci	 * session for a much longer time since the firmware will internally
33678c2ecf20Sopenharmony_ci	 * create two events: a 300TU one with a very high priority that
33688c2ecf20Sopenharmony_ci	 * won't be fragmented which should be enough for 99% of the cases,
33698c2ecf20Sopenharmony_ci	 * and another one (which we configure here to be 900TU long) which
33708c2ecf20Sopenharmony_ci	 * will have a slightly lower priority, but more importantly, can be
33718c2ecf20Sopenharmony_ci	 * fragmented so that it'll allow other activities to run.
33728c2ecf20Sopenharmony_ci	 */
33738c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
33748c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
33758c2ecf20Sopenharmony_ci		iwl_mvm_schedule_session_protection(mvm, vif, 900,
33768c2ecf20Sopenharmony_ci						    min_duration, false);
33778c2ecf20Sopenharmony_ci	else
33788c2ecf20Sopenharmony_ci		iwl_mvm_protect_session(mvm, vif, duration,
33798c2ecf20Sopenharmony_ci					min_duration, 500, false);
33808c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
33818c2ecf20Sopenharmony_ci}
33828c2ecf20Sopenharmony_ci
33838c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
33848c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif,
33858c2ecf20Sopenharmony_ci					struct cfg80211_sched_scan_request *req,
33868c2ecf20Sopenharmony_ci					struct ieee80211_scan_ies *ies)
33878c2ecf20Sopenharmony_ci{
33888c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_ci	int ret;
33918c2ecf20Sopenharmony_ci
33928c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
33938c2ecf20Sopenharmony_ci
33948c2ecf20Sopenharmony_ci	if (!vif->bss_conf.idle) {
33958c2ecf20Sopenharmony_ci		ret = -EBUSY;
33968c2ecf20Sopenharmony_ci		goto out;
33978c2ecf20Sopenharmony_ci	}
33988c2ecf20Sopenharmony_ci
33998c2ecf20Sopenharmony_ci	ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ciout:
34028c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
34038c2ecf20Sopenharmony_ci	return ret;
34048c2ecf20Sopenharmony_ci}
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
34078c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif)
34088c2ecf20Sopenharmony_ci{
34098c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
34108c2ecf20Sopenharmony_ci	int ret;
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	/* Due to a race condition, it's possible that mac80211 asks
34158c2ecf20Sopenharmony_ci	 * us to stop a sched_scan when it's already stopped.  This
34168c2ecf20Sopenharmony_ci	 * can happen, for instance, if we stopped the scan ourselves,
34178c2ecf20Sopenharmony_ci	 * called ieee80211_sched_scan_stopped() and the userspace called
34188c2ecf20Sopenharmony_ci	 * stop sched scan scan before ieee80211_sched_scan_stopped_work()
34198c2ecf20Sopenharmony_ci	 * could run.  To handle this, simply return if the scan is
34208c2ecf20Sopenharmony_ci	 * not running.
34218c2ecf20Sopenharmony_ci	*/
34228c2ecf20Sopenharmony_ci	if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) {
34238c2ecf20Sopenharmony_ci		mutex_unlock(&mvm->mutex);
34248c2ecf20Sopenharmony_ci		return 0;
34258c2ecf20Sopenharmony_ci	}
34268c2ecf20Sopenharmony_ci
34278c2ecf20Sopenharmony_ci	ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false);
34288c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
34298c2ecf20Sopenharmony_ci	iwl_mvm_wait_for_async_handlers(mvm);
34308c2ecf20Sopenharmony_ci
34318c2ecf20Sopenharmony_ci	return ret;
34328c2ecf20Sopenharmony_ci}
34338c2ecf20Sopenharmony_ci
34348c2ecf20Sopenharmony_cistatic int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
34358c2ecf20Sopenharmony_ci				 enum set_key_cmd cmd,
34368c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
34378c2ecf20Sopenharmony_ci				 struct ieee80211_sta *sta,
34388c2ecf20Sopenharmony_ci				 struct ieee80211_key_conf *key)
34398c2ecf20Sopenharmony_ci{
34408c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
34418c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
34428c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta;
34438c2ecf20Sopenharmony_ci	struct iwl_mvm_key_pn *ptk_pn;
34448c2ecf20Sopenharmony_ci	int keyidx = key->keyidx;
34458c2ecf20Sopenharmony_ci	int ret, i;
34468c2ecf20Sopenharmony_ci	u8 key_offset;
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci	switch (key->cipher) {
34498c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
34508c2ecf20Sopenharmony_ci		if (!mvm->trans->trans_cfg->gen2) {
34518c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
34528c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
34538c2ecf20Sopenharmony_ci		} else if (vif->type == NL80211_IFTYPE_STATION) {
34548c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_PUT_MIC_SPACE;
34558c2ecf20Sopenharmony_ci		} else {
34568c2ecf20Sopenharmony_ci			IWL_DEBUG_MAC80211(mvm, "Use SW encryption for TKIP\n");
34578c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
34588c2ecf20Sopenharmony_ci		}
34598c2ecf20Sopenharmony_ci		break;
34608c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
34618c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP:
34628c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP_256:
34638c2ecf20Sopenharmony_ci		if (!iwl_mvm_has_new_tx_api(mvm))
34648c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
34658c2ecf20Sopenharmony_ci		break;
34668c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_AES_CMAC:
34678c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
34688c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
34698c2ecf20Sopenharmony_ci		WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE));
34708c2ecf20Sopenharmony_ci		break;
34718c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
34728c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
34738c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_STATION)
34748c2ecf20Sopenharmony_ci			break;
34758c2ecf20Sopenharmony_ci		if (iwl_mvm_has_new_tx_api(mvm))
34768c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
34778c2ecf20Sopenharmony_ci		/* support HW crypto on TX */
34788c2ecf20Sopenharmony_ci		return 0;
34798c2ecf20Sopenharmony_ci	default:
34808c2ecf20Sopenharmony_ci		/* currently FW supports only one optional cipher scheme */
34818c2ecf20Sopenharmony_ci		if (hw->n_cipher_schemes &&
34828c2ecf20Sopenharmony_ci		    hw->cipher_schemes->cipher == key->cipher)
34838c2ecf20Sopenharmony_ci			key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
34848c2ecf20Sopenharmony_ci		else
34858c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
34868c2ecf20Sopenharmony_ci	}
34878c2ecf20Sopenharmony_ci
34888c2ecf20Sopenharmony_ci	switch (cmd) {
34898c2ecf20Sopenharmony_ci	case SET_KEY:
34908c2ecf20Sopenharmony_ci		if ((vif->type == NL80211_IFTYPE_ADHOC ||
34918c2ecf20Sopenharmony_ci		     vif->type == NL80211_IFTYPE_AP) && !sta) {
34928c2ecf20Sopenharmony_ci			/*
34938c2ecf20Sopenharmony_ci			 * GTK on AP interface is a TX-only key, return 0;
34948c2ecf20Sopenharmony_ci			 * on IBSS they're per-station and because we're lazy
34958c2ecf20Sopenharmony_ci			 * we don't support them for RX, so do the same.
34968c2ecf20Sopenharmony_ci			 * CMAC/GMAC in AP/IBSS modes must be done in software.
34978c2ecf20Sopenharmony_ci			 */
34988c2ecf20Sopenharmony_ci			if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
34998c2ecf20Sopenharmony_ci			    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
35008c2ecf20Sopenharmony_ci			    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
35018c2ecf20Sopenharmony_ci				ret = -EOPNOTSUPP;
35028c2ecf20Sopenharmony_ci				break;
35038c2ecf20Sopenharmony_ci			}
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_ci			if (key->cipher != WLAN_CIPHER_SUITE_GCMP &&
35068c2ecf20Sopenharmony_ci			    key->cipher != WLAN_CIPHER_SUITE_GCMP_256 &&
35078c2ecf20Sopenharmony_ci			    !iwl_mvm_has_new_tx_api(mvm)) {
35088c2ecf20Sopenharmony_ci				key->hw_key_idx = STA_KEY_IDX_INVALID;
35098c2ecf20Sopenharmony_ci				ret = 0;
35108c2ecf20Sopenharmony_ci				break;
35118c2ecf20Sopenharmony_ci			}
35128c2ecf20Sopenharmony_ci
35138c2ecf20Sopenharmony_ci			if (!mvmvif->ap_ibss_active) {
35148c2ecf20Sopenharmony_ci				for (i = 0;
35158c2ecf20Sopenharmony_ci				     i < ARRAY_SIZE(mvmvif->ap_early_keys);
35168c2ecf20Sopenharmony_ci				     i++) {
35178c2ecf20Sopenharmony_ci					if (!mvmvif->ap_early_keys[i]) {
35188c2ecf20Sopenharmony_ci						mvmvif->ap_early_keys[i] = key;
35198c2ecf20Sopenharmony_ci						break;
35208c2ecf20Sopenharmony_ci					}
35218c2ecf20Sopenharmony_ci				}
35228c2ecf20Sopenharmony_ci
35238c2ecf20Sopenharmony_ci				if (i >= ARRAY_SIZE(mvmvif->ap_early_keys))
35248c2ecf20Sopenharmony_ci					ret = -ENOSPC;
35258c2ecf20Sopenharmony_ci				else
35268c2ecf20Sopenharmony_ci					ret = 0;
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_ci				break;
35298c2ecf20Sopenharmony_ci			}
35308c2ecf20Sopenharmony_ci		}
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci		/* During FW restart, in order to restore the state as it was,
35338c2ecf20Sopenharmony_ci		 * don't try to reprogram keys we previously failed for.
35348c2ecf20Sopenharmony_ci		 */
35358c2ecf20Sopenharmony_ci		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
35368c2ecf20Sopenharmony_ci		    key->hw_key_idx == STA_KEY_IDX_INVALID) {
35378c2ecf20Sopenharmony_ci			IWL_DEBUG_MAC80211(mvm,
35388c2ecf20Sopenharmony_ci					   "skip invalid idx key programming during restart\n");
35398c2ecf20Sopenharmony_ci			ret = 0;
35408c2ecf20Sopenharmony_ci			break;
35418c2ecf20Sopenharmony_ci		}
35428c2ecf20Sopenharmony_ci
35438c2ecf20Sopenharmony_ci		if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
35448c2ecf20Sopenharmony_ci		    sta && iwl_mvm_has_new_rx_api(mvm) &&
35458c2ecf20Sopenharmony_ci		    key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
35468c2ecf20Sopenharmony_ci		    (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
35478c2ecf20Sopenharmony_ci		     key->cipher == WLAN_CIPHER_SUITE_GCMP ||
35488c2ecf20Sopenharmony_ci		     key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) {
35498c2ecf20Sopenharmony_ci			struct ieee80211_key_seq seq;
35508c2ecf20Sopenharmony_ci			int tid, q;
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci			mvmsta = iwl_mvm_sta_from_mac80211(sta);
35538c2ecf20Sopenharmony_ci			WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx]));
35548c2ecf20Sopenharmony_ci			ptk_pn = kzalloc(struct_size(ptk_pn, q,
35558c2ecf20Sopenharmony_ci						     mvm->trans->num_rx_queues),
35568c2ecf20Sopenharmony_ci					 GFP_KERNEL);
35578c2ecf20Sopenharmony_ci			if (!ptk_pn) {
35588c2ecf20Sopenharmony_ci				ret = -ENOMEM;
35598c2ecf20Sopenharmony_ci				break;
35608c2ecf20Sopenharmony_ci			}
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_ci			for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
35638c2ecf20Sopenharmony_ci				ieee80211_get_key_rx_seq(key, tid, &seq);
35648c2ecf20Sopenharmony_ci				for (q = 0; q < mvm->trans->num_rx_queues; q++)
35658c2ecf20Sopenharmony_ci					memcpy(ptk_pn->q[q].pn[tid],
35668c2ecf20Sopenharmony_ci					       seq.ccmp.pn,
35678c2ecf20Sopenharmony_ci					       IEEE80211_CCMP_PN_LEN);
35688c2ecf20Sopenharmony_ci			}
35698c2ecf20Sopenharmony_ci
35708c2ecf20Sopenharmony_ci			rcu_assign_pointer(mvmsta->ptk_pn[keyidx], ptk_pn);
35718c2ecf20Sopenharmony_ci		}
35728c2ecf20Sopenharmony_ci
35738c2ecf20Sopenharmony_ci		/* in HW restart reuse the index, otherwise request a new one */
35748c2ecf20Sopenharmony_ci		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
35758c2ecf20Sopenharmony_ci			key_offset = key->hw_key_idx;
35768c2ecf20Sopenharmony_ci		else
35778c2ecf20Sopenharmony_ci			key_offset = STA_KEY_IDX_INVALID;
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci		IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
35808c2ecf20Sopenharmony_ci		ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset);
35818c2ecf20Sopenharmony_ci		if (ret) {
35828c2ecf20Sopenharmony_ci			IWL_WARN(mvm, "set key failed\n");
35838c2ecf20Sopenharmony_ci			key->hw_key_idx = STA_KEY_IDX_INVALID;
35848c2ecf20Sopenharmony_ci			/*
35858c2ecf20Sopenharmony_ci			 * can't add key for RX, but we don't need it
35868c2ecf20Sopenharmony_ci			 * in the device for TX so still return 0,
35878c2ecf20Sopenharmony_ci			 * unless we have new TX API where we cannot
35888c2ecf20Sopenharmony_ci			 * put key material into the TX_CMD
35898c2ecf20Sopenharmony_ci			 */
35908c2ecf20Sopenharmony_ci			if (iwl_mvm_has_new_tx_api(mvm))
35918c2ecf20Sopenharmony_ci				ret = -EOPNOTSUPP;
35928c2ecf20Sopenharmony_ci			else
35938c2ecf20Sopenharmony_ci				ret = 0;
35948c2ecf20Sopenharmony_ci		}
35958c2ecf20Sopenharmony_ci
35968c2ecf20Sopenharmony_ci		break;
35978c2ecf20Sopenharmony_ci	case DISABLE_KEY:
35988c2ecf20Sopenharmony_ci		ret = -ENOENT;
35998c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) {
36008c2ecf20Sopenharmony_ci			if (mvmvif->ap_early_keys[i] == key) {
36018c2ecf20Sopenharmony_ci				mvmvif->ap_early_keys[i] = NULL;
36028c2ecf20Sopenharmony_ci				ret = 0;
36038c2ecf20Sopenharmony_ci			}
36048c2ecf20Sopenharmony_ci		}
36058c2ecf20Sopenharmony_ci
36068c2ecf20Sopenharmony_ci		/* found in pending list - don't do anything else */
36078c2ecf20Sopenharmony_ci		if (ret == 0)
36088c2ecf20Sopenharmony_ci			break;
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci		if (key->hw_key_idx == STA_KEY_IDX_INVALID) {
36118c2ecf20Sopenharmony_ci			ret = 0;
36128c2ecf20Sopenharmony_ci			break;
36138c2ecf20Sopenharmony_ci		}
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_ci		if (sta && iwl_mvm_has_new_rx_api(mvm) &&
36168c2ecf20Sopenharmony_ci		    key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
36178c2ecf20Sopenharmony_ci		    (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
36188c2ecf20Sopenharmony_ci		     key->cipher == WLAN_CIPHER_SUITE_GCMP ||
36198c2ecf20Sopenharmony_ci		     key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) {
36208c2ecf20Sopenharmony_ci			mvmsta = iwl_mvm_sta_from_mac80211(sta);
36218c2ecf20Sopenharmony_ci			ptk_pn = rcu_dereference_protected(
36228c2ecf20Sopenharmony_ci						mvmsta->ptk_pn[keyidx],
36238c2ecf20Sopenharmony_ci						lockdep_is_held(&mvm->mutex));
36248c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL);
36258c2ecf20Sopenharmony_ci			if (ptk_pn)
36268c2ecf20Sopenharmony_ci				kfree_rcu(ptk_pn, rcu_head);
36278c2ecf20Sopenharmony_ci		}
36288c2ecf20Sopenharmony_ci
36298c2ecf20Sopenharmony_ci		IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
36308c2ecf20Sopenharmony_ci		ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
36318c2ecf20Sopenharmony_ci		break;
36328c2ecf20Sopenharmony_ci	default:
36338c2ecf20Sopenharmony_ci		ret = -EINVAL;
36348c2ecf20Sopenharmony_ci	}
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci	return ret;
36378c2ecf20Sopenharmony_ci}
36388c2ecf20Sopenharmony_ci
36398c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
36408c2ecf20Sopenharmony_ci			       enum set_key_cmd cmd,
36418c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif,
36428c2ecf20Sopenharmony_ci			       struct ieee80211_sta *sta,
36438c2ecf20Sopenharmony_ci			       struct ieee80211_key_conf *key)
36448c2ecf20Sopenharmony_ci{
36458c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
36468c2ecf20Sopenharmony_ci	int ret;
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
36498c2ecf20Sopenharmony_ci	ret = __iwl_mvm_mac_set_key(hw, cmd, vif, sta, key);
36508c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
36518c2ecf20Sopenharmony_ci
36528c2ecf20Sopenharmony_ci	return ret;
36538c2ecf20Sopenharmony_ci}
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
36568c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif,
36578c2ecf20Sopenharmony_ci					struct ieee80211_key_conf *keyconf,
36588c2ecf20Sopenharmony_ci					struct ieee80211_sta *sta,
36598c2ecf20Sopenharmony_ci					u32 iv32, u16 *phase1key)
36608c2ecf20Sopenharmony_ci{
36618c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci	if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
36648c2ecf20Sopenharmony_ci		return;
36658c2ecf20Sopenharmony_ci
36668c2ecf20Sopenharmony_ci	iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
36678c2ecf20Sopenharmony_ci}
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci
36708c2ecf20Sopenharmony_cistatic bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
36718c2ecf20Sopenharmony_ci			       struct iwl_rx_packet *pkt, void *data)
36728c2ecf20Sopenharmony_ci{
36738c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm =
36748c2ecf20Sopenharmony_ci		container_of(notif_wait, struct iwl_mvm, notif_wait);
36758c2ecf20Sopenharmony_ci	struct iwl_hs20_roc_res *resp;
36768c2ecf20Sopenharmony_ci	int resp_len = iwl_rx_packet_payload_len(pkt);
36778c2ecf20Sopenharmony_ci	struct iwl_mvm_time_event_data *te_data = data;
36788c2ecf20Sopenharmony_ci
36798c2ecf20Sopenharmony_ci	if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD))
36808c2ecf20Sopenharmony_ci		return true;
36818c2ecf20Sopenharmony_ci
36828c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
36838c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n");
36848c2ecf20Sopenharmony_ci		return true;
36858c2ecf20Sopenharmony_ci	}
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	resp = (void *)pkt->data;
36888c2ecf20Sopenharmony_ci
36898c2ecf20Sopenharmony_ci	IWL_DEBUG_TE(mvm,
36908c2ecf20Sopenharmony_ci		     "Aux ROC: Received response from ucode: status=%d uid=%d\n",
36918c2ecf20Sopenharmony_ci		     resp->status, resp->event_unique_id);
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	te_data->uid = le32_to_cpu(resp->event_unique_id);
36948c2ecf20Sopenharmony_ci	IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
36958c2ecf20Sopenharmony_ci		     te_data->uid);
36968c2ecf20Sopenharmony_ci
36978c2ecf20Sopenharmony_ci	spin_lock_bh(&mvm->time_event_lock);
36988c2ecf20Sopenharmony_ci	list_add_tail(&te_data->list, &mvm->aux_roc_te_list);
36998c2ecf20Sopenharmony_ci	spin_unlock_bh(&mvm->time_event_lock);
37008c2ecf20Sopenharmony_ci
37018c2ecf20Sopenharmony_ci	return true;
37028c2ecf20Sopenharmony_ci}
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
37058c2ecf20Sopenharmony_ci#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
37068c2ecf20Sopenharmony_ci#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
37078c2ecf20Sopenharmony_ci#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
37088c2ecf20Sopenharmony_ci#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
37098c2ecf20Sopenharmony_cistatic int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
37108c2ecf20Sopenharmony_ci				    struct ieee80211_channel *channel,
37118c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
37128c2ecf20Sopenharmony_ci				    int duration)
37138c2ecf20Sopenharmony_ci{
37148c2ecf20Sopenharmony_ci	int res;
37158c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
37168c2ecf20Sopenharmony_ci	struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
37178c2ecf20Sopenharmony_ci	static const u16 time_event_response[] = { HOT_SPOT_CMD };
37188c2ecf20Sopenharmony_ci	struct iwl_notification_wait wait_time_event;
37198c2ecf20Sopenharmony_ci	u32 dtim_interval = vif->bss_conf.dtim_period *
37208c2ecf20Sopenharmony_ci		vif->bss_conf.beacon_int;
37218c2ecf20Sopenharmony_ci	u32 req_dur, delay;
37228c2ecf20Sopenharmony_ci	struct iwl_hs20_roc_req aux_roc_req = {
37238c2ecf20Sopenharmony_ci		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
37248c2ecf20Sopenharmony_ci		.id_and_color =
37258c2ecf20Sopenharmony_ci			cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
37268c2ecf20Sopenharmony_ci		.sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
37278c2ecf20Sopenharmony_ci	};
37288c2ecf20Sopenharmony_ci	struct iwl_hs20_roc_req_tail *tail = iwl_mvm_chan_info_cmd_tail(mvm,
37298c2ecf20Sopenharmony_ci		&aux_roc_req.channel_info);
37308c2ecf20Sopenharmony_ci	u16 len = sizeof(aux_roc_req) - iwl_mvm_chan_info_padding(mvm);
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci	/* Set the channel info data */
37338c2ecf20Sopenharmony_ci	iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value,
37348c2ecf20Sopenharmony_ci			      iwl_mvm_phy_band_from_nl80211(channel->band),
37358c2ecf20Sopenharmony_ci			      PHY_VHT_CHANNEL_MODE20,
37368c2ecf20Sopenharmony_ci			      0);
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	/* Set the time and duration */
37398c2ecf20Sopenharmony_ci	tail->apply_time = cpu_to_le32(iwl_mvm_get_systime(mvm));
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_ci	delay = AUX_ROC_MIN_DELAY;
37428c2ecf20Sopenharmony_ci	req_dur = MSEC_TO_TU(duration);
37438c2ecf20Sopenharmony_ci
37448c2ecf20Sopenharmony_ci	/*
37458c2ecf20Sopenharmony_ci	 * If we are associated we want the delay time to be at least one
37468c2ecf20Sopenharmony_ci	 * dtim interval so that the FW can wait until after the DTIM and
37478c2ecf20Sopenharmony_ci	 * then start the time event, this will potentially allow us to
37488c2ecf20Sopenharmony_ci	 * remain off-channel for the max duration.
37498c2ecf20Sopenharmony_ci	 * Since we want to use almost a whole dtim interval we would also
37508c2ecf20Sopenharmony_ci	 * like the delay to be for 2-3 dtim intervals, in case there are
37518c2ecf20Sopenharmony_ci	 * other time events with higher priority.
37528c2ecf20Sopenharmony_ci	 */
37538c2ecf20Sopenharmony_ci	if (vif->bss_conf.assoc) {
37548c2ecf20Sopenharmony_ci		delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY);
37558c2ecf20Sopenharmony_ci		/* We cannot remain off-channel longer than the DTIM interval */
37568c2ecf20Sopenharmony_ci		if (dtim_interval <= req_dur) {
37578c2ecf20Sopenharmony_ci			req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER;
37588c2ecf20Sopenharmony_ci			if (req_dur <= AUX_ROC_MIN_DURATION)
37598c2ecf20Sopenharmony_ci				req_dur = dtim_interval -
37608c2ecf20Sopenharmony_ci					AUX_ROC_MIN_SAFETY_BUFFER;
37618c2ecf20Sopenharmony_ci		}
37628c2ecf20Sopenharmony_ci	}
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_ci	tail->duration = cpu_to_le32(req_dur);
37658c2ecf20Sopenharmony_ci	tail->apply_time_max_delay = cpu_to_le32(delay);
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci	IWL_DEBUG_TE(mvm,
37688c2ecf20Sopenharmony_ci		     "ROC: Requesting to remain on channel %u for %ums\n",
37698c2ecf20Sopenharmony_ci		     channel->hw_value, req_dur);
37708c2ecf20Sopenharmony_ci	IWL_DEBUG_TE(mvm,
37718c2ecf20Sopenharmony_ci		     "\t(requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
37728c2ecf20Sopenharmony_ci		     duration, delay, dtim_interval);
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci	/* Set the node address */
37758c2ecf20Sopenharmony_ci	memcpy(tail->node_addr, vif->addr, ETH_ALEN);
37768c2ecf20Sopenharmony_ci
37778c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_ci	spin_lock_bh(&mvm->time_event_lock);
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ci	if (WARN_ON(te_data->id == HOT_SPOT_CMD)) {
37828c2ecf20Sopenharmony_ci		spin_unlock_bh(&mvm->time_event_lock);
37838c2ecf20Sopenharmony_ci		return -EIO;
37848c2ecf20Sopenharmony_ci	}
37858c2ecf20Sopenharmony_ci
37868c2ecf20Sopenharmony_ci	te_data->vif = vif;
37878c2ecf20Sopenharmony_ci	te_data->duration = duration;
37888c2ecf20Sopenharmony_ci	te_data->id = HOT_SPOT_CMD;
37898c2ecf20Sopenharmony_ci
37908c2ecf20Sopenharmony_ci	spin_unlock_bh(&mvm->time_event_lock);
37918c2ecf20Sopenharmony_ci
37928c2ecf20Sopenharmony_ci	/*
37938c2ecf20Sopenharmony_ci	 * Use a notification wait, which really just processes the
37948c2ecf20Sopenharmony_ci	 * command response and doesn't wait for anything, in order
37958c2ecf20Sopenharmony_ci	 * to be able to process the response and get the UID inside
37968c2ecf20Sopenharmony_ci	 * the RX path. Using CMD_WANT_SKB doesn't work because it
37978c2ecf20Sopenharmony_ci	 * stores the buffer and then wakes up this thread, by which
37988c2ecf20Sopenharmony_ci	 * time another notification (that the time event started)
37998c2ecf20Sopenharmony_ci	 * might already be processed unsuccessfully.
38008c2ecf20Sopenharmony_ci	 */
38018c2ecf20Sopenharmony_ci	iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
38028c2ecf20Sopenharmony_ci				   time_event_response,
38038c2ecf20Sopenharmony_ci				   ARRAY_SIZE(time_event_response),
38048c2ecf20Sopenharmony_ci				   iwl_mvm_rx_aux_roc, te_data);
38058c2ecf20Sopenharmony_ci
38068c2ecf20Sopenharmony_ci	res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, len,
38078c2ecf20Sopenharmony_ci				   &aux_roc_req);
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_ci	if (res) {
38108c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res);
38118c2ecf20Sopenharmony_ci		iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
38128c2ecf20Sopenharmony_ci		goto out_clear_te;
38138c2ecf20Sopenharmony_ci	}
38148c2ecf20Sopenharmony_ci
38158c2ecf20Sopenharmony_ci	/* No need to wait for anything, so just pass 1 (0 isn't valid) */
38168c2ecf20Sopenharmony_ci	res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
38178c2ecf20Sopenharmony_ci	/* should never fail */
38188c2ecf20Sopenharmony_ci	WARN_ON_ONCE(res);
38198c2ecf20Sopenharmony_ci
38208c2ecf20Sopenharmony_ci	if (res) {
38218c2ecf20Sopenharmony_ci out_clear_te:
38228c2ecf20Sopenharmony_ci		spin_lock_bh(&mvm->time_event_lock);
38238c2ecf20Sopenharmony_ci		iwl_mvm_te_clear_data(mvm, te_data);
38248c2ecf20Sopenharmony_ci		spin_unlock_bh(&mvm->time_event_lock);
38258c2ecf20Sopenharmony_ci	}
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_ci	return res;
38288c2ecf20Sopenharmony_ci}
38298c2ecf20Sopenharmony_ci
38308c2ecf20Sopenharmony_cistatic int iwl_mvm_roc(struct ieee80211_hw *hw,
38318c2ecf20Sopenharmony_ci		       struct ieee80211_vif *vif,
38328c2ecf20Sopenharmony_ci		       struct ieee80211_channel *channel,
38338c2ecf20Sopenharmony_ci		       int duration,
38348c2ecf20Sopenharmony_ci		       enum ieee80211_roc_type type)
38358c2ecf20Sopenharmony_ci{
38368c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
38378c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
38388c2ecf20Sopenharmony_ci	struct cfg80211_chan_def chandef;
38398c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt;
38408c2ecf20Sopenharmony_ci	bool band_change_removal;
38418c2ecf20Sopenharmony_ci	int ret, i;
38428c2ecf20Sopenharmony_ci
38438c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
38448c2ecf20Sopenharmony_ci			   duration, type);
38458c2ecf20Sopenharmony_ci
38468c2ecf20Sopenharmony_ci	/*
38478c2ecf20Sopenharmony_ci	 * Flush the done work, just in case it's still pending, so that
38488c2ecf20Sopenharmony_ci	 * the work it does can complete and we can accept new frames.
38498c2ecf20Sopenharmony_ci	 */
38508c2ecf20Sopenharmony_ci	flush_work(&mvm->roc_done_wk);
38518c2ecf20Sopenharmony_ci
38528c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
38538c2ecf20Sopenharmony_ci
38548c2ecf20Sopenharmony_ci	switch (vif->type) {
38558c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
38568c2ecf20Sopenharmony_ci		if (fw_has_capa(&mvm->fw->ucode_capa,
38578c2ecf20Sopenharmony_ci				IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
38588c2ecf20Sopenharmony_ci			/* Use aux roc framework (HS20) */
38598c2ecf20Sopenharmony_ci			if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP,
38608c2ecf20Sopenharmony_ci						  ADD_STA, 0) >= 12) {
38618c2ecf20Sopenharmony_ci				u32 lmac_id;
38628c2ecf20Sopenharmony_ci
38638c2ecf20Sopenharmony_ci				lmac_id = iwl_mvm_get_lmac_id(mvm->fw,
38648c2ecf20Sopenharmony_ci							      channel->band);
38658c2ecf20Sopenharmony_ci				ret = iwl_mvm_add_aux_sta(mvm, lmac_id);
38668c2ecf20Sopenharmony_ci				if (WARN(ret,
38678c2ecf20Sopenharmony_ci					 "Failed to allocate aux station"))
38688c2ecf20Sopenharmony_ci					goto out_unlock;
38698c2ecf20Sopenharmony_ci			}
38708c2ecf20Sopenharmony_ci			ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
38718c2ecf20Sopenharmony_ci						       vif, duration);
38728c2ecf20Sopenharmony_ci			goto out_unlock;
38738c2ecf20Sopenharmony_ci		}
38748c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "hotspot not supported\n");
38758c2ecf20Sopenharmony_ci		ret = -EINVAL;
38768c2ecf20Sopenharmony_ci		goto out_unlock;
38778c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
38788c2ecf20Sopenharmony_ci		/* handle below */
38798c2ecf20Sopenharmony_ci		break;
38808c2ecf20Sopenharmony_ci	default:
38818c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
38828c2ecf20Sopenharmony_ci		ret = -EINVAL;
38838c2ecf20Sopenharmony_ci		goto out_unlock;
38848c2ecf20Sopenharmony_ci	}
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_PHY_CTX; i++) {
38878c2ecf20Sopenharmony_ci		phy_ctxt = &mvm->phy_ctxts[i];
38888c2ecf20Sopenharmony_ci		if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
38898c2ecf20Sopenharmony_ci			continue;
38908c2ecf20Sopenharmony_ci
38918c2ecf20Sopenharmony_ci		if (phy_ctxt->ref && channel == phy_ctxt->channel) {
38928c2ecf20Sopenharmony_ci			/*
38938c2ecf20Sopenharmony_ci			 * Unbind the P2P_DEVICE from the current PHY context,
38948c2ecf20Sopenharmony_ci			 * and if the PHY context is not used remove it.
38958c2ecf20Sopenharmony_ci			 */
38968c2ecf20Sopenharmony_ci			ret = iwl_mvm_binding_remove_vif(mvm, vif);
38978c2ecf20Sopenharmony_ci			if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
38988c2ecf20Sopenharmony_ci				goto out_unlock;
38998c2ecf20Sopenharmony_ci
39008c2ecf20Sopenharmony_ci			iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci			/* Bind the P2P_DEVICE to the current PHY Context */
39038c2ecf20Sopenharmony_ci			mvmvif->phy_ctxt = phy_ctxt;
39048c2ecf20Sopenharmony_ci
39058c2ecf20Sopenharmony_ci			ret = iwl_mvm_binding_add_vif(mvm, vif);
39068c2ecf20Sopenharmony_ci			if (WARN(ret, "Failed binding P2P_DEVICE\n"))
39078c2ecf20Sopenharmony_ci				goto out_unlock;
39088c2ecf20Sopenharmony_ci
39098c2ecf20Sopenharmony_ci			iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
39108c2ecf20Sopenharmony_ci			goto schedule_time_event;
39118c2ecf20Sopenharmony_ci		}
39128c2ecf20Sopenharmony_ci	}
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci	/* Need to update the PHY context only if the ROC channel changed */
39158c2ecf20Sopenharmony_ci	if (channel == mvmvif->phy_ctxt->channel)
39168c2ecf20Sopenharmony_ci		goto schedule_time_event;
39178c2ecf20Sopenharmony_ci
39188c2ecf20Sopenharmony_ci	cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
39198c2ecf20Sopenharmony_ci
39208c2ecf20Sopenharmony_ci	/*
39218c2ecf20Sopenharmony_ci	 * Check if the remain-on-channel is on a different band and that
39228c2ecf20Sopenharmony_ci	 * requires context removal, see iwl_mvm_phy_ctxt_changed(). If
39238c2ecf20Sopenharmony_ci	 * so, we'll need to release and then re-configure here, since we
39248c2ecf20Sopenharmony_ci	 * must not remove a PHY context that's part of a binding.
39258c2ecf20Sopenharmony_ci	 */
39268c2ecf20Sopenharmony_ci	band_change_removal =
39278c2ecf20Sopenharmony_ci		fw_has_capa(&mvm->fw->ucode_capa,
39288c2ecf20Sopenharmony_ci			    IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
39298c2ecf20Sopenharmony_ci		mvmvif->phy_ctxt->channel->band != chandef.chan->band;
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_ci	if (mvmvif->phy_ctxt->ref == 1 && !band_change_removal) {
39328c2ecf20Sopenharmony_ci		/*
39338c2ecf20Sopenharmony_ci		 * Change the PHY context configuration as it is currently
39348c2ecf20Sopenharmony_ci		 * referenced only by the P2P Device MAC (and we can modify it)
39358c2ecf20Sopenharmony_ci		 */
39368c2ecf20Sopenharmony_ci		ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
39378c2ecf20Sopenharmony_ci					       &chandef, 1, 1);
39388c2ecf20Sopenharmony_ci		if (ret)
39398c2ecf20Sopenharmony_ci			goto out_unlock;
39408c2ecf20Sopenharmony_ci	} else {
39418c2ecf20Sopenharmony_ci		/*
39428c2ecf20Sopenharmony_ci		 * The PHY context is shared with other MACs (or we're trying to
39438c2ecf20Sopenharmony_ci		 * switch bands), so remove the P2P Device from the binding,
39448c2ecf20Sopenharmony_ci		 * allocate an new PHY context and create a new binding.
39458c2ecf20Sopenharmony_ci		 */
39468c2ecf20Sopenharmony_ci		phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
39478c2ecf20Sopenharmony_ci		if (!phy_ctxt) {
39488c2ecf20Sopenharmony_ci			ret = -ENOSPC;
39498c2ecf20Sopenharmony_ci			goto out_unlock;
39508c2ecf20Sopenharmony_ci		}
39518c2ecf20Sopenharmony_ci
39528c2ecf20Sopenharmony_ci		ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
39538c2ecf20Sopenharmony_ci					       1, 1);
39548c2ecf20Sopenharmony_ci		if (ret) {
39558c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "Failed to change PHY context\n");
39568c2ecf20Sopenharmony_ci			goto out_unlock;
39578c2ecf20Sopenharmony_ci		}
39588c2ecf20Sopenharmony_ci
39598c2ecf20Sopenharmony_ci		/* Unbind the P2P_DEVICE from the current PHY context */
39608c2ecf20Sopenharmony_ci		ret = iwl_mvm_binding_remove_vif(mvm, vif);
39618c2ecf20Sopenharmony_ci		if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
39628c2ecf20Sopenharmony_ci			goto out_unlock;
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_ci		/* Bind the P2P_DEVICE to the new allocated PHY context */
39678c2ecf20Sopenharmony_ci		mvmvif->phy_ctxt = phy_ctxt;
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ci		ret = iwl_mvm_binding_add_vif(mvm, vif);
39708c2ecf20Sopenharmony_ci		if (WARN(ret, "Failed binding P2P_DEVICE\n"))
39718c2ecf20Sopenharmony_ci			goto out_unlock;
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
39748c2ecf20Sopenharmony_ci	}
39758c2ecf20Sopenharmony_ci
39768c2ecf20Sopenharmony_cischedule_time_event:
39778c2ecf20Sopenharmony_ci	/* Schedule the time events */
39788c2ecf20Sopenharmony_ci	ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
39798c2ecf20Sopenharmony_ci
39808c2ecf20Sopenharmony_ciout_unlock:
39818c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
39828c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "leave\n");
39838c2ecf20Sopenharmony_ci	return ret;
39848c2ecf20Sopenharmony_ci}
39858c2ecf20Sopenharmony_ci
39868c2ecf20Sopenharmony_cistatic int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,
39878c2ecf20Sopenharmony_ci			      struct ieee80211_vif *vif)
39888c2ecf20Sopenharmony_ci{
39898c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
39908c2ecf20Sopenharmony_ci
39918c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "enter\n");
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
39948c2ecf20Sopenharmony_ci	iwl_mvm_stop_roc(mvm, vif);
39958c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "leave\n");
39988c2ecf20Sopenharmony_ci	return 0;
39998c2ecf20Sopenharmony_ci}
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_cistruct iwl_mvm_ftm_responder_iter_data {
40028c2ecf20Sopenharmony_ci	bool responder;
40038c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *ctx;
40048c2ecf20Sopenharmony_ci};
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_cistatic void iwl_mvm_ftm_responder_chanctx_iter(void *_data, u8 *mac,
40078c2ecf20Sopenharmony_ci					       struct ieee80211_vif *vif)
40088c2ecf20Sopenharmony_ci{
40098c2ecf20Sopenharmony_ci	struct iwl_mvm_ftm_responder_iter_data *data = _data;
40108c2ecf20Sopenharmony_ci
40118c2ecf20Sopenharmony_ci	if (rcu_access_pointer(vif->chanctx_conf) == data->ctx &&
40128c2ecf20Sopenharmony_ci	    vif->type == NL80211_IFTYPE_AP && vif->bss_conf.ftmr_params)
40138c2ecf20Sopenharmony_ci		data->responder = true;
40148c2ecf20Sopenharmony_ci}
40158c2ecf20Sopenharmony_ci
40168c2ecf20Sopenharmony_cistatic bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm,
40178c2ecf20Sopenharmony_ci					     struct ieee80211_chanctx_conf *ctx)
40188c2ecf20Sopenharmony_ci{
40198c2ecf20Sopenharmony_ci	struct iwl_mvm_ftm_responder_iter_data data = {
40208c2ecf20Sopenharmony_ci		.responder = false,
40218c2ecf20Sopenharmony_ci		.ctx = ctx,
40228c2ecf20Sopenharmony_ci	};
40238c2ecf20Sopenharmony_ci
40248c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
40258c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
40268c2ecf20Sopenharmony_ci					iwl_mvm_ftm_responder_chanctx_iter,
40278c2ecf20Sopenharmony_ci					&data);
40288c2ecf20Sopenharmony_ci	return data.responder;
40298c2ecf20Sopenharmony_ci}
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_cistatic int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
40328c2ecf20Sopenharmony_ci				 struct ieee80211_chanctx_conf *ctx)
40338c2ecf20Sopenharmony_ci{
40348c2ecf20Sopenharmony_ci	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
40358c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt;
40368c2ecf20Sopenharmony_ci	bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
40378c2ecf20Sopenharmony_ci	struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
40388c2ecf20Sopenharmony_ci	int ret;
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
40438c2ecf20Sopenharmony_ci
40448c2ecf20Sopenharmony_ci	phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
40458c2ecf20Sopenharmony_ci	if (!phy_ctxt) {
40468c2ecf20Sopenharmony_ci		ret = -ENOSPC;
40478c2ecf20Sopenharmony_ci		goto out;
40488c2ecf20Sopenharmony_ci	}
40498c2ecf20Sopenharmony_ci
40508c2ecf20Sopenharmony_ci	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
40518c2ecf20Sopenharmony_ci				       ctx->rx_chains_static,
40528c2ecf20Sopenharmony_ci				       ctx->rx_chains_dynamic);
40538c2ecf20Sopenharmony_ci	if (ret) {
40548c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to add PHY context\n");
40558c2ecf20Sopenharmony_ci		goto out;
40568c2ecf20Sopenharmony_ci	}
40578c2ecf20Sopenharmony_ci
40588c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
40598c2ecf20Sopenharmony_ci	*phy_ctxt_id = phy_ctxt->id;
40608c2ecf20Sopenharmony_ciout:
40618c2ecf20Sopenharmony_ci	return ret;
40628c2ecf20Sopenharmony_ci}
40638c2ecf20Sopenharmony_ci
40648c2ecf20Sopenharmony_cistatic int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
40658c2ecf20Sopenharmony_ci			       struct ieee80211_chanctx_conf *ctx)
40668c2ecf20Sopenharmony_ci{
40678c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
40688c2ecf20Sopenharmony_ci	int ret;
40698c2ecf20Sopenharmony_ci
40708c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
40718c2ecf20Sopenharmony_ci	ret = __iwl_mvm_add_chanctx(mvm, ctx);
40728c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
40738c2ecf20Sopenharmony_ci
40748c2ecf20Sopenharmony_ci	return ret;
40758c2ecf20Sopenharmony_ci}
40768c2ecf20Sopenharmony_ci
40778c2ecf20Sopenharmony_cistatic void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
40788c2ecf20Sopenharmony_ci				     struct ieee80211_chanctx_conf *ctx)
40798c2ecf20Sopenharmony_ci{
40808c2ecf20Sopenharmony_ci	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
40818c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
40828c2ecf20Sopenharmony_ci
40838c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
40848c2ecf20Sopenharmony_ci
40858c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
40868c2ecf20Sopenharmony_ci}
40878c2ecf20Sopenharmony_ci
40888c2ecf20Sopenharmony_cistatic void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
40898c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx_conf *ctx)
40908c2ecf20Sopenharmony_ci{
40918c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
40928c2ecf20Sopenharmony_ci
40938c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
40948c2ecf20Sopenharmony_ci	__iwl_mvm_remove_chanctx(mvm, ctx);
40958c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
40968c2ecf20Sopenharmony_ci}
40978c2ecf20Sopenharmony_ci
40988c2ecf20Sopenharmony_cistatic void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
40998c2ecf20Sopenharmony_ci				   struct ieee80211_chanctx_conf *ctx,
41008c2ecf20Sopenharmony_ci				   u32 changed)
41018c2ecf20Sopenharmony_ci{
41028c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
41038c2ecf20Sopenharmony_ci	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
41048c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
41058c2ecf20Sopenharmony_ci	bool responder = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx);
41068c2ecf20Sopenharmony_ci	struct cfg80211_chan_def *def = responder ? &ctx->def : &ctx->min_def;
41078c2ecf20Sopenharmony_ci
41088c2ecf20Sopenharmony_ci	if (WARN_ONCE((phy_ctxt->ref > 1) &&
41098c2ecf20Sopenharmony_ci		      (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
41108c2ecf20Sopenharmony_ci				   IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
41118c2ecf20Sopenharmony_ci				   IEEE80211_CHANCTX_CHANGE_RADAR |
41128c2ecf20Sopenharmony_ci				   IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),
41138c2ecf20Sopenharmony_ci		      "Cannot change PHY. Ref=%d, changed=0x%X\n",
41148c2ecf20Sopenharmony_ci		      phy_ctxt->ref, changed))
41158c2ecf20Sopenharmony_ci		return;
41168c2ecf20Sopenharmony_ci
41178c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_ci	/* we are only changing the min_width, may be a noop */
41208c2ecf20Sopenharmony_ci	if (changed == IEEE80211_CHANCTX_CHANGE_MIN_WIDTH) {
41218c2ecf20Sopenharmony_ci		if (phy_ctxt->width == def->width)
41228c2ecf20Sopenharmony_ci			goto out_unlock;
41238c2ecf20Sopenharmony_ci
41248c2ecf20Sopenharmony_ci		/* we are just toggling between 20_NOHT and 20 */
41258c2ecf20Sopenharmony_ci		if (phy_ctxt->width <= NL80211_CHAN_WIDTH_20 &&
41268c2ecf20Sopenharmony_ci		    def->width <= NL80211_CHAN_WIDTH_20)
41278c2ecf20Sopenharmony_ci			goto out_unlock;
41288c2ecf20Sopenharmony_ci	}
41298c2ecf20Sopenharmony_ci
41308c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_vif_change(mvm);
41318c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def,
41328c2ecf20Sopenharmony_ci				 ctx->rx_chains_static,
41338c2ecf20Sopenharmony_ci				 ctx->rx_chains_dynamic);
41348c2ecf20Sopenharmony_ci
41358c2ecf20Sopenharmony_ciout_unlock:
41368c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
41378c2ecf20Sopenharmony_ci}
41388c2ecf20Sopenharmony_ci
41398c2ecf20Sopenharmony_cistatic int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
41408c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif,
41418c2ecf20Sopenharmony_ci					struct ieee80211_chanctx_conf *ctx,
41428c2ecf20Sopenharmony_ci					bool switching_chanctx)
41438c2ecf20Sopenharmony_ci{
41448c2ecf20Sopenharmony_ci	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
41458c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
41468c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
41478c2ecf20Sopenharmony_ci	int ret;
41488c2ecf20Sopenharmony_ci
41498c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
41508c2ecf20Sopenharmony_ci
41518c2ecf20Sopenharmony_ci	mvmvif->phy_ctxt = phy_ctxt;
41528c2ecf20Sopenharmony_ci
41538c2ecf20Sopenharmony_ci	switch (vif->type) {
41548c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
41558c2ecf20Sopenharmony_ci		/* only needed if we're switching chanctx (i.e. during CSA) */
41568c2ecf20Sopenharmony_ci		if (switching_chanctx) {
41578c2ecf20Sopenharmony_ci			mvmvif->ap_ibss_active = true;
41588c2ecf20Sopenharmony_ci			break;
41598c2ecf20Sopenharmony_ci		}
41608c2ecf20Sopenharmony_ci		/* fall through */
41618c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
41628c2ecf20Sopenharmony_ci		/*
41638c2ecf20Sopenharmony_ci		 * The AP binding flow is handled as part of the start_ap flow
41648c2ecf20Sopenharmony_ci		 * (in bss_info_changed), similarly for IBSS.
41658c2ecf20Sopenharmony_ci		 */
41668c2ecf20Sopenharmony_ci		ret = 0;
41678c2ecf20Sopenharmony_ci		goto out;
41688c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
41698c2ecf20Sopenharmony_ci		mvmvif->csa_bcn_pending = false;
41708c2ecf20Sopenharmony_ci		break;
41718c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
41728c2ecf20Sopenharmony_ci		/* always disable PS when a monitor interface is active */
41738c2ecf20Sopenharmony_ci		mvmvif->ps_disabled = true;
41748c2ecf20Sopenharmony_ci		break;
41758c2ecf20Sopenharmony_ci	default:
41768c2ecf20Sopenharmony_ci		ret = -EINVAL;
41778c2ecf20Sopenharmony_ci		goto out;
41788c2ecf20Sopenharmony_ci	}
41798c2ecf20Sopenharmony_ci
41808c2ecf20Sopenharmony_ci	ret = iwl_mvm_binding_add_vif(mvm, vif);
41818c2ecf20Sopenharmony_ci	if (ret)
41828c2ecf20Sopenharmony_ci		goto out;
41838c2ecf20Sopenharmony_ci
41848c2ecf20Sopenharmony_ci	/*
41858c2ecf20Sopenharmony_ci	 * Power state must be updated before quotas,
41868c2ecf20Sopenharmony_ci	 * otherwise fw will complain.
41878c2ecf20Sopenharmony_ci	 */
41888c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
41898c2ecf20Sopenharmony_ci
41908c2ecf20Sopenharmony_ci	/* Setting the quota at this stage is only required for monitor
41918c2ecf20Sopenharmony_ci	 * interfaces. For the other types, the bss_info changed flow
41928c2ecf20Sopenharmony_ci	 * will handle quota settings.
41938c2ecf20Sopenharmony_ci	 */
41948c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_MONITOR) {
41958c2ecf20Sopenharmony_ci		mvmvif->monitor_active = true;
41968c2ecf20Sopenharmony_ci		ret = iwl_mvm_update_quotas(mvm, false, NULL);
41978c2ecf20Sopenharmony_ci		if (ret)
41988c2ecf20Sopenharmony_ci			goto out_remove_binding;
41998c2ecf20Sopenharmony_ci
42008c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_snif_sta(mvm, vif);
42018c2ecf20Sopenharmony_ci		if (ret)
42028c2ecf20Sopenharmony_ci			goto out_remove_binding;
42038c2ecf20Sopenharmony_ci
42048c2ecf20Sopenharmony_ci	}
42058c2ecf20Sopenharmony_ci
42068c2ecf20Sopenharmony_ci	/* Handle binding during CSA */
42078c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP) {
42088c2ecf20Sopenharmony_ci		iwl_mvm_update_quotas(mvm, false, NULL);
42098c2ecf20Sopenharmony_ci		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
42108c2ecf20Sopenharmony_ci	}
42118c2ecf20Sopenharmony_ci
42128c2ecf20Sopenharmony_ci	if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
42138c2ecf20Sopenharmony_ci		mvmvif->csa_bcn_pending = true;
42148c2ecf20Sopenharmony_ci
42158c2ecf20Sopenharmony_ci		if (!fw_has_capa(&mvm->fw->ucode_capa,
42168c2ecf20Sopenharmony_ci				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
42178c2ecf20Sopenharmony_ci			u32 duration = 3 * vif->bss_conf.beacon_int;
42188c2ecf20Sopenharmony_ci
42198c2ecf20Sopenharmony_ci			/* Protect the session to make sure we hear the first
42208c2ecf20Sopenharmony_ci			 * beacon on the new channel.
42218c2ecf20Sopenharmony_ci			 */
42228c2ecf20Sopenharmony_ci			iwl_mvm_protect_session(mvm, vif, duration, duration,
42238c2ecf20Sopenharmony_ci						vif->bss_conf.beacon_int / 2,
42248c2ecf20Sopenharmony_ci						true);
42258c2ecf20Sopenharmony_ci		}
42268c2ecf20Sopenharmony_ci
42278c2ecf20Sopenharmony_ci		iwl_mvm_update_quotas(mvm, false, NULL);
42288c2ecf20Sopenharmony_ci	}
42298c2ecf20Sopenharmony_ci
42308c2ecf20Sopenharmony_ci	goto out;
42318c2ecf20Sopenharmony_ci
42328c2ecf20Sopenharmony_ciout_remove_binding:
42338c2ecf20Sopenharmony_ci	iwl_mvm_binding_remove_vif(mvm, vif);
42348c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
42358c2ecf20Sopenharmony_ciout:
42368c2ecf20Sopenharmony_ci	if (ret)
42378c2ecf20Sopenharmony_ci		mvmvif->phy_ctxt = NULL;
42388c2ecf20Sopenharmony_ci	return ret;
42398c2ecf20Sopenharmony_ci}
42408c2ecf20Sopenharmony_cistatic int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
42418c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
42428c2ecf20Sopenharmony_ci				      struct ieee80211_chanctx_conf *ctx)
42438c2ecf20Sopenharmony_ci{
42448c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
42458c2ecf20Sopenharmony_ci	int ret;
42468c2ecf20Sopenharmony_ci
42478c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
42488c2ecf20Sopenharmony_ci	ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
42498c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
42508c2ecf20Sopenharmony_ci
42518c2ecf20Sopenharmony_ci	return ret;
42528c2ecf20Sopenharmony_ci}
42538c2ecf20Sopenharmony_ci
42548c2ecf20Sopenharmony_cistatic void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
42558c2ecf20Sopenharmony_ci					   struct ieee80211_vif *vif,
42568c2ecf20Sopenharmony_ci					   struct ieee80211_chanctx_conf *ctx,
42578c2ecf20Sopenharmony_ci					   bool switching_chanctx)
42588c2ecf20Sopenharmony_ci{
42598c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
42608c2ecf20Sopenharmony_ci	struct ieee80211_vif *disabled_vif = NULL;
42618c2ecf20Sopenharmony_ci
42628c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
42638c2ecf20Sopenharmony_ci
42648c2ecf20Sopenharmony_ci	iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
42658c2ecf20Sopenharmony_ci
42668c2ecf20Sopenharmony_ci	switch (vif->type) {
42678c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
42688c2ecf20Sopenharmony_ci		goto out;
42698c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
42708c2ecf20Sopenharmony_ci		mvmvif->monitor_active = false;
42718c2ecf20Sopenharmony_ci		mvmvif->ps_disabled = false;
42728c2ecf20Sopenharmony_ci		iwl_mvm_rm_snif_sta(mvm, vif);
42738c2ecf20Sopenharmony_ci		break;
42748c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
42758c2ecf20Sopenharmony_ci		/* This part is triggered only during CSA */
42768c2ecf20Sopenharmony_ci		if (!switching_chanctx || !mvmvif->ap_ibss_active)
42778c2ecf20Sopenharmony_ci			goto out;
42788c2ecf20Sopenharmony_ci
42798c2ecf20Sopenharmony_ci		mvmvif->csa_countdown = false;
42808c2ecf20Sopenharmony_ci
42818c2ecf20Sopenharmony_ci		/* Set CS bit on all the stations */
42828c2ecf20Sopenharmony_ci		iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
42838c2ecf20Sopenharmony_ci
42848c2ecf20Sopenharmony_ci		/* Save blocked iface, the timeout is set on the next beacon */
42858c2ecf20Sopenharmony_ci		rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
42868c2ecf20Sopenharmony_ci
42878c2ecf20Sopenharmony_ci		mvmvif->ap_ibss_active = false;
42888c2ecf20Sopenharmony_ci		break;
42898c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
42908c2ecf20Sopenharmony_ci		if (!switching_chanctx)
42918c2ecf20Sopenharmony_ci			break;
42928c2ecf20Sopenharmony_ci
42938c2ecf20Sopenharmony_ci		disabled_vif = vif;
42948c2ecf20Sopenharmony_ci
42958c2ecf20Sopenharmony_ci		if (!fw_has_capa(&mvm->fw->ucode_capa,
42968c2ecf20Sopenharmony_ci				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))
42978c2ecf20Sopenharmony_ci			iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
42988c2ecf20Sopenharmony_ci		break;
42998c2ecf20Sopenharmony_ci	default:
43008c2ecf20Sopenharmony_ci		break;
43018c2ecf20Sopenharmony_ci	}
43028c2ecf20Sopenharmony_ci
43038c2ecf20Sopenharmony_ci	iwl_mvm_update_quotas(mvm, false, disabled_vif);
43048c2ecf20Sopenharmony_ci	iwl_mvm_binding_remove_vif(mvm, vif);
43058c2ecf20Sopenharmony_ci
43068c2ecf20Sopenharmony_ciout:
43078c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD) &&
43088c2ecf20Sopenharmony_ci	    switching_chanctx)
43098c2ecf20Sopenharmony_ci		return;
43108c2ecf20Sopenharmony_ci	mvmvif->phy_ctxt = NULL;
43118c2ecf20Sopenharmony_ci	iwl_mvm_power_update_mac(mvm);
43128c2ecf20Sopenharmony_ci}
43138c2ecf20Sopenharmony_ci
43148c2ecf20Sopenharmony_cistatic void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
43158c2ecf20Sopenharmony_ci					 struct ieee80211_vif *vif,
43168c2ecf20Sopenharmony_ci					 struct ieee80211_chanctx_conf *ctx)
43178c2ecf20Sopenharmony_ci{
43188c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
43198c2ecf20Sopenharmony_ci
43208c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
43218c2ecf20Sopenharmony_ci	__iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
43228c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
43238c2ecf20Sopenharmony_ci}
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_cistatic int
43268c2ecf20Sopenharmony_ciiwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
43278c2ecf20Sopenharmony_ci				struct ieee80211_vif_chanctx_switch *vifs)
43288c2ecf20Sopenharmony_ci{
43298c2ecf20Sopenharmony_ci	int ret;
43308c2ecf20Sopenharmony_ci
43318c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
43328c2ecf20Sopenharmony_ci	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
43338c2ecf20Sopenharmony_ci	__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
43348c2ecf20Sopenharmony_ci
43358c2ecf20Sopenharmony_ci	ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
43368c2ecf20Sopenharmony_ci	if (ret) {
43378c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
43388c2ecf20Sopenharmony_ci		goto out_reassign;
43398c2ecf20Sopenharmony_ci	}
43408c2ecf20Sopenharmony_ci
43418c2ecf20Sopenharmony_ci	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
43428c2ecf20Sopenharmony_ci					   true);
43438c2ecf20Sopenharmony_ci	if (ret) {
43448c2ecf20Sopenharmony_ci		IWL_ERR(mvm,
43458c2ecf20Sopenharmony_ci			"failed to assign new_ctx during channel switch\n");
43468c2ecf20Sopenharmony_ci		goto out_remove;
43478c2ecf20Sopenharmony_ci	}
43488c2ecf20Sopenharmony_ci
43498c2ecf20Sopenharmony_ci	/* we don't support TDLS during DCM - can be caused by channel switch */
43508c2ecf20Sopenharmony_ci	if (iwl_mvm_phy_ctx_count(mvm) > 1)
43518c2ecf20Sopenharmony_ci		iwl_mvm_teardown_tdls_peers(mvm);
43528c2ecf20Sopenharmony_ci
43538c2ecf20Sopenharmony_ci	goto out;
43548c2ecf20Sopenharmony_ci
43558c2ecf20Sopenharmony_ciout_remove:
43568c2ecf20Sopenharmony_ci	__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
43578c2ecf20Sopenharmony_ci
43588c2ecf20Sopenharmony_ciout_reassign:
43598c2ecf20Sopenharmony_ci	if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
43608c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
43618c2ecf20Sopenharmony_ci		goto out_restart;
43628c2ecf20Sopenharmony_ci	}
43638c2ecf20Sopenharmony_ci
43648c2ecf20Sopenharmony_ci	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
43658c2ecf20Sopenharmony_ci					 true)) {
43668c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
43678c2ecf20Sopenharmony_ci		goto out_restart;
43688c2ecf20Sopenharmony_ci	}
43698c2ecf20Sopenharmony_ci
43708c2ecf20Sopenharmony_ci	goto out;
43718c2ecf20Sopenharmony_ci
43728c2ecf20Sopenharmony_ciout_restart:
43738c2ecf20Sopenharmony_ci	/* things keep failing, better restart the hw */
43748c2ecf20Sopenharmony_ci	iwl_mvm_nic_restart(mvm, false);
43758c2ecf20Sopenharmony_ci
43768c2ecf20Sopenharmony_ciout:
43778c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
43788c2ecf20Sopenharmony_ci
43798c2ecf20Sopenharmony_ci	return ret;
43808c2ecf20Sopenharmony_ci}
43818c2ecf20Sopenharmony_ci
43828c2ecf20Sopenharmony_cistatic int
43838c2ecf20Sopenharmony_ciiwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
43848c2ecf20Sopenharmony_ci				    struct ieee80211_vif_chanctx_switch *vifs)
43858c2ecf20Sopenharmony_ci{
43868c2ecf20Sopenharmony_ci	int ret;
43878c2ecf20Sopenharmony_ci
43888c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
43898c2ecf20Sopenharmony_ci	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
43908c2ecf20Sopenharmony_ci
43918c2ecf20Sopenharmony_ci	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
43928c2ecf20Sopenharmony_ci					   true);
43938c2ecf20Sopenharmony_ci	if (ret) {
43948c2ecf20Sopenharmony_ci		IWL_ERR(mvm,
43958c2ecf20Sopenharmony_ci			"failed to assign new_ctx during channel switch\n");
43968c2ecf20Sopenharmony_ci		goto out_reassign;
43978c2ecf20Sopenharmony_ci	}
43988c2ecf20Sopenharmony_ci
43998c2ecf20Sopenharmony_ci	goto out;
44008c2ecf20Sopenharmony_ci
44018c2ecf20Sopenharmony_ciout_reassign:
44028c2ecf20Sopenharmony_ci	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
44038c2ecf20Sopenharmony_ci					 true)) {
44048c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
44058c2ecf20Sopenharmony_ci		goto out_restart;
44068c2ecf20Sopenharmony_ci	}
44078c2ecf20Sopenharmony_ci
44088c2ecf20Sopenharmony_ci	goto out;
44098c2ecf20Sopenharmony_ci
44108c2ecf20Sopenharmony_ciout_restart:
44118c2ecf20Sopenharmony_ci	/* things keep failing, better restart the hw */
44128c2ecf20Sopenharmony_ci	iwl_mvm_nic_restart(mvm, false);
44138c2ecf20Sopenharmony_ci
44148c2ecf20Sopenharmony_ciout:
44158c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
44168c2ecf20Sopenharmony_ci
44178c2ecf20Sopenharmony_ci	return ret;
44188c2ecf20Sopenharmony_ci}
44198c2ecf20Sopenharmony_ci
44208c2ecf20Sopenharmony_cistatic int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
44218c2ecf20Sopenharmony_ci				      struct ieee80211_vif_chanctx_switch *vifs,
44228c2ecf20Sopenharmony_ci				      int n_vifs,
44238c2ecf20Sopenharmony_ci				      enum ieee80211_chanctx_switch_mode mode)
44248c2ecf20Sopenharmony_ci{
44258c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
44268c2ecf20Sopenharmony_ci	int ret;
44278c2ecf20Sopenharmony_ci
44288c2ecf20Sopenharmony_ci	/* we only support a single-vif right now */
44298c2ecf20Sopenharmony_ci	if (n_vifs > 1)
44308c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
44318c2ecf20Sopenharmony_ci
44328c2ecf20Sopenharmony_ci	switch (mode) {
44338c2ecf20Sopenharmony_ci	case CHANCTX_SWMODE_SWAP_CONTEXTS:
44348c2ecf20Sopenharmony_ci		ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
44358c2ecf20Sopenharmony_ci		break;
44368c2ecf20Sopenharmony_ci	case CHANCTX_SWMODE_REASSIGN_VIF:
44378c2ecf20Sopenharmony_ci		ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
44388c2ecf20Sopenharmony_ci		break;
44398c2ecf20Sopenharmony_ci	default:
44408c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
44418c2ecf20Sopenharmony_ci		break;
44428c2ecf20Sopenharmony_ci	}
44438c2ecf20Sopenharmony_ci
44448c2ecf20Sopenharmony_ci	return ret;
44458c2ecf20Sopenharmony_ci}
44468c2ecf20Sopenharmony_ci
44478c2ecf20Sopenharmony_cistatic int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw)
44488c2ecf20Sopenharmony_ci{
44498c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
44508c2ecf20Sopenharmony_ci
44518c2ecf20Sopenharmony_ci	return mvm->ibss_manager;
44528c2ecf20Sopenharmony_ci}
44538c2ecf20Sopenharmony_ci
44548c2ecf20Sopenharmony_cistatic int iwl_mvm_set_tim(struct ieee80211_hw *hw,
44558c2ecf20Sopenharmony_ci			   struct ieee80211_sta *sta,
44568c2ecf20Sopenharmony_ci			   bool set)
44578c2ecf20Sopenharmony_ci{
44588c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
44598c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
44608c2ecf20Sopenharmony_ci
44618c2ecf20Sopenharmony_ci	if (!mvm_sta || !mvm_sta->vif) {
44628c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Station is not associated to a vif\n");
44638c2ecf20Sopenharmony_ci		return -EINVAL;
44648c2ecf20Sopenharmony_ci	}
44658c2ecf20Sopenharmony_ci
44668c2ecf20Sopenharmony_ci	return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
44678c2ecf20Sopenharmony_ci}
44688c2ecf20Sopenharmony_ci
44698c2ecf20Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE
44708c2ecf20Sopenharmony_cistatic const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = {
44718c2ecf20Sopenharmony_ci	[IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 },
44728c2ecf20Sopenharmony_ci	[IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 },
44738c2ecf20Sopenharmony_ci	[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 },
44748c2ecf20Sopenharmony_ci};
44758c2ecf20Sopenharmony_ci
44768c2ecf20Sopenharmony_cistatic int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
44778c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
44788c2ecf20Sopenharmony_ci				      void *data, int len)
44798c2ecf20Sopenharmony_ci{
44808c2ecf20Sopenharmony_ci	struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1];
44818c2ecf20Sopenharmony_ci	int err;
44828c2ecf20Sopenharmony_ci	u32 noa_duration;
44838c2ecf20Sopenharmony_ci
44848c2ecf20Sopenharmony_ci	err = nla_parse_deprecated(tb, IWL_MVM_TM_ATTR_MAX, data, len,
44858c2ecf20Sopenharmony_ci				   iwl_mvm_tm_policy, NULL);
44868c2ecf20Sopenharmony_ci	if (err)
44878c2ecf20Sopenharmony_ci		return err;
44888c2ecf20Sopenharmony_ci
44898c2ecf20Sopenharmony_ci	if (!tb[IWL_MVM_TM_ATTR_CMD])
44908c2ecf20Sopenharmony_ci		return -EINVAL;
44918c2ecf20Sopenharmony_ci
44928c2ecf20Sopenharmony_ci	switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) {
44938c2ecf20Sopenharmony_ci	case IWL_MVM_TM_CMD_SET_NOA:
44948c2ecf20Sopenharmony_ci		if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p ||
44958c2ecf20Sopenharmony_ci		    !vif->bss_conf.enable_beacon ||
44968c2ecf20Sopenharmony_ci		    !tb[IWL_MVM_TM_ATTR_NOA_DURATION])
44978c2ecf20Sopenharmony_ci			return -EINVAL;
44988c2ecf20Sopenharmony_ci
44998c2ecf20Sopenharmony_ci		noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]);
45008c2ecf20Sopenharmony_ci		if (noa_duration >= vif->bss_conf.beacon_int)
45018c2ecf20Sopenharmony_ci			return -EINVAL;
45028c2ecf20Sopenharmony_ci
45038c2ecf20Sopenharmony_ci		mvm->noa_duration = noa_duration;
45048c2ecf20Sopenharmony_ci		mvm->noa_vif = vif;
45058c2ecf20Sopenharmony_ci
45068c2ecf20Sopenharmony_ci		return iwl_mvm_update_quotas(mvm, true, NULL);
45078c2ecf20Sopenharmony_ci	case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
45088c2ecf20Sopenharmony_ci		/* must be associated client vif - ignore authorized */
45098c2ecf20Sopenharmony_ci		if (!vif || vif->type != NL80211_IFTYPE_STATION ||
45108c2ecf20Sopenharmony_ci		    !vif->bss_conf.assoc || !vif->bss_conf.dtim_period ||
45118c2ecf20Sopenharmony_ci		    !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])
45128c2ecf20Sopenharmony_ci			return -EINVAL;
45138c2ecf20Sopenharmony_ci
45148c2ecf20Sopenharmony_ci		if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
45158c2ecf20Sopenharmony_ci			return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
45168c2ecf20Sopenharmony_ci		return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
45178c2ecf20Sopenharmony_ci	}
45188c2ecf20Sopenharmony_ci
45198c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
45208c2ecf20Sopenharmony_ci}
45218c2ecf20Sopenharmony_ci
45228c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
45238c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
45248c2ecf20Sopenharmony_ci				    void *data, int len)
45258c2ecf20Sopenharmony_ci{
45268c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
45278c2ecf20Sopenharmony_ci	int err;
45288c2ecf20Sopenharmony_ci
45298c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
45308c2ecf20Sopenharmony_ci	err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len);
45318c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
45328c2ecf20Sopenharmony_ci
45338c2ecf20Sopenharmony_ci	return err;
45348c2ecf20Sopenharmony_ci}
45358c2ecf20Sopenharmony_ci#endif
45368c2ecf20Sopenharmony_ci
45378c2ecf20Sopenharmony_cistatic void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
45388c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif,
45398c2ecf20Sopenharmony_ci				   struct ieee80211_channel_switch *chsw)
45408c2ecf20Sopenharmony_ci{
45418c2ecf20Sopenharmony_ci	/* By implementing this operation, we prevent mac80211 from
45428c2ecf20Sopenharmony_ci	 * starting its own channel switch timer, so that we can call
45438c2ecf20Sopenharmony_ci	 * ieee80211_chswitch_done() ourselves at the right time
45448c2ecf20Sopenharmony_ci	 * (which is when the absence time event starts).
45458c2ecf20Sopenharmony_ci	 */
45468c2ecf20Sopenharmony_ci
45478c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
45488c2ecf20Sopenharmony_ci			   "dummy channel switch op\n");
45498c2ecf20Sopenharmony_ci}
45508c2ecf20Sopenharmony_ci
45518c2ecf20Sopenharmony_cistatic int iwl_mvm_schedule_client_csa(struct iwl_mvm *mvm,
45528c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
45538c2ecf20Sopenharmony_ci				       struct ieee80211_channel_switch *chsw)
45548c2ecf20Sopenharmony_ci{
45558c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
45568c2ecf20Sopenharmony_ci	struct iwl_chan_switch_te_cmd cmd = {
45578c2ecf20Sopenharmony_ci		.mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
45588c2ecf20Sopenharmony_ci							  mvmvif->color)),
45598c2ecf20Sopenharmony_ci		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
45608c2ecf20Sopenharmony_ci		.tsf = cpu_to_le32(chsw->timestamp),
45618c2ecf20Sopenharmony_ci		.cs_count = chsw->count,
45628c2ecf20Sopenharmony_ci		.cs_mode = chsw->block_tx,
45638c2ecf20Sopenharmony_ci	};
45648c2ecf20Sopenharmony_ci
45658c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
45668c2ecf20Sopenharmony_ci
45678c2ecf20Sopenharmony_ci	if (chsw->delay)
45688c2ecf20Sopenharmony_ci		cmd.cs_delayed_bcn_count =
45698c2ecf20Sopenharmony_ci			DIV_ROUND_UP(chsw->delay, vif->bss_conf.beacon_int);
45708c2ecf20Sopenharmony_ci
45718c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm,
45728c2ecf20Sopenharmony_ci				    WIDE_ID(MAC_CONF_GROUP,
45738c2ecf20Sopenharmony_ci					    CHANNEL_SWITCH_TIME_EVENT_CMD),
45748c2ecf20Sopenharmony_ci				    0, sizeof(cmd), &cmd);
45758c2ecf20Sopenharmony_ci}
45768c2ecf20Sopenharmony_ci
45778c2ecf20Sopenharmony_cistatic int iwl_mvm_old_pre_chan_sw_sta(struct iwl_mvm *mvm,
45788c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
45798c2ecf20Sopenharmony_ci				       struct ieee80211_channel_switch *chsw)
45808c2ecf20Sopenharmony_ci{
45818c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
45828c2ecf20Sopenharmony_ci	u32 apply_time;
45838c2ecf20Sopenharmony_ci
45848c2ecf20Sopenharmony_ci	/* Schedule the time event to a bit before beacon 1,
45858c2ecf20Sopenharmony_ci	 * to make sure we're in the new channel when the
45868c2ecf20Sopenharmony_ci	 * GO/AP arrives. In case count <= 1 immediately schedule the
45878c2ecf20Sopenharmony_ci	 * TE (this might result with some packet loss or connection
45888c2ecf20Sopenharmony_ci	 * loss).
45898c2ecf20Sopenharmony_ci	 */
45908c2ecf20Sopenharmony_ci	if (chsw->count <= 1)
45918c2ecf20Sopenharmony_ci		apply_time = 0;
45928c2ecf20Sopenharmony_ci	else
45938c2ecf20Sopenharmony_ci		apply_time = chsw->device_timestamp +
45948c2ecf20Sopenharmony_ci			((vif->bss_conf.beacon_int * (chsw->count - 1) -
45958c2ecf20Sopenharmony_ci			  IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
45968c2ecf20Sopenharmony_ci
45978c2ecf20Sopenharmony_ci	if (chsw->block_tx)
45988c2ecf20Sopenharmony_ci		iwl_mvm_csa_client_absent(mvm, vif);
45998c2ecf20Sopenharmony_ci
46008c2ecf20Sopenharmony_ci	if (mvmvif->bf_data.bf_enabled) {
46018c2ecf20Sopenharmony_ci		int ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci		if (ret)
46048c2ecf20Sopenharmony_ci			return ret;
46058c2ecf20Sopenharmony_ci	}
46068c2ecf20Sopenharmony_ci
46078c2ecf20Sopenharmony_ci	iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
46088c2ecf20Sopenharmony_ci				    apply_time);
46098c2ecf20Sopenharmony_ci
46108c2ecf20Sopenharmony_ci	return 0;
46118c2ecf20Sopenharmony_ci}
46128c2ecf20Sopenharmony_ci
46138c2ecf20Sopenharmony_ci#define IWL_MAX_CSA_BLOCK_TX 1500
46148c2ecf20Sopenharmony_cistatic int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
46158c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
46168c2ecf20Sopenharmony_ci				      struct ieee80211_channel_switch *chsw)
46178c2ecf20Sopenharmony_ci{
46188c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
46198c2ecf20Sopenharmony_ci	struct ieee80211_vif *csa_vif;
46208c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
46218c2ecf20Sopenharmony_ci	int ret;
46228c2ecf20Sopenharmony_ci
46238c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	mvmvif->csa_failed = false;
46268c2ecf20Sopenharmony_ci
46278c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
46288c2ecf20Sopenharmony_ci			   chsw->chandef.center_freq1);
46298c2ecf20Sopenharmony_ci
46308c2ecf20Sopenharmony_ci	iwl_fw_dbg_trigger_simple_stop(&mvm->fwrt,
46318c2ecf20Sopenharmony_ci				       ieee80211_vif_to_wdev(vif),
46328c2ecf20Sopenharmony_ci				       FW_DBG_TRIGGER_CHANNEL_SWITCH);
46338c2ecf20Sopenharmony_ci
46348c2ecf20Sopenharmony_ci	switch (vif->type) {
46358c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
46368c2ecf20Sopenharmony_ci		csa_vif =
46378c2ecf20Sopenharmony_ci			rcu_dereference_protected(mvm->csa_vif,
46388c2ecf20Sopenharmony_ci						  lockdep_is_held(&mvm->mutex));
46398c2ecf20Sopenharmony_ci		if (WARN_ONCE(csa_vif && csa_vif->csa_active,
46408c2ecf20Sopenharmony_ci			      "Another CSA is already in progress")) {
46418c2ecf20Sopenharmony_ci			ret = -EBUSY;
46428c2ecf20Sopenharmony_ci			goto out_unlock;
46438c2ecf20Sopenharmony_ci		}
46448c2ecf20Sopenharmony_ci
46458c2ecf20Sopenharmony_ci		/* we still didn't unblock tx. prevent new CS meanwhile */
46468c2ecf20Sopenharmony_ci		if (rcu_dereference_protected(mvm->csa_tx_blocked_vif,
46478c2ecf20Sopenharmony_ci					      lockdep_is_held(&mvm->mutex))) {
46488c2ecf20Sopenharmony_ci			ret = -EBUSY;
46498c2ecf20Sopenharmony_ci			goto out_unlock;
46508c2ecf20Sopenharmony_ci		}
46518c2ecf20Sopenharmony_ci
46528c2ecf20Sopenharmony_ci		rcu_assign_pointer(mvm->csa_vif, vif);
46538c2ecf20Sopenharmony_ci
46548c2ecf20Sopenharmony_ci		if (WARN_ONCE(mvmvif->csa_countdown,
46558c2ecf20Sopenharmony_ci			      "Previous CSA countdown didn't complete")) {
46568c2ecf20Sopenharmony_ci			ret = -EBUSY;
46578c2ecf20Sopenharmony_ci			goto out_unlock;
46588c2ecf20Sopenharmony_ci		}
46598c2ecf20Sopenharmony_ci
46608c2ecf20Sopenharmony_ci		mvmvif->csa_target_freq = chsw->chandef.chan->center_freq;
46618c2ecf20Sopenharmony_ci
46628c2ecf20Sopenharmony_ci		break;
46638c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
46648c2ecf20Sopenharmony_ci		if (chsw->block_tx) {
46658c2ecf20Sopenharmony_ci			/*
46668c2ecf20Sopenharmony_ci			 * In case of undetermined / long time with immediate
46678c2ecf20Sopenharmony_ci			 * quiet monitor status to gracefully disconnect
46688c2ecf20Sopenharmony_ci			 */
46698c2ecf20Sopenharmony_ci			if (!chsw->count ||
46708c2ecf20Sopenharmony_ci			    chsw->count * vif->bss_conf.beacon_int >
46718c2ecf20Sopenharmony_ci			    IWL_MAX_CSA_BLOCK_TX)
46728c2ecf20Sopenharmony_ci				schedule_delayed_work(&mvmvif->csa_work,
46738c2ecf20Sopenharmony_ci						      msecs_to_jiffies(IWL_MAX_CSA_BLOCK_TX));
46748c2ecf20Sopenharmony_ci		}
46758c2ecf20Sopenharmony_ci
46768c2ecf20Sopenharmony_ci		if (!fw_has_capa(&mvm->fw->ucode_capa,
46778c2ecf20Sopenharmony_ci				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
46788c2ecf20Sopenharmony_ci			ret = iwl_mvm_old_pre_chan_sw_sta(mvm, vif, chsw);
46798c2ecf20Sopenharmony_ci			if (ret)
46808c2ecf20Sopenharmony_ci				goto out_unlock;
46818c2ecf20Sopenharmony_ci		} else {
46828c2ecf20Sopenharmony_ci			iwl_mvm_schedule_client_csa(mvm, vif, chsw);
46838c2ecf20Sopenharmony_ci		}
46848c2ecf20Sopenharmony_ci
46858c2ecf20Sopenharmony_ci		mvmvif->csa_count = chsw->count;
46868c2ecf20Sopenharmony_ci		mvmvif->csa_misbehave = false;
46878c2ecf20Sopenharmony_ci		break;
46888c2ecf20Sopenharmony_ci	default:
46898c2ecf20Sopenharmony_ci		break;
46908c2ecf20Sopenharmony_ci	}
46918c2ecf20Sopenharmony_ci
46928c2ecf20Sopenharmony_ci	mvmvif->ps_disabled = true;
46938c2ecf20Sopenharmony_ci
46948c2ecf20Sopenharmony_ci	ret = iwl_mvm_power_update_ps(mvm);
46958c2ecf20Sopenharmony_ci	if (ret)
46968c2ecf20Sopenharmony_ci		goto out_unlock;
46978c2ecf20Sopenharmony_ci
46988c2ecf20Sopenharmony_ci	/* we won't be on this channel any longer */
46998c2ecf20Sopenharmony_ci	iwl_mvm_teardown_tdls_peers(mvm);
47008c2ecf20Sopenharmony_ci
47018c2ecf20Sopenharmony_ciout_unlock:
47028c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
47038c2ecf20Sopenharmony_ci
47048c2ecf20Sopenharmony_ci	return ret;
47058c2ecf20Sopenharmony_ci}
47068c2ecf20Sopenharmony_ci
47078c2ecf20Sopenharmony_cistatic void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
47088c2ecf20Sopenharmony_ci					     struct ieee80211_vif *vif,
47098c2ecf20Sopenharmony_ci					     struct ieee80211_channel_switch *chsw)
47108c2ecf20Sopenharmony_ci{
47118c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
47128c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
47138c2ecf20Sopenharmony_ci	struct iwl_chan_switch_te_cmd cmd = {
47148c2ecf20Sopenharmony_ci		.mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
47158c2ecf20Sopenharmony_ci							  mvmvif->color)),
47168c2ecf20Sopenharmony_ci		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
47178c2ecf20Sopenharmony_ci		.tsf = cpu_to_le32(chsw->timestamp),
47188c2ecf20Sopenharmony_ci		.cs_count = chsw->count,
47198c2ecf20Sopenharmony_ci		.cs_mode = chsw->block_tx,
47208c2ecf20Sopenharmony_ci	};
47218c2ecf20Sopenharmony_ci
47228c2ecf20Sopenharmony_ci	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CS_MODIFY))
47238c2ecf20Sopenharmony_ci		return;
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci	if (chsw->count >= mvmvif->csa_count && chsw->block_tx) {
47268c2ecf20Sopenharmony_ci		if (mvmvif->csa_misbehave) {
47278c2ecf20Sopenharmony_ci			/* Second time, give up on this AP*/
47288c2ecf20Sopenharmony_ci			iwl_mvm_abort_channel_switch(hw, vif);
47298c2ecf20Sopenharmony_ci			ieee80211_chswitch_done(vif, false);
47308c2ecf20Sopenharmony_ci			mvmvif->csa_misbehave = false;
47318c2ecf20Sopenharmony_ci			return;
47328c2ecf20Sopenharmony_ci		}
47338c2ecf20Sopenharmony_ci		mvmvif->csa_misbehave = true;
47348c2ecf20Sopenharmony_ci	}
47358c2ecf20Sopenharmony_ci	mvmvif->csa_count = chsw->count;
47368c2ecf20Sopenharmony_ci
47378c2ecf20Sopenharmony_ci	IWL_DEBUG_MAC80211(mvm, "Modify CSA on mac %d\n", mvmvif->id);
47388c2ecf20Sopenharmony_ci
47398c2ecf20Sopenharmony_ci	WARN_ON(iwl_mvm_send_cmd_pdu(mvm,
47408c2ecf20Sopenharmony_ci				     WIDE_ID(MAC_CONF_GROUP,
47418c2ecf20Sopenharmony_ci					     CHANNEL_SWITCH_TIME_EVENT_CMD),
47428c2ecf20Sopenharmony_ci				     CMD_ASYNC, sizeof(cmd), &cmd));
47438c2ecf20Sopenharmony_ci}
47448c2ecf20Sopenharmony_ci
47458c2ecf20Sopenharmony_cistatic void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)
47468c2ecf20Sopenharmony_ci{
47478c2ecf20Sopenharmony_ci	int i;
47488c2ecf20Sopenharmony_ci
47498c2ecf20Sopenharmony_ci	if (!iwl_mvm_has_new_tx_api(mvm)) {
47508c2ecf20Sopenharmony_ci		if (drop) {
47518c2ecf20Sopenharmony_ci			mutex_lock(&mvm->mutex);
47528c2ecf20Sopenharmony_ci			iwl_mvm_flush_tx_path(mvm,
47538c2ecf20Sopenharmony_ci				iwl_mvm_flushable_queues(mvm) & queues, 0);
47548c2ecf20Sopenharmony_ci			mutex_unlock(&mvm->mutex);
47558c2ecf20Sopenharmony_ci		} else {
47568c2ecf20Sopenharmony_ci			iwl_trans_wait_tx_queues_empty(mvm->trans, queues);
47578c2ecf20Sopenharmony_ci		}
47588c2ecf20Sopenharmony_ci		return;
47598c2ecf20Sopenharmony_ci	}
47608c2ecf20Sopenharmony_ci
47618c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
47628c2ecf20Sopenharmony_ci	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
47638c2ecf20Sopenharmony_ci		struct ieee80211_sta *sta;
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
47668c2ecf20Sopenharmony_ci						lockdep_is_held(&mvm->mutex));
47678c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(sta))
47688c2ecf20Sopenharmony_ci			continue;
47698c2ecf20Sopenharmony_ci
47708c2ecf20Sopenharmony_ci		if (drop)
47718c2ecf20Sopenharmony_ci			iwl_mvm_flush_sta_tids(mvm, i, 0xFFFF, 0);
47728c2ecf20Sopenharmony_ci		else
47738c2ecf20Sopenharmony_ci			iwl_mvm_wait_sta_queues_empty(mvm,
47748c2ecf20Sopenharmony_ci					iwl_mvm_sta_from_mac80211(sta));
47758c2ecf20Sopenharmony_ci	}
47768c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
47778c2ecf20Sopenharmony_ci}
47788c2ecf20Sopenharmony_ci
47798c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
47808c2ecf20Sopenharmony_ci			      struct ieee80211_vif *vif, u32 queues, bool drop)
47818c2ecf20Sopenharmony_ci{
47828c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
47838c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif;
47848c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta;
47858c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
47868c2ecf20Sopenharmony_ci	int i;
47878c2ecf20Sopenharmony_ci	u32 msk = 0;
47888c2ecf20Sopenharmony_ci
47898c2ecf20Sopenharmony_ci	if (!vif) {
47908c2ecf20Sopenharmony_ci		iwl_mvm_flush_no_vif(mvm, queues, drop);
47918c2ecf20Sopenharmony_ci		return;
47928c2ecf20Sopenharmony_ci	}
47938c2ecf20Sopenharmony_ci
47948c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION)
47958c2ecf20Sopenharmony_ci		return;
47968c2ecf20Sopenharmony_ci
47978c2ecf20Sopenharmony_ci	/* Make sure we're done with the deferred traffic before flushing */
47988c2ecf20Sopenharmony_ci	flush_work(&mvm->add_stream_wk);
47998c2ecf20Sopenharmony_ci
48008c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
48018c2ecf20Sopenharmony_ci	mvmvif = iwl_mvm_vif_from_mac80211(vif);
48028c2ecf20Sopenharmony_ci
48038c2ecf20Sopenharmony_ci	/* flush the AP-station and all TDLS peers */
48048c2ecf20Sopenharmony_ci	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
48058c2ecf20Sopenharmony_ci		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
48068c2ecf20Sopenharmony_ci						lockdep_is_held(&mvm->mutex));
48078c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(sta))
48088c2ecf20Sopenharmony_ci			continue;
48098c2ecf20Sopenharmony_ci
48108c2ecf20Sopenharmony_ci		mvmsta = iwl_mvm_sta_from_mac80211(sta);
48118c2ecf20Sopenharmony_ci		if (mvmsta->vif != vif)
48128c2ecf20Sopenharmony_ci			continue;
48138c2ecf20Sopenharmony_ci
48148c2ecf20Sopenharmony_ci		/* make sure only TDLS peers or the AP are flushed */
48158c2ecf20Sopenharmony_ci		WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
48168c2ecf20Sopenharmony_ci
48178c2ecf20Sopenharmony_ci		if (drop) {
48188c2ecf20Sopenharmony_ci			if (iwl_mvm_flush_sta(mvm, mvmsta, false))
48198c2ecf20Sopenharmony_ci				IWL_ERR(mvm, "flush request fail\n");
48208c2ecf20Sopenharmony_ci		} else {
48218c2ecf20Sopenharmony_ci			msk |= mvmsta->tfd_queue_msk;
48228c2ecf20Sopenharmony_ci			if (iwl_mvm_has_new_tx_api(mvm))
48238c2ecf20Sopenharmony_ci				iwl_mvm_wait_sta_queues_empty(mvm, mvmsta);
48248c2ecf20Sopenharmony_ci		}
48258c2ecf20Sopenharmony_ci	}
48268c2ecf20Sopenharmony_ci
48278c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
48288c2ecf20Sopenharmony_ci
48298c2ecf20Sopenharmony_ci	/* this can take a while, and we may need/want other operations
48308c2ecf20Sopenharmony_ci	 * to succeed while doing this, so do it without the mutex held
48318c2ecf20Sopenharmony_ci	 */
48328c2ecf20Sopenharmony_ci	if (!drop && !iwl_mvm_has_new_tx_api(mvm))
48338c2ecf20Sopenharmony_ci		iwl_trans_wait_tx_queues_empty(mvm->trans, msk);
48348c2ecf20Sopenharmony_ci}
48358c2ecf20Sopenharmony_ci
48368c2ecf20Sopenharmony_cistatic int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
48378c2ecf20Sopenharmony_ci				  struct survey_info *survey)
48388c2ecf20Sopenharmony_ci{
48398c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
48408c2ecf20Sopenharmony_ci	int ret;
48418c2ecf20Sopenharmony_ci
48428c2ecf20Sopenharmony_ci	memset(survey, 0, sizeof(*survey));
48438c2ecf20Sopenharmony_ci
48448c2ecf20Sopenharmony_ci	/* only support global statistics right now */
48458c2ecf20Sopenharmony_ci	if (idx != 0)
48468c2ecf20Sopenharmony_ci		return -ENOENT;
48478c2ecf20Sopenharmony_ci
48488c2ecf20Sopenharmony_ci	if (!fw_has_capa(&mvm->fw->ucode_capa,
48498c2ecf20Sopenharmony_ci			 IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
48508c2ecf20Sopenharmony_ci		return -ENOENT;
48518c2ecf20Sopenharmony_ci
48528c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
48538c2ecf20Sopenharmony_ci
48548c2ecf20Sopenharmony_ci	if (iwl_mvm_firmware_running(mvm)) {
48558c2ecf20Sopenharmony_ci		ret = iwl_mvm_request_statistics(mvm, false);
48568c2ecf20Sopenharmony_ci		if (ret)
48578c2ecf20Sopenharmony_ci			goto out;
48588c2ecf20Sopenharmony_ci	}
48598c2ecf20Sopenharmony_ci
48608c2ecf20Sopenharmony_ci	survey->filled = SURVEY_INFO_TIME |
48618c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME_RX |
48628c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME_TX |
48638c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME_SCAN;
48648c2ecf20Sopenharmony_ci	survey->time = mvm->accu_radio_stats.on_time_rf +
48658c2ecf20Sopenharmony_ci		       mvm->radio_stats.on_time_rf;
48668c2ecf20Sopenharmony_ci	do_div(survey->time, USEC_PER_MSEC);
48678c2ecf20Sopenharmony_ci
48688c2ecf20Sopenharmony_ci	survey->time_rx = mvm->accu_radio_stats.rx_time +
48698c2ecf20Sopenharmony_ci			  mvm->radio_stats.rx_time;
48708c2ecf20Sopenharmony_ci	do_div(survey->time_rx, USEC_PER_MSEC);
48718c2ecf20Sopenharmony_ci
48728c2ecf20Sopenharmony_ci	survey->time_tx = mvm->accu_radio_stats.tx_time +
48738c2ecf20Sopenharmony_ci			  mvm->radio_stats.tx_time;
48748c2ecf20Sopenharmony_ci	do_div(survey->time_tx, USEC_PER_MSEC);
48758c2ecf20Sopenharmony_ci
48768c2ecf20Sopenharmony_ci	survey->time_scan = mvm->accu_radio_stats.on_time_scan +
48778c2ecf20Sopenharmony_ci			    mvm->radio_stats.on_time_scan;
48788c2ecf20Sopenharmony_ci	do_div(survey->time_scan, USEC_PER_MSEC);
48798c2ecf20Sopenharmony_ci
48808c2ecf20Sopenharmony_ci	ret = 0;
48818c2ecf20Sopenharmony_ci out:
48828c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
48838c2ecf20Sopenharmony_ci	return ret;
48848c2ecf20Sopenharmony_ci}
48858c2ecf20Sopenharmony_ci
48868c2ecf20Sopenharmony_cistatic void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)
48878c2ecf20Sopenharmony_ci{
48888c2ecf20Sopenharmony_ci	switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
48898c2ecf20Sopenharmony_ci	case RATE_MCS_CHAN_WIDTH_20:
48908c2ecf20Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_20;
48918c2ecf20Sopenharmony_ci		break;
48928c2ecf20Sopenharmony_ci	case RATE_MCS_CHAN_WIDTH_40:
48938c2ecf20Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_40;
48948c2ecf20Sopenharmony_ci		break;
48958c2ecf20Sopenharmony_ci	case RATE_MCS_CHAN_WIDTH_80:
48968c2ecf20Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_80;
48978c2ecf20Sopenharmony_ci		break;
48988c2ecf20Sopenharmony_ci	case RATE_MCS_CHAN_WIDTH_160:
48998c2ecf20Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_160;
49008c2ecf20Sopenharmony_ci		break;
49018c2ecf20Sopenharmony_ci	}
49028c2ecf20Sopenharmony_ci
49038c2ecf20Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT_MSK) {
49048c2ecf20Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_MCS;
49058c2ecf20Sopenharmony_ci		rinfo->mcs = u32_get_bits(rate_n_flags, RATE_HT_MCS_INDEX_MSK);
49068c2ecf20Sopenharmony_ci		rinfo->nss = u32_get_bits(rate_n_flags,
49078c2ecf20Sopenharmony_ci					  RATE_HT_MCS_NSS_MSK) + 1;
49088c2ecf20Sopenharmony_ci		if (rate_n_flags & RATE_MCS_SGI_MSK)
49098c2ecf20Sopenharmony_ci			rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
49108c2ecf20Sopenharmony_ci	} else if (rate_n_flags & RATE_MCS_VHT_MSK) {
49118c2ecf20Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
49128c2ecf20Sopenharmony_ci		rinfo->mcs = u32_get_bits(rate_n_flags,
49138c2ecf20Sopenharmony_ci					  RATE_VHT_MCS_RATE_CODE_MSK);
49148c2ecf20Sopenharmony_ci		rinfo->nss = u32_get_bits(rate_n_flags,
49158c2ecf20Sopenharmony_ci					  RATE_VHT_MCS_NSS_MSK) + 1;
49168c2ecf20Sopenharmony_ci		if (rate_n_flags & RATE_MCS_SGI_MSK)
49178c2ecf20Sopenharmony_ci			rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
49188c2ecf20Sopenharmony_ci	} else if (rate_n_flags & RATE_MCS_HE_MSK) {
49198c2ecf20Sopenharmony_ci		u32 gi_ltf = u32_get_bits(rate_n_flags,
49208c2ecf20Sopenharmony_ci					  RATE_MCS_HE_GI_LTF_MSK);
49218c2ecf20Sopenharmony_ci
49228c2ecf20Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_HE_MCS;
49238c2ecf20Sopenharmony_ci		rinfo->mcs = u32_get_bits(rate_n_flags,
49248c2ecf20Sopenharmony_ci					  RATE_VHT_MCS_RATE_CODE_MSK);
49258c2ecf20Sopenharmony_ci		rinfo->nss = u32_get_bits(rate_n_flags,
49268c2ecf20Sopenharmony_ci					  RATE_VHT_MCS_NSS_MSK) + 1;
49278c2ecf20Sopenharmony_ci
49288c2ecf20Sopenharmony_ci		if (rate_n_flags & RATE_MCS_HE_106T_MSK) {
49298c2ecf20Sopenharmony_ci			rinfo->bw = RATE_INFO_BW_HE_RU;
49308c2ecf20Sopenharmony_ci			rinfo->he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106;
49318c2ecf20Sopenharmony_ci		}
49328c2ecf20Sopenharmony_ci
49338c2ecf20Sopenharmony_ci		switch (rate_n_flags & RATE_MCS_HE_TYPE_MSK) {
49348c2ecf20Sopenharmony_ci		case RATE_MCS_HE_TYPE_SU:
49358c2ecf20Sopenharmony_ci		case RATE_MCS_HE_TYPE_EXT_SU:
49368c2ecf20Sopenharmony_ci			if (gi_ltf == 0 || gi_ltf == 1)
49378c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
49388c2ecf20Sopenharmony_ci			else if (gi_ltf == 2)
49398c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
49408c2ecf20Sopenharmony_ci			else if (rate_n_flags & RATE_MCS_SGI_MSK)
49418c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
49428c2ecf20Sopenharmony_ci			else
49438c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
49448c2ecf20Sopenharmony_ci			break;
49458c2ecf20Sopenharmony_ci		case RATE_MCS_HE_TYPE_MU:
49468c2ecf20Sopenharmony_ci			if (gi_ltf == 0 || gi_ltf == 1)
49478c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8;
49488c2ecf20Sopenharmony_ci			else if (gi_ltf == 2)
49498c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
49508c2ecf20Sopenharmony_ci			else
49518c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
49528c2ecf20Sopenharmony_ci			break;
49538c2ecf20Sopenharmony_ci		case RATE_MCS_HE_TYPE_TRIG:
49548c2ecf20Sopenharmony_ci			if (gi_ltf == 0 || gi_ltf == 1)
49558c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
49568c2ecf20Sopenharmony_ci			else
49578c2ecf20Sopenharmony_ci				rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2;
49588c2ecf20Sopenharmony_ci			break;
49598c2ecf20Sopenharmony_ci		}
49608c2ecf20Sopenharmony_ci
49618c2ecf20Sopenharmony_ci		if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK)
49628c2ecf20Sopenharmony_ci			rinfo->he_dcm = 1;
49638c2ecf20Sopenharmony_ci	} else {
49648c2ecf20Sopenharmony_ci		switch (u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK)) {
49658c2ecf20Sopenharmony_ci		case IWL_RATE_1M_PLCP:
49668c2ecf20Sopenharmony_ci			rinfo->legacy = 10;
49678c2ecf20Sopenharmony_ci			break;
49688c2ecf20Sopenharmony_ci		case IWL_RATE_2M_PLCP:
49698c2ecf20Sopenharmony_ci			rinfo->legacy = 20;
49708c2ecf20Sopenharmony_ci			break;
49718c2ecf20Sopenharmony_ci		case IWL_RATE_5M_PLCP:
49728c2ecf20Sopenharmony_ci			rinfo->legacy = 55;
49738c2ecf20Sopenharmony_ci			break;
49748c2ecf20Sopenharmony_ci		case IWL_RATE_11M_PLCP:
49758c2ecf20Sopenharmony_ci			rinfo->legacy = 110;
49768c2ecf20Sopenharmony_ci			break;
49778c2ecf20Sopenharmony_ci		case IWL_RATE_6M_PLCP:
49788c2ecf20Sopenharmony_ci			rinfo->legacy = 60;
49798c2ecf20Sopenharmony_ci			break;
49808c2ecf20Sopenharmony_ci		case IWL_RATE_9M_PLCP:
49818c2ecf20Sopenharmony_ci			rinfo->legacy = 90;
49828c2ecf20Sopenharmony_ci			break;
49838c2ecf20Sopenharmony_ci		case IWL_RATE_12M_PLCP:
49848c2ecf20Sopenharmony_ci			rinfo->legacy = 120;
49858c2ecf20Sopenharmony_ci			break;
49868c2ecf20Sopenharmony_ci		case IWL_RATE_18M_PLCP:
49878c2ecf20Sopenharmony_ci			rinfo->legacy = 180;
49888c2ecf20Sopenharmony_ci			break;
49898c2ecf20Sopenharmony_ci		case IWL_RATE_24M_PLCP:
49908c2ecf20Sopenharmony_ci			rinfo->legacy = 240;
49918c2ecf20Sopenharmony_ci			break;
49928c2ecf20Sopenharmony_ci		case IWL_RATE_36M_PLCP:
49938c2ecf20Sopenharmony_ci			rinfo->legacy = 360;
49948c2ecf20Sopenharmony_ci			break;
49958c2ecf20Sopenharmony_ci		case IWL_RATE_48M_PLCP:
49968c2ecf20Sopenharmony_ci			rinfo->legacy = 480;
49978c2ecf20Sopenharmony_ci			break;
49988c2ecf20Sopenharmony_ci		case IWL_RATE_54M_PLCP:
49998c2ecf20Sopenharmony_ci			rinfo->legacy = 540;
50008c2ecf20Sopenharmony_ci			break;
50018c2ecf20Sopenharmony_ci		}
50028c2ecf20Sopenharmony_ci	}
50038c2ecf20Sopenharmony_ci}
50048c2ecf20Sopenharmony_ci
50058c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
50068c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
50078c2ecf20Sopenharmony_ci				       struct ieee80211_sta *sta,
50088c2ecf20Sopenharmony_ci				       struct station_info *sinfo)
50098c2ecf20Sopenharmony_ci{
50108c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
50118c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
50128c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
50138c2ecf20Sopenharmony_ci
50148c2ecf20Sopenharmony_ci	if (mvmsta->avg_energy) {
50158c2ecf20Sopenharmony_ci		sinfo->signal_avg = -(s8)mvmsta->avg_energy;
50168c2ecf20Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
50178c2ecf20Sopenharmony_ci	}
50188c2ecf20Sopenharmony_ci
50198c2ecf20Sopenharmony_ci	if (iwl_mvm_has_tlc_offload(mvm)) {
50208c2ecf20Sopenharmony_ci		struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
50218c2ecf20Sopenharmony_ci
50228c2ecf20Sopenharmony_ci		iwl_mvm_set_sta_rate(lq_sta->last_rate_n_flags, &sinfo->txrate);
50238c2ecf20Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
50248c2ecf20Sopenharmony_ci	}
50258c2ecf20Sopenharmony_ci
50268c2ecf20Sopenharmony_ci	/* if beacon filtering isn't on mac80211 does it anyway */
50278c2ecf20Sopenharmony_ci	if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
50288c2ecf20Sopenharmony_ci		return;
50298c2ecf20Sopenharmony_ci
50308c2ecf20Sopenharmony_ci	if (!vif->bss_conf.assoc)
50318c2ecf20Sopenharmony_ci		return;
50328c2ecf20Sopenharmony_ci
50338c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
50348c2ecf20Sopenharmony_ci
50358c2ecf20Sopenharmony_ci	if (mvmvif->ap_sta_id != mvmsta->sta_id)
50368c2ecf20Sopenharmony_ci		goto unlock;
50378c2ecf20Sopenharmony_ci
50388c2ecf20Sopenharmony_ci	if (iwl_mvm_request_statistics(mvm, false))
50398c2ecf20Sopenharmony_ci		goto unlock;
50408c2ecf20Sopenharmony_ci
50418c2ecf20Sopenharmony_ci	sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
50428c2ecf20Sopenharmony_ci			   mvmvif->beacon_stats.accu_num_beacons;
50438c2ecf20Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX);
50448c2ecf20Sopenharmony_ci	if (mvmvif->beacon_stats.avg_signal) {
50458c2ecf20Sopenharmony_ci		/* firmware only reports a value after RXing a few beacons */
50468c2ecf20Sopenharmony_ci		sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
50478c2ecf20Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
50488c2ecf20Sopenharmony_ci	}
50498c2ecf20Sopenharmony_ci unlock:
50508c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
50518c2ecf20Sopenharmony_ci}
50528c2ecf20Sopenharmony_ci
50538c2ecf20Sopenharmony_cistatic void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm,
50548c2ecf20Sopenharmony_ci					struct ieee80211_vif *vif,
50558c2ecf20Sopenharmony_ci					const struct ieee80211_event *event)
50568c2ecf20Sopenharmony_ci{
50578c2ecf20Sopenharmony_ci#define CHECK_MLME_TRIGGER(_cnt, _fmt...)				\
50588c2ecf20Sopenharmony_ci	do {								\
50598c2ecf20Sopenharmony_ci		if ((trig_mlme->_cnt) && --(trig_mlme->_cnt))		\
50608c2ecf20Sopenharmony_ci			break;						\
50618c2ecf20Sopenharmony_ci		iwl_fw_dbg_collect_trig(&(mvm)->fwrt, trig, _fmt);	\
50628c2ecf20Sopenharmony_ci	} while (0)
50638c2ecf20Sopenharmony_ci
50648c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_tlv *trig;
50658c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_mlme *trig_mlme;
50668c2ecf20Sopenharmony_ci
50678c2ecf20Sopenharmony_ci	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, ieee80211_vif_to_wdev(vif),
50688c2ecf20Sopenharmony_ci				     FW_DBG_TRIGGER_MLME);
50698c2ecf20Sopenharmony_ci	if (!trig)
50708c2ecf20Sopenharmony_ci		return;
50718c2ecf20Sopenharmony_ci
50728c2ecf20Sopenharmony_ci	trig_mlme = (void *)trig->data;
50738c2ecf20Sopenharmony_ci
50748c2ecf20Sopenharmony_ci	if (event->u.mlme.data == ASSOC_EVENT) {
50758c2ecf20Sopenharmony_ci		if (event->u.mlme.status == MLME_DENIED)
50768c2ecf20Sopenharmony_ci			CHECK_MLME_TRIGGER(stop_assoc_denied,
50778c2ecf20Sopenharmony_ci					   "DENIED ASSOC: reason %d",
50788c2ecf20Sopenharmony_ci					    event->u.mlme.reason);
50798c2ecf20Sopenharmony_ci		else if (event->u.mlme.status == MLME_TIMEOUT)
50808c2ecf20Sopenharmony_ci			CHECK_MLME_TRIGGER(stop_assoc_timeout,
50818c2ecf20Sopenharmony_ci					   "ASSOC TIMEOUT");
50828c2ecf20Sopenharmony_ci	} else if (event->u.mlme.data == AUTH_EVENT) {
50838c2ecf20Sopenharmony_ci		if (event->u.mlme.status == MLME_DENIED)
50848c2ecf20Sopenharmony_ci			CHECK_MLME_TRIGGER(stop_auth_denied,
50858c2ecf20Sopenharmony_ci					   "DENIED AUTH: reason %d",
50868c2ecf20Sopenharmony_ci					   event->u.mlme.reason);
50878c2ecf20Sopenharmony_ci		else if (event->u.mlme.status == MLME_TIMEOUT)
50888c2ecf20Sopenharmony_ci			CHECK_MLME_TRIGGER(stop_auth_timeout,
50898c2ecf20Sopenharmony_ci					   "AUTH TIMEOUT");
50908c2ecf20Sopenharmony_ci	} else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
50918c2ecf20Sopenharmony_ci		CHECK_MLME_TRIGGER(stop_rx_deauth,
50928c2ecf20Sopenharmony_ci				   "DEAUTH RX %d", event->u.mlme.reason);
50938c2ecf20Sopenharmony_ci	} else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
50948c2ecf20Sopenharmony_ci		CHECK_MLME_TRIGGER(stop_tx_deauth,
50958c2ecf20Sopenharmony_ci				   "DEAUTH TX %d", event->u.mlme.reason);
50968c2ecf20Sopenharmony_ci	}
50978c2ecf20Sopenharmony_ci#undef CHECK_MLME_TRIGGER
50988c2ecf20Sopenharmony_ci}
50998c2ecf20Sopenharmony_ci
51008c2ecf20Sopenharmony_cistatic void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
51018c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif,
51028c2ecf20Sopenharmony_ci					  const struct ieee80211_event *event)
51038c2ecf20Sopenharmony_ci{
51048c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_tlv *trig;
51058c2ecf20Sopenharmony_ci	struct iwl_fw_dbg_trigger_ba *ba_trig;
51068c2ecf20Sopenharmony_ci
51078c2ecf20Sopenharmony_ci	trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, ieee80211_vif_to_wdev(vif),
51088c2ecf20Sopenharmony_ci				     FW_DBG_TRIGGER_BA);
51098c2ecf20Sopenharmony_ci	if (!trig)
51108c2ecf20Sopenharmony_ci		return;
51118c2ecf20Sopenharmony_ci
51128c2ecf20Sopenharmony_ci	ba_trig = (void *)trig->data;
51138c2ecf20Sopenharmony_ci
51148c2ecf20Sopenharmony_ci	if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid)))
51158c2ecf20Sopenharmony_ci		return;
51168c2ecf20Sopenharmony_ci
51178c2ecf20Sopenharmony_ci	iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
51188c2ecf20Sopenharmony_ci				"BAR received from %pM, tid %d, ssn %d",
51198c2ecf20Sopenharmony_ci				event->u.ba.sta->addr, event->u.ba.tid,
51208c2ecf20Sopenharmony_ci				event->u.ba.ssn);
51218c2ecf20Sopenharmony_ci}
51228c2ecf20Sopenharmony_ci
51238c2ecf20Sopenharmony_cistatic void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
51248c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
51258c2ecf20Sopenharmony_ci				       const struct ieee80211_event *event)
51268c2ecf20Sopenharmony_ci{
51278c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
51288c2ecf20Sopenharmony_ci
51298c2ecf20Sopenharmony_ci	switch (event->type) {
51308c2ecf20Sopenharmony_ci	case MLME_EVENT:
51318c2ecf20Sopenharmony_ci		iwl_mvm_event_mlme_callback(mvm, vif, event);
51328c2ecf20Sopenharmony_ci		break;
51338c2ecf20Sopenharmony_ci	case BAR_RX_EVENT:
51348c2ecf20Sopenharmony_ci		iwl_mvm_event_bar_rx_callback(mvm, vif, event);
51358c2ecf20Sopenharmony_ci		break;
51368c2ecf20Sopenharmony_ci	case BA_FRAME_TIMEOUT:
51378c2ecf20Sopenharmony_ci		iwl_mvm_event_frame_timeout_callback(mvm, vif, event->u.ba.sta,
51388c2ecf20Sopenharmony_ci						     event->u.ba.tid);
51398c2ecf20Sopenharmony_ci		break;
51408c2ecf20Sopenharmony_ci	default:
51418c2ecf20Sopenharmony_ci		break;
51428c2ecf20Sopenharmony_ci	}
51438c2ecf20Sopenharmony_ci}
51448c2ecf20Sopenharmony_ci
51458c2ecf20Sopenharmony_civoid iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
51468c2ecf20Sopenharmony_ci				     struct iwl_mvm_internal_rxq_notif *notif,
51478c2ecf20Sopenharmony_ci				     u32 size)
51488c2ecf20Sopenharmony_ci{
51498c2ecf20Sopenharmony_ci	u32 qmask = BIT(mvm->trans->num_rx_queues) - 1;
51508c2ecf20Sopenharmony_ci	int ret;
51518c2ecf20Sopenharmony_ci
51528c2ecf20Sopenharmony_ci
51538c2ecf20Sopenharmony_ci	if (!iwl_mvm_has_new_rx_api(mvm))
51548c2ecf20Sopenharmony_ci		return;
51558c2ecf20Sopenharmony_ci
51568c2ecf20Sopenharmony_ci	if (notif->sync) {
51578c2ecf20Sopenharmony_ci		notif->cookie = mvm->queue_sync_cookie;
51588c2ecf20Sopenharmony_ci		atomic_set(&mvm->queue_sync_counter,
51598c2ecf20Sopenharmony_ci			   mvm->trans->num_rx_queues);
51608c2ecf20Sopenharmony_ci	}
51618c2ecf20Sopenharmony_ci
51628c2ecf20Sopenharmony_ci	ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif,
51638c2ecf20Sopenharmony_ci				      size, !notif->sync);
51648c2ecf20Sopenharmony_ci	if (ret) {
51658c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to trigger RX queues sync (%d)\n", ret);
51668c2ecf20Sopenharmony_ci		goto out;
51678c2ecf20Sopenharmony_ci	}
51688c2ecf20Sopenharmony_ci
51698c2ecf20Sopenharmony_ci	if (notif->sync) {
51708c2ecf20Sopenharmony_ci		lockdep_assert_held(&mvm->mutex);
51718c2ecf20Sopenharmony_ci		ret = wait_event_timeout(mvm->rx_sync_waitq,
51728c2ecf20Sopenharmony_ci					 atomic_read(&mvm->queue_sync_counter) == 0 ||
51738c2ecf20Sopenharmony_ci					 iwl_mvm_is_radio_killed(mvm),
51748c2ecf20Sopenharmony_ci					 HZ);
51758c2ecf20Sopenharmony_ci		WARN_ON_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm));
51768c2ecf20Sopenharmony_ci	}
51778c2ecf20Sopenharmony_ci
51788c2ecf20Sopenharmony_ciout:
51798c2ecf20Sopenharmony_ci	atomic_set(&mvm->queue_sync_counter, 0);
51808c2ecf20Sopenharmony_ci	if (notif->sync)
51818c2ecf20Sopenharmony_ci		mvm->queue_sync_cookie++;
51828c2ecf20Sopenharmony_ci}
51838c2ecf20Sopenharmony_ci
51848c2ecf20Sopenharmony_cistatic void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
51858c2ecf20Sopenharmony_ci{
51868c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
51878c2ecf20Sopenharmony_ci	struct iwl_mvm_internal_rxq_notif data = {
51888c2ecf20Sopenharmony_ci		.type = IWL_MVM_RXQ_EMPTY,
51898c2ecf20Sopenharmony_ci		.sync = 1,
51908c2ecf20Sopenharmony_ci	};
51918c2ecf20Sopenharmony_ci
51928c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
51938c2ecf20Sopenharmony_ci	iwl_mvm_sync_rx_queues_internal(mvm, &data, sizeof(data));
51948c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
51958c2ecf20Sopenharmony_ci}
51968c2ecf20Sopenharmony_ci
51978c2ecf20Sopenharmony_cistatic int
51988c2ecf20Sopenharmony_ciiwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,
51998c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
52008c2ecf20Sopenharmony_ci				    struct cfg80211_ftm_responder_stats *stats)
52018c2ecf20Sopenharmony_ci{
52028c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
52038c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
52048c2ecf20Sopenharmony_ci
52058c2ecf20Sopenharmony_ci	if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
52068c2ecf20Sopenharmony_ci	    !mvmvif->ap_ibss_active || !vif->bss_conf.ftm_responder)
52078c2ecf20Sopenharmony_ci		return -EINVAL;
52088c2ecf20Sopenharmony_ci
52098c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
52108c2ecf20Sopenharmony_ci	*stats = mvm->ftm_resp_stats;
52118c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
52128c2ecf20Sopenharmony_ci
52138c2ecf20Sopenharmony_ci	stats->filled = BIT(NL80211_FTM_STATS_SUCCESS_NUM) |
52148c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_PARTIAL_NUM) |
52158c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_FAILED_NUM) |
52168c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_ASAP_NUM) |
52178c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_NON_ASAP_NUM) |
52188c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_TOTAL_DURATION_MSEC) |
52198c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM) |
52208c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM) |
52218c2ecf20Sopenharmony_ci			BIT(NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM);
52228c2ecf20Sopenharmony_ci
52238c2ecf20Sopenharmony_ci	return 0;
52248c2ecf20Sopenharmony_ci}
52258c2ecf20Sopenharmony_ci
52268c2ecf20Sopenharmony_cistatic int iwl_mvm_start_pmsr(struct ieee80211_hw *hw,
52278c2ecf20Sopenharmony_ci			      struct ieee80211_vif *vif,
52288c2ecf20Sopenharmony_ci			      struct cfg80211_pmsr_request *request)
52298c2ecf20Sopenharmony_ci{
52308c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
52318c2ecf20Sopenharmony_ci	int ret;
52328c2ecf20Sopenharmony_ci
52338c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
52348c2ecf20Sopenharmony_ci	ret = iwl_mvm_ftm_start(mvm, vif, request);
52358c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
52368c2ecf20Sopenharmony_ci
52378c2ecf20Sopenharmony_ci	return ret;
52388c2ecf20Sopenharmony_ci}
52398c2ecf20Sopenharmony_ci
52408c2ecf20Sopenharmony_cistatic void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw,
52418c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif,
52428c2ecf20Sopenharmony_ci			       struct cfg80211_pmsr_request *request)
52438c2ecf20Sopenharmony_ci{
52448c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
52458c2ecf20Sopenharmony_ci
52468c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
52478c2ecf20Sopenharmony_ci	iwl_mvm_ftm_abort(mvm, request);
52488c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
52498c2ecf20Sopenharmony_ci}
52508c2ecf20Sopenharmony_ci
52518c2ecf20Sopenharmony_cistatic bool iwl_mvm_can_hw_csum(struct sk_buff *skb)
52528c2ecf20Sopenharmony_ci{
52538c2ecf20Sopenharmony_ci	u8 protocol = ip_hdr(skb)->protocol;
52548c2ecf20Sopenharmony_ci
52558c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_INET))
52568c2ecf20Sopenharmony_ci		return false;
52578c2ecf20Sopenharmony_ci
52588c2ecf20Sopenharmony_ci	return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP;
52598c2ecf20Sopenharmony_ci}
52608c2ecf20Sopenharmony_ci
52618c2ecf20Sopenharmony_cistatic bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,
52628c2ecf20Sopenharmony_ci				      struct sk_buff *head,
52638c2ecf20Sopenharmony_ci				      struct sk_buff *skb)
52648c2ecf20Sopenharmony_ci{
52658c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
52668c2ecf20Sopenharmony_ci
52678c2ecf20Sopenharmony_ci	/* For now don't aggregate IPv6 in AMSDU */
52688c2ecf20Sopenharmony_ci	if (skb->protocol != htons(ETH_P_IP))
52698c2ecf20Sopenharmony_ci		return false;
52708c2ecf20Sopenharmony_ci
52718c2ecf20Sopenharmony_ci	if (!iwl_mvm_is_csum_supported(mvm))
52728c2ecf20Sopenharmony_ci		return true;
52738c2ecf20Sopenharmony_ci
52748c2ecf20Sopenharmony_ci	return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head);
52758c2ecf20Sopenharmony_ci}
52768c2ecf20Sopenharmony_ci
52778c2ecf20Sopenharmony_ciconst struct ieee80211_ops iwl_mvm_hw_ops = {
52788c2ecf20Sopenharmony_ci	.tx = iwl_mvm_mac_tx,
52798c2ecf20Sopenharmony_ci	.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
52808c2ecf20Sopenharmony_ci	.ampdu_action = iwl_mvm_mac_ampdu_action,
52818c2ecf20Sopenharmony_ci	.get_antenna = iwl_mvm_op_get_antenna,
52828c2ecf20Sopenharmony_ci	.start = iwl_mvm_mac_start,
52838c2ecf20Sopenharmony_ci	.reconfig_complete = iwl_mvm_mac_reconfig_complete,
52848c2ecf20Sopenharmony_ci	.stop = iwl_mvm_mac_stop,
52858c2ecf20Sopenharmony_ci	.add_interface = iwl_mvm_mac_add_interface,
52868c2ecf20Sopenharmony_ci	.remove_interface = iwl_mvm_mac_remove_interface,
52878c2ecf20Sopenharmony_ci	.config = iwl_mvm_mac_config,
52888c2ecf20Sopenharmony_ci	.prepare_multicast = iwl_mvm_prepare_multicast,
52898c2ecf20Sopenharmony_ci	.configure_filter = iwl_mvm_configure_filter,
52908c2ecf20Sopenharmony_ci	.config_iface_filter = iwl_mvm_config_iface_filter,
52918c2ecf20Sopenharmony_ci	.bss_info_changed = iwl_mvm_bss_info_changed,
52928c2ecf20Sopenharmony_ci	.hw_scan = iwl_mvm_mac_hw_scan,
52938c2ecf20Sopenharmony_ci	.cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
52948c2ecf20Sopenharmony_ci	.sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,
52958c2ecf20Sopenharmony_ci	.sta_state = iwl_mvm_mac_sta_state,
52968c2ecf20Sopenharmony_ci	.sta_notify = iwl_mvm_mac_sta_notify,
52978c2ecf20Sopenharmony_ci	.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
52988c2ecf20Sopenharmony_ci	.release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
52998c2ecf20Sopenharmony_ci	.set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
53008c2ecf20Sopenharmony_ci	.sta_rc_update = iwl_mvm_sta_rc_update,
53018c2ecf20Sopenharmony_ci	.conf_tx = iwl_mvm_mac_conf_tx,
53028c2ecf20Sopenharmony_ci	.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
53038c2ecf20Sopenharmony_ci	.mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
53048c2ecf20Sopenharmony_ci	.flush = iwl_mvm_mac_flush,
53058c2ecf20Sopenharmony_ci	.sched_scan_start = iwl_mvm_mac_sched_scan_start,
53068c2ecf20Sopenharmony_ci	.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
53078c2ecf20Sopenharmony_ci	.set_key = iwl_mvm_mac_set_key,
53088c2ecf20Sopenharmony_ci	.update_tkip_key = iwl_mvm_mac_update_tkip_key,
53098c2ecf20Sopenharmony_ci	.remain_on_channel = iwl_mvm_roc,
53108c2ecf20Sopenharmony_ci	.cancel_remain_on_channel = iwl_mvm_cancel_roc,
53118c2ecf20Sopenharmony_ci	.add_chanctx = iwl_mvm_add_chanctx,
53128c2ecf20Sopenharmony_ci	.remove_chanctx = iwl_mvm_remove_chanctx,
53138c2ecf20Sopenharmony_ci	.change_chanctx = iwl_mvm_change_chanctx,
53148c2ecf20Sopenharmony_ci	.assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
53158c2ecf20Sopenharmony_ci	.unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
53168c2ecf20Sopenharmony_ci	.switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
53178c2ecf20Sopenharmony_ci
53188c2ecf20Sopenharmony_ci	.start_ap = iwl_mvm_start_ap_ibss,
53198c2ecf20Sopenharmony_ci	.stop_ap = iwl_mvm_stop_ap_ibss,
53208c2ecf20Sopenharmony_ci	.join_ibss = iwl_mvm_start_ap_ibss,
53218c2ecf20Sopenharmony_ci	.leave_ibss = iwl_mvm_stop_ap_ibss,
53228c2ecf20Sopenharmony_ci
53238c2ecf20Sopenharmony_ci	.tx_last_beacon = iwl_mvm_tx_last_beacon,
53248c2ecf20Sopenharmony_ci
53258c2ecf20Sopenharmony_ci	.set_tim = iwl_mvm_set_tim,
53268c2ecf20Sopenharmony_ci
53278c2ecf20Sopenharmony_ci	.channel_switch = iwl_mvm_channel_switch,
53288c2ecf20Sopenharmony_ci	.pre_channel_switch = iwl_mvm_pre_channel_switch,
53298c2ecf20Sopenharmony_ci	.post_channel_switch = iwl_mvm_post_channel_switch,
53308c2ecf20Sopenharmony_ci	.abort_channel_switch = iwl_mvm_abort_channel_switch,
53318c2ecf20Sopenharmony_ci	.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,
53328c2ecf20Sopenharmony_ci
53338c2ecf20Sopenharmony_ci	.tdls_channel_switch = iwl_mvm_tdls_channel_switch,
53348c2ecf20Sopenharmony_ci	.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
53358c2ecf20Sopenharmony_ci	.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
53368c2ecf20Sopenharmony_ci
53378c2ecf20Sopenharmony_ci	.event_callback = iwl_mvm_mac_event_callback,
53388c2ecf20Sopenharmony_ci
53398c2ecf20Sopenharmony_ci	.sync_rx_queues = iwl_mvm_sync_rx_queues,
53408c2ecf20Sopenharmony_ci
53418c2ecf20Sopenharmony_ci	CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
53428c2ecf20Sopenharmony_ci
53438c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
53448c2ecf20Sopenharmony_ci	/* look at d3.c */
53458c2ecf20Sopenharmony_ci	.suspend = iwl_mvm_suspend,
53468c2ecf20Sopenharmony_ci	.resume = iwl_mvm_resume,
53478c2ecf20Sopenharmony_ci	.set_wakeup = iwl_mvm_set_wakeup,
53488c2ecf20Sopenharmony_ci	.set_rekey_data = iwl_mvm_set_rekey_data,
53498c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
53508c2ecf20Sopenharmony_ci	.ipv6_addr_change = iwl_mvm_ipv6_addr_change,
53518c2ecf20Sopenharmony_ci#endif
53528c2ecf20Sopenharmony_ci	.set_default_unicast_key = iwl_mvm_set_default_unicast_key,
53538c2ecf20Sopenharmony_ci#endif
53548c2ecf20Sopenharmony_ci	.get_survey = iwl_mvm_mac_get_survey,
53558c2ecf20Sopenharmony_ci	.sta_statistics = iwl_mvm_mac_sta_statistics,
53568c2ecf20Sopenharmony_ci	.get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats,
53578c2ecf20Sopenharmony_ci	.start_pmsr = iwl_mvm_start_pmsr,
53588c2ecf20Sopenharmony_ci	.abort_pmsr = iwl_mvm_abort_pmsr,
53598c2ecf20Sopenharmony_ci
53608c2ecf20Sopenharmony_ci	.can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate,
53618c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
53628c2ecf20Sopenharmony_ci	.sta_add_debugfs = iwl_mvm_sta_add_debugfs,
53638c2ecf20Sopenharmony_ci#endif
53648c2ecf20Sopenharmony_ci};
5365