18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/******************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Contact Information:
78c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
88c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *****************************************************************************/
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <net/mac80211.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "iwl-io.h"
188c2ecf20Sopenharmony_ci#include "iwl-agn-hw.h"
198c2ecf20Sopenharmony_ci#include "iwl-trans.h"
208c2ecf20Sopenharmony_ci#include "iwl-modparams.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "dev.h"
238c2ecf20Sopenharmony_ci#include "agn.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciint iwlagn_hw_valid_rtc_data_addr(u32 addr)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) &&
288c2ecf20Sopenharmony_ci		(addr < IWLAGN_RTC_DATA_UPPER_BOUND);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciint iwlagn_send_tx_power(struct iwl_priv *priv)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct iwlagn_tx_power_dbm_cmd tx_power_cmd;
348c2ecf20Sopenharmony_ci	u8 tx_ant_cfg_cmd;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status),
378c2ecf20Sopenharmony_ci		      "TX Power requested while scanning!\n"))
388c2ecf20Sopenharmony_ci		return -EAGAIN;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* half dBm need to multiply */
418c2ecf20Sopenharmony_ci	tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) {
448c2ecf20Sopenharmony_ci		/*
458c2ecf20Sopenharmony_ci		 * For the newer devices which using enhanced/extend tx power
468c2ecf20Sopenharmony_ci		 * table in EEPROM, the format is in half dBm. driver need to
478c2ecf20Sopenharmony_ci		 * convert to dBm format before report to mac80211.
488c2ecf20Sopenharmony_ci		 * By doing so, there is a possibility of 1/2 dBm resolution
498c2ecf20Sopenharmony_ci		 * lost. driver will perform "round-up" operation before
508c2ecf20Sopenharmony_ci		 * reporting, but it will cause 1/2 dBm tx power over the
518c2ecf20Sopenharmony_ci		 * regulatory limit. Perform the checking here, if the
528c2ecf20Sopenharmony_ci		 * "tx_power_user_lmt" is higher than EEPROM value (in
538c2ecf20Sopenharmony_ci		 * half-dBm format), lower the tx power based on EEPROM
548c2ecf20Sopenharmony_ci		 */
558c2ecf20Sopenharmony_ci		tx_power_cmd.global_lmt =
568c2ecf20Sopenharmony_ci			priv->nvm_data->max_tx_pwr_half_dbm;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED;
598c2ecf20Sopenharmony_ci	tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (IWL_UCODE_API(priv->fw->ucode_ver) == 1)
628c2ecf20Sopenharmony_ci		tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1;
638c2ecf20Sopenharmony_ci	else
648c2ecf20Sopenharmony_ci		tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0,
678c2ecf20Sopenharmony_ci			sizeof(tx_power_cmd), &tx_power_cmd);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid iwlagn_temperature(struct iwl_priv *priv)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	lockdep_assert_held(&priv->statistics.lock);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* store temperature from correct statistics (in Celsius) */
758c2ecf20Sopenharmony_ci	priv->temperature = le32_to_cpu(priv->statistics.common.temperature);
768c2ecf20Sopenharmony_ci	iwl_tt_handler(priv);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int idx = 0;
828c2ecf20Sopenharmony_ci	int band_offset = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* HT rate format: mac80211 wants an MCS number, which is just LSB */
858c2ecf20Sopenharmony_ci	if (rate_n_flags & RATE_MCS_HT_MSK) {
868c2ecf20Sopenharmony_ci		idx = (rate_n_flags & 0xff);
878c2ecf20Sopenharmony_ci		return idx;
888c2ecf20Sopenharmony_ci	/* Legacy rate format, search for match in table */
898c2ecf20Sopenharmony_ci	} else {
908c2ecf20Sopenharmony_ci		if (band == NL80211_BAND_5GHZ)
918c2ecf20Sopenharmony_ci			band_offset = IWL_FIRST_OFDM_RATE;
928c2ecf20Sopenharmony_ci		for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
938c2ecf20Sopenharmony_ci			if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF))
948c2ecf20Sopenharmony_ci				return idx - band_offset;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return -1;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciint iwlagn_manage_ibss_station(struct iwl_priv *priv,
1018c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif, bool add)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (add)
1068c2ecf20Sopenharmony_ci		return iwlagn_add_bssid_station(priv, vif_priv->ctx,
1078c2ecf20Sopenharmony_ci						vif->bss_conf.bssid,
1088c2ecf20Sopenharmony_ci						&vif_priv->ibss_bssid_sta_id);
1098c2ecf20Sopenharmony_ci	return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id,
1108c2ecf20Sopenharmony_ci				  vif->bss_conf.bssid);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci * pre-requirements:
1178c2ecf20Sopenharmony_ci *  1. acquire mutex before calling
1188c2ecf20Sopenharmony_ci *  2. make sure rf is on and not in exit state
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ciint iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
1238c2ecf20Sopenharmony_ci		.flush_control = cpu_to_le16(IWL_DROP_ALL),
1248c2ecf20Sopenharmony_ci	};
1258c2ecf20Sopenharmony_ci	struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
1268c2ecf20Sopenharmony_ci		.flush_control = cpu_to_le16(IWL_DROP_ALL),
1278c2ecf20Sopenharmony_ci	};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
1308c2ecf20Sopenharmony_ci			    IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
1338c2ecf20Sopenharmony_ci		queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
1348c2ecf20Sopenharmony_ci				 IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
1358c2ecf20Sopenharmony_ci				 IWL_PAN_SCD_MGMT_MSK |
1368c2ecf20Sopenharmony_ci				 IWL_PAN_SCD_MULTICAST_MSK;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (priv->nvm_data->sku_cap_11n_enable)
1398c2ecf20Sopenharmony_ci		queue_control |= IWL_AGG_TX_QUEUE_MSK;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (scd_q_msk)
1428c2ecf20Sopenharmony_ci		queue_control = scd_q_msk;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
1458c2ecf20Sopenharmony_ci	flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
1468c2ecf20Sopenharmony_ci	flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
1498c2ecf20Sopenharmony_ci		return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
1508c2ecf20Sopenharmony_ci					    sizeof(flush_cmd_v3),
1518c2ecf20Sopenharmony_ci					    &flush_cmd_v3);
1528c2ecf20Sopenharmony_ci	return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
1538c2ecf20Sopenharmony_ci				    sizeof(flush_cmd_v2), &flush_cmd_v2);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_civoid iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	mutex_lock(&priv->mutex);
1598c2ecf20Sopenharmony_ci	ieee80211_stop_queues(priv->hw);
1608c2ecf20Sopenharmony_ci	if (iwlagn_txfifo_flush(priv, 0)) {
1618c2ecf20Sopenharmony_ci		IWL_ERR(priv, "flush request fail\n");
1628c2ecf20Sopenharmony_ci		goto done;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
1658c2ecf20Sopenharmony_ci	iwl_trans_wait_tx_queues_empty(priv->trans, 0xffffffff);
1668c2ecf20Sopenharmony_cidone:
1678c2ecf20Sopenharmony_ci	ieee80211_wake_queues(priv->hw);
1688c2ecf20Sopenharmony_ci	mutex_unlock(&priv->mutex);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * BT coex
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_ci/* Notmal TDM */
1758c2ecf20Sopenharmony_cistatic const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
1768c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1778c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1788c2ecf20Sopenharmony_ci	cpu_to_le32(0xaeaaaaaa),
1798c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1808c2ecf20Sopenharmony_ci	cpu_to_le32(0xcc00ff28),
1818c2ecf20Sopenharmony_ci	cpu_to_le32(0x0000aaaa),
1828c2ecf20Sopenharmony_ci	cpu_to_le32(0xcc00aaaa),
1838c2ecf20Sopenharmony_ci	cpu_to_le32(0x0000aaaa),
1848c2ecf20Sopenharmony_ci	cpu_to_le32(0xc0004000),
1858c2ecf20Sopenharmony_ci	cpu_to_le32(0x00004000),
1868c2ecf20Sopenharmony_ci	cpu_to_le32(0xf0005000),
1878c2ecf20Sopenharmony_ci	cpu_to_le32(0xf0005000),
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/* Full concurrency */
1918c2ecf20Sopenharmony_cistatic const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = {
1928c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1938c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1948c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1958c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1968c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1978c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1988c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
1998c2ecf20Sopenharmony_ci	cpu_to_le32(0xaaaaaaaa),
2008c2ecf20Sopenharmony_ci	cpu_to_le32(0x00000000),
2018c2ecf20Sopenharmony_ci	cpu_to_le32(0x00000000),
2028c2ecf20Sopenharmony_ci	cpu_to_le32(0x00000000),
2038c2ecf20Sopenharmony_ci	cpu_to_le32(0x00000000),
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid iwlagn_send_advance_bt_config(struct iwl_priv *priv)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct iwl_basic_bt_cmd basic = {
2098c2ecf20Sopenharmony_ci		.max_kill = IWLAGN_BT_MAX_KILL_DEFAULT,
2108c2ecf20Sopenharmony_ci		.bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT,
2118c2ecf20Sopenharmony_ci		.bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT,
2128c2ecf20Sopenharmony_ci		.bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT,
2138c2ecf20Sopenharmony_ci	};
2148c2ecf20Sopenharmony_ci	struct iwl_bt_cmd_v1 bt_cmd_v1;
2158c2ecf20Sopenharmony_ci	struct iwl_bt_cmd_v2 bt_cmd_v2;
2168c2ecf20Sopenharmony_ci	int ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
2198c2ecf20Sopenharmony_ci			sizeof(basic.bt3_lookup_table));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (priv->lib->bt_params) {
2228c2ecf20Sopenharmony_ci		/*
2238c2ecf20Sopenharmony_ci		 * newer generation of devices (2000 series and newer)
2248c2ecf20Sopenharmony_ci		 * use the version 2 of the bt command
2258c2ecf20Sopenharmony_ci		 * we need to make sure sending the host command
2268c2ecf20Sopenharmony_ci		 * with correct data structure to avoid uCode assert
2278c2ecf20Sopenharmony_ci		 */
2288c2ecf20Sopenharmony_ci		if (priv->lib->bt_params->bt_session_2) {
2298c2ecf20Sopenharmony_ci			bt_cmd_v2.prio_boost = cpu_to_le32(
2308c2ecf20Sopenharmony_ci				priv->lib->bt_params->bt_prio_boost);
2318c2ecf20Sopenharmony_ci			bt_cmd_v2.tx_prio_boost = 0;
2328c2ecf20Sopenharmony_ci			bt_cmd_v2.rx_prio_boost = 0;
2338c2ecf20Sopenharmony_ci		} else {
2348c2ecf20Sopenharmony_ci			/* older version only has 8 bits */
2358c2ecf20Sopenharmony_ci			WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
2368c2ecf20Sopenharmony_ci			bt_cmd_v1.prio_boost =
2378c2ecf20Sopenharmony_ci				priv->lib->bt_params->bt_prio_boost;
2388c2ecf20Sopenharmony_ci			bt_cmd_v1.tx_prio_boost = 0;
2398c2ecf20Sopenharmony_ci			bt_cmd_v1.rx_prio_boost = 0;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	} else {
2428c2ecf20Sopenharmony_ci		IWL_ERR(priv, "failed to construct BT Coex Config\n");
2438c2ecf20Sopenharmony_ci		return;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * Possible situations when BT needs to take over for receive,
2488c2ecf20Sopenharmony_ci	 * at the same time where STA needs to response to AP's frame(s),
2498c2ecf20Sopenharmony_ci	 * reduce the tx power of the required response frames, by that,
2508c2ecf20Sopenharmony_ci	 * allow the concurrent BT receive & WiFi transmit
2518c2ecf20Sopenharmony_ci	 * (BT - ANT A, WiFi -ANT B), without interference to one another
2528c2ecf20Sopenharmony_ci	 *
2538c2ecf20Sopenharmony_ci	 * Reduced tx power apply to control frames only (ACK/Back/CTS)
2548c2ecf20Sopenharmony_ci	 * when indicated by the BT config command
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	basic.kill_ack_mask = priv->kill_ack_mask;
2578c2ecf20Sopenharmony_ci	basic.kill_cts_mask = priv->kill_cts_mask;
2588c2ecf20Sopenharmony_ci	if (priv->reduced_txpower)
2598c2ecf20Sopenharmony_ci		basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR;
2608c2ecf20Sopenharmony_ci	basic.valid = priv->bt_valid;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * Configure BT coex mode to "no coexistence" when the
2648c2ecf20Sopenharmony_ci	 * user disabled BT coexistence, we have no interface
2658c2ecf20Sopenharmony_ci	 * (might be in monitor mode), or the interface is in
2668c2ecf20Sopenharmony_ci	 * IBSS mode (no proper uCode support for coex then).
2678c2ecf20Sopenharmony_ci	 */
2688c2ecf20Sopenharmony_ci	if (!iwlwifi_mod_params.bt_coex_active ||
2698c2ecf20Sopenharmony_ci	    priv->iw_mode == NL80211_IFTYPE_ADHOC) {
2708c2ecf20Sopenharmony_ci		basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED;
2718c2ecf20Sopenharmony_ci	} else {
2728c2ecf20Sopenharmony_ci		basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W <<
2738c2ecf20Sopenharmony_ci					IWLAGN_BT_FLAG_COEX_MODE_SHIFT;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (!priv->bt_enable_pspoll)
2768c2ecf20Sopenharmony_ci			basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
2778c2ecf20Sopenharmony_ci		else
2788c2ecf20Sopenharmony_ci			basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (priv->bt_ch_announce)
2818c2ecf20Sopenharmony_ci			basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION;
2828c2ecf20Sopenharmony_ci		IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	priv->bt_enable_flag = basic.flags;
2858c2ecf20Sopenharmony_ci	if (priv->bt_full_concurrent)
2868c2ecf20Sopenharmony_ci		memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup,
2878c2ecf20Sopenharmony_ci			sizeof(iwlagn_concurrent_lookup));
2888c2ecf20Sopenharmony_ci	else
2898c2ecf20Sopenharmony_ci		memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup,
2908c2ecf20Sopenharmony_ci			sizeof(iwlagn_def_3w_lookup));
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n",
2938c2ecf20Sopenharmony_ci		       basic.flags ? "active" : "disabled",
2948c2ecf20Sopenharmony_ci		       priv->bt_full_concurrent ?
2958c2ecf20Sopenharmony_ci		       "full concurrency" : "3-wire");
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (priv->lib->bt_params->bt_session_2) {
2988c2ecf20Sopenharmony_ci		memcpy(&bt_cmd_v2.basic, &basic,
2998c2ecf20Sopenharmony_ci			sizeof(basic));
3008c2ecf20Sopenharmony_ci		ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
3018c2ecf20Sopenharmony_ci			0, sizeof(bt_cmd_v2), &bt_cmd_v2);
3028c2ecf20Sopenharmony_ci	} else {
3038c2ecf20Sopenharmony_ci		memcpy(&bt_cmd_v1.basic, &basic,
3048c2ecf20Sopenharmony_ci			sizeof(basic));
3058c2ecf20Sopenharmony_ci		ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
3068c2ecf20Sopenharmony_ci			0, sizeof(bt_cmd_v1), &bt_cmd_v1);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	if (ret)
3098c2ecf20Sopenharmony_ci		IWL_ERR(priv, "failed to send BT Coex Config\n");
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_civoid iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx, *found_ctx = NULL;
3168c2ecf20Sopenharmony_ci	bool found_ap = false;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Check whether AP or GO mode is active. */
3218c2ecf20Sopenharmony_ci	if (rssi_ena) {
3228c2ecf20Sopenharmony_ci		for_each_context(priv, ctx) {
3238c2ecf20Sopenharmony_ci			if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP &&
3248c2ecf20Sopenharmony_ci			    iwl_is_associated_ctx(ctx)) {
3258c2ecf20Sopenharmony_ci				found_ap = true;
3268c2ecf20Sopenharmony_ci				break;
3278c2ecf20Sopenharmony_ci			}
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*
3328c2ecf20Sopenharmony_ci	 * If disable was received or If GO/AP mode, disable RSSI
3338c2ecf20Sopenharmony_ci	 * measurements.
3348c2ecf20Sopenharmony_ci	 */
3358c2ecf20Sopenharmony_ci	if (!rssi_ena || found_ap) {
3368c2ecf20Sopenharmony_ci		if (priv->cur_rssi_ctx) {
3378c2ecf20Sopenharmony_ci			ctx = priv->cur_rssi_ctx;
3388c2ecf20Sopenharmony_ci			ieee80211_disable_rssi_reports(ctx->vif);
3398c2ecf20Sopenharmony_ci			priv->cur_rssi_ctx = NULL;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci		return;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 * If rssi measurements need to be enabled, consider all cases now.
3468c2ecf20Sopenharmony_ci	 * Figure out how many contexts are active.
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	for_each_context(priv, ctx) {
3498c2ecf20Sopenharmony_ci		if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION &&
3508c2ecf20Sopenharmony_ci		    iwl_is_associated_ctx(ctx)) {
3518c2ecf20Sopenharmony_ci			found_ctx = ctx;
3528c2ecf20Sopenharmony_ci			break;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/*
3578c2ecf20Sopenharmony_ci	 * rssi monitor already enabled for the correct interface...nothing
3588c2ecf20Sopenharmony_ci	 * to do.
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	if (found_ctx == priv->cur_rssi_ctx)
3618c2ecf20Sopenharmony_ci		return;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/*
3648c2ecf20Sopenharmony_ci	 * Figure out if rssi monitor is currently enabled, and needs
3658c2ecf20Sopenharmony_ci	 * to be changed. If rssi monitor is already enabled, disable
3668c2ecf20Sopenharmony_ci	 * it first else just enable rssi measurements on the
3678c2ecf20Sopenharmony_ci	 * interface found above.
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	if (priv->cur_rssi_ctx) {
3708c2ecf20Sopenharmony_ci		ctx = priv->cur_rssi_ctx;
3718c2ecf20Sopenharmony_ci		if (ctx->vif)
3728c2ecf20Sopenharmony_ci			ieee80211_disable_rssi_reports(ctx->vif);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	priv->cur_rssi_ctx = found_ctx;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (!found_ctx)
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ieee80211_enable_rssi_reports(found_ctx->vif,
3818c2ecf20Sopenharmony_ci			IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD,
3828c2ecf20Sopenharmony_ci			IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
3888c2ecf20Sopenharmony_ci		BT_UART_MSG_FRAME3SCOESCO_POS;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void iwlagn_bt_traffic_change_work(struct work_struct *work)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct iwl_priv *priv =
3948c2ecf20Sopenharmony_ci		container_of(work, struct iwl_priv, bt_traffic_change_work);
3958c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx;
3968c2ecf20Sopenharmony_ci	int smps_request = -1;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
3998c2ecf20Sopenharmony_ci		/* bt coex disabled */
4008c2ecf20Sopenharmony_ci		return;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/*
4048c2ecf20Sopenharmony_ci	 * Note: bt_traffic_load can be overridden by scan complete and
4058c2ecf20Sopenharmony_ci	 * coex profile notifications. Ignore that since only bad consequence
4068c2ecf20Sopenharmony_ci	 * can be not matching debug print with actual state.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n",
4098c2ecf20Sopenharmony_ci		       priv->bt_traffic_load);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	switch (priv->bt_traffic_load) {
4128c2ecf20Sopenharmony_ci	case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
4138c2ecf20Sopenharmony_ci		if (priv->bt_status)
4148c2ecf20Sopenharmony_ci			smps_request = IEEE80211_SMPS_DYNAMIC;
4158c2ecf20Sopenharmony_ci		else
4168c2ecf20Sopenharmony_ci			smps_request = IEEE80211_SMPS_AUTOMATIC;
4178c2ecf20Sopenharmony_ci		break;
4188c2ecf20Sopenharmony_ci	case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
4198c2ecf20Sopenharmony_ci		smps_request = IEEE80211_SMPS_DYNAMIC;
4208c2ecf20Sopenharmony_ci		break;
4218c2ecf20Sopenharmony_ci	case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
4228c2ecf20Sopenharmony_ci	case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
4238c2ecf20Sopenharmony_ci		smps_request = IEEE80211_SMPS_STATIC;
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	default:
4268c2ecf20Sopenharmony_ci		IWL_ERR(priv, "Invalid BT traffic load: %d\n",
4278c2ecf20Sopenharmony_ci			priv->bt_traffic_load);
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	mutex_lock(&priv->mutex);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/*
4348c2ecf20Sopenharmony_ci	 * We can not send command to firmware while scanning. When the scan
4358c2ecf20Sopenharmony_ci	 * complete we will schedule this work again. We do check with mutex
4368c2ecf20Sopenharmony_ci	 * locked to prevent new scan request to arrive. We do not check
4378c2ecf20Sopenharmony_ci	 * STATUS_SCANNING to avoid race when queue_work two times from
4388c2ecf20Sopenharmony_ci	 * different notifications, but quit and not perform any work at all.
4398c2ecf20Sopenharmony_ci	 */
4408c2ecf20Sopenharmony_ci	if (test_bit(STATUS_SCAN_HW, &priv->status))
4418c2ecf20Sopenharmony_ci		goto out;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	iwl_update_chain_flags(priv);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (smps_request != -1) {
4468c2ecf20Sopenharmony_ci		priv->current_ht_config.smps = smps_request;
4478c2ecf20Sopenharmony_ci		for_each_context(priv, ctx) {
4488c2ecf20Sopenharmony_ci			if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION)
4498c2ecf20Sopenharmony_ci				ieee80211_request_smps(ctx->vif, smps_request);
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/*
4548c2ecf20Sopenharmony_ci	 * Dynamic PS poll related functionality. Adjust RSSI measurements if
4558c2ecf20Sopenharmony_ci	 * necessary.
4568c2ecf20Sopenharmony_ci	 */
4578c2ecf20Sopenharmony_ci	iwlagn_bt_coex_rssi_monitor(priv);
4588c2ecf20Sopenharmony_ciout:
4598c2ecf20Sopenharmony_ci	mutex_unlock(&priv->mutex);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/*
4638c2ecf20Sopenharmony_ci * If BT sco traffic, and RSSI monitor is enabled, move measurements to the
4648c2ecf20Sopenharmony_ci * correct interface or disable it if this is the last interface to be
4658c2ecf20Sopenharmony_ci * removed.
4668c2ecf20Sopenharmony_ci */
4678c2ecf20Sopenharmony_civoid iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	if (priv->bt_is_sco &&
4708c2ecf20Sopenharmony_ci	    priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS)
4718c2ecf20Sopenharmony_ci		iwlagn_bt_adjust_rssi_monitor(priv, true);
4728c2ecf20Sopenharmony_ci	else
4738c2ecf20Sopenharmony_ci		iwlagn_bt_adjust_rssi_monitor(priv, false);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void iwlagn_print_uartmsg(struct iwl_priv *priv,
4778c2ecf20Sopenharmony_ci				struct iwl_bt_uart_msg *uart_msg)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, "
4808c2ecf20Sopenharmony_ci			"Update Req = 0x%X\n",
4818c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
4828c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME1MSGTYPE_POS,
4838c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >>
4848c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME1SSN_POS,
4858c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >>
4868c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME1UPDATEREQ_POS);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
4898c2ecf20Sopenharmony_ci			"Chl_SeqN = 0x%X, In band = 0x%X\n",
4908c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
4918c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
4928c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >>
4938c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME2TRAFFICLOAD_POS,
4948c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >>
4958c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME2CHLSEQN_POS,
4968c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >>
4978c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME2INBAND_POS);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
5008c2ecf20Sopenharmony_ci			"ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n",
5018c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
5028c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3SCOESCO_POS,
5038c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >>
5048c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3SNIFF_POS,
5058c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >>
5068c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3A2DP_POS,
5078c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >>
5088c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3ACL_POS,
5098c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >>
5108c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3MASTER_POS,
5118c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
5128c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME3OBEX_POS);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n",
5158c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
5168c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME4IDLEDURATION_POS);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
5198c2ecf20Sopenharmony_ci			"eSCO Retransmissions = 0x%X\n",
5208c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
5218c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME5TXACTIVITY_POS,
5228c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >>
5238c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME5RXACTIVITY_POS,
5248c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
5258c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n",
5288c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
5298c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
5308c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
5318c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME6DISCOVERABLE_POS);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = "
5348c2ecf20Sopenharmony_ci			"0x%X, Inquiry = 0x%X, Connectable = 0x%X\n",
5358c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
5368c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
5378c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >>
5388c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME7PAGE_POS,
5398c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >>
5408c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME7INQUIRY_POS,
5418c2ecf20Sopenharmony_ci		(BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >>
5428c2ecf20Sopenharmony_ci			BT_UART_MSG_FRAME7CONNECTABLE_POS);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic bool iwlagn_set_kill_msk(struct iwl_priv *priv,
5468c2ecf20Sopenharmony_ci				struct iwl_bt_uart_msg *uart_msg)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	bool need_update = false;
5498c2ecf20Sopenharmony_ci	u8 kill_msk = IWL_BT_KILL_REDUCE;
5508c2ecf20Sopenharmony_ci	static const __le32 bt_kill_ack_msg[3] = {
5518c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_ACK_MASK_DEFAULT,
5528c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
5538c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
5548c2ecf20Sopenharmony_ci	static const __le32 bt_kill_cts_msg[3] = {
5558c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_CTS_MASK_DEFAULT,
5568c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_ACK_CTS_MASK_SCO,
5578c2ecf20Sopenharmony_ci		IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE};
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (!priv->reduced_txpower)
5608c2ecf20Sopenharmony_ci		kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3)
5618c2ecf20Sopenharmony_ci			? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT;
5628c2ecf20Sopenharmony_ci	if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] ||
5638c2ecf20Sopenharmony_ci	    priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) {
5648c2ecf20Sopenharmony_ci		priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK;
5658c2ecf20Sopenharmony_ci		priv->kill_ack_mask = bt_kill_ack_msg[kill_msk];
5668c2ecf20Sopenharmony_ci		priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK;
5678c2ecf20Sopenharmony_ci		priv->kill_cts_mask = bt_kill_cts_msg[kill_msk];
5688c2ecf20Sopenharmony_ci		need_update = true;
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci	return need_update;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/*
5748c2ecf20Sopenharmony_ci * Upon RSSI changes, sends a bt config command with following changes
5758c2ecf20Sopenharmony_ci *  1. enable/disable "reduced control frames tx power
5768c2ecf20Sopenharmony_ci *  2. update the "kill)ack_mask" and "kill_cts_mask"
5778c2ecf20Sopenharmony_ci *
5788c2ecf20Sopenharmony_ci * If "reduced tx power" is enabled, uCode shall
5798c2ecf20Sopenharmony_ci *  1. ACK/Back/CTS rate shall reduced to 6Mbps
5808c2ecf20Sopenharmony_ci *  2. not use duplciate 20/40MHz mode
5818c2ecf20Sopenharmony_ci */
5828c2ecf20Sopenharmony_cistatic bool iwlagn_fill_txpower_mode(struct iwl_priv *priv,
5838c2ecf20Sopenharmony_ci				struct iwl_bt_uart_msg *uart_msg)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	bool need_update = false;
5868c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
5878c2ecf20Sopenharmony_ci	int ave_rssi;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) {
5908c2ecf20Sopenharmony_ci		IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n");
5918c2ecf20Sopenharmony_ci		return false;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	ave_rssi = ieee80211_ave_rssi(ctx->vif);
5958c2ecf20Sopenharmony_ci	if (!ave_rssi) {
5968c2ecf20Sopenharmony_ci		/* no rssi data, no changes to reduce tx power */
5978c2ecf20Sopenharmony_ci		IWL_DEBUG_COEX(priv, "no rssi data available\n");
5988c2ecf20Sopenharmony_ci		return need_update;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	if (!priv->reduced_txpower &&
6018c2ecf20Sopenharmony_ci	    !iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
6028c2ecf20Sopenharmony_ci	    (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) &&
6038c2ecf20Sopenharmony_ci	    (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
6048c2ecf20Sopenharmony_ci	    BT_UART_MSG_FRAME3OBEX_MSK)) &&
6058c2ecf20Sopenharmony_ci	    !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
6068c2ecf20Sopenharmony_ci	    BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) {
6078c2ecf20Sopenharmony_ci		/* enabling reduced tx power */
6088c2ecf20Sopenharmony_ci		priv->reduced_txpower = true;
6098c2ecf20Sopenharmony_ci		priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
6108c2ecf20Sopenharmony_ci		need_update = true;
6118c2ecf20Sopenharmony_ci	} else if (priv->reduced_txpower &&
6128c2ecf20Sopenharmony_ci		   (iwl_is_associated(priv, IWL_RXON_CTX_PAN) ||
6138c2ecf20Sopenharmony_ci		   (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) ||
6148c2ecf20Sopenharmony_ci		   (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK |
6158c2ecf20Sopenharmony_ci		   BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) ||
6168c2ecf20Sopenharmony_ci		   !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK |
6178c2ecf20Sopenharmony_ci		   BT_UART_MSG_FRAME3OBEX_MSK)))) {
6188c2ecf20Sopenharmony_ci		/* disable reduced tx power */
6198c2ecf20Sopenharmony_ci		priv->reduced_txpower = false;
6208c2ecf20Sopenharmony_ci		priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR;
6218c2ecf20Sopenharmony_ci		need_update = true;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return need_update;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv,
6288c2ecf20Sopenharmony_ci					 struct iwl_rx_cmd_buffer *rxb)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
6318c2ecf20Sopenharmony_ci	struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data;
6328c2ecf20Sopenharmony_ci	struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) {
6358c2ecf20Sopenharmony_ci		/* bt coex disabled */
6368c2ecf20Sopenharmony_ci		return;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "BT Coex notification:\n");
6408c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "    status: %d\n", coex->bt_status);
6418c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "    traffic load: %d\n", coex->bt_traffic_load);
6428c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(priv, "    CI compliance: %d\n",
6438c2ecf20Sopenharmony_ci			coex->bt_ci_compliance);
6448c2ecf20Sopenharmony_ci	iwlagn_print_uartmsg(priv, uart_msg);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	priv->last_bt_traffic_load = priv->bt_traffic_load;
6478c2ecf20Sopenharmony_ci	priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
6508c2ecf20Sopenharmony_ci		if (priv->bt_status != coex->bt_status ||
6518c2ecf20Sopenharmony_ci		    priv->last_bt_traffic_load != coex->bt_traffic_load) {
6528c2ecf20Sopenharmony_ci			if (coex->bt_status) {
6538c2ecf20Sopenharmony_ci				/* BT on */
6548c2ecf20Sopenharmony_ci				if (!priv->bt_ch_announce)
6558c2ecf20Sopenharmony_ci					priv->bt_traffic_load =
6568c2ecf20Sopenharmony_ci						IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
6578c2ecf20Sopenharmony_ci				else
6588c2ecf20Sopenharmony_ci					priv->bt_traffic_load =
6598c2ecf20Sopenharmony_ci						coex->bt_traffic_load;
6608c2ecf20Sopenharmony_ci			} else {
6618c2ecf20Sopenharmony_ci				/* BT off */
6628c2ecf20Sopenharmony_ci				priv->bt_traffic_load =
6638c2ecf20Sopenharmony_ci					IWL_BT_COEX_TRAFFIC_LOAD_NONE;
6648c2ecf20Sopenharmony_ci			}
6658c2ecf20Sopenharmony_ci			priv->bt_status = coex->bt_status;
6668c2ecf20Sopenharmony_ci			queue_work(priv->workqueue,
6678c2ecf20Sopenharmony_ci				   &priv->bt_traffic_change_work);
6688c2ecf20Sopenharmony_ci		}
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* schedule to send runtime bt_config */
6728c2ecf20Sopenharmony_ci	/* check reduce power before change ack/cts kill mask */
6738c2ecf20Sopenharmony_ci	if (iwlagn_fill_txpower_mode(priv, uart_msg) ||
6748c2ecf20Sopenharmony_ci	    iwlagn_set_kill_msk(priv, uart_msg))
6758c2ecf20Sopenharmony_ci		queue_work(priv->workqueue, &priv->bt_runtime_config);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* FIXME: based on notification, adjust the prio_boost */
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	priv->bt_ci_compliance = coex->bt_ci_compliance;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_civoid iwlagn_bt_rx_handler_setup(struct iwl_priv *priv)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] =
6868c2ecf20Sopenharmony_ci		iwlagn_bt_coex_profile_notif;
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_civoid iwlagn_bt_setup_deferred_work(struct iwl_priv *priv)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	INIT_WORK(&priv->bt_traffic_change_work,
6928c2ecf20Sopenharmony_ci		  iwlagn_bt_traffic_change_work);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_civoid iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	cancel_work_sync(&priv->bt_traffic_change_work);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic bool is_single_rx_stream(struct iwl_priv *priv)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
7038c2ecf20Sopenharmony_ci	       priv->current_ht_config.single_chain_sufficient;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci#define IWL_NUM_RX_CHAINS_MULTIPLE	3
7078c2ecf20Sopenharmony_ci#define IWL_NUM_RX_CHAINS_SINGLE	2
7088c2ecf20Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_DUAL	2
7098c2ecf20Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_SINGLE	1
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci/*
7128c2ecf20Sopenharmony_ci * Determine how many receiver/antenna chains to use.
7138c2ecf20Sopenharmony_ci *
7148c2ecf20Sopenharmony_ci * More provides better reception via diversity.  Fewer saves power
7158c2ecf20Sopenharmony_ci * at the expense of throughput, but only when not in powersave to
7168c2ecf20Sopenharmony_ci * start with.
7178c2ecf20Sopenharmony_ci *
7188c2ecf20Sopenharmony_ci * MIMO (dual stream) requires at least 2, but works better with 3.
7198c2ecf20Sopenharmony_ci * This does not determine *which* chains to use, just how many.
7208c2ecf20Sopenharmony_ci */
7218c2ecf20Sopenharmony_cistatic int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	if (priv->lib->bt_params &&
7248c2ecf20Sopenharmony_ci	    priv->lib->bt_params->advanced_bt_coexist &&
7258c2ecf20Sopenharmony_ci	    (priv->bt_full_concurrent ||
7268c2ecf20Sopenharmony_ci	     priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
7278c2ecf20Sopenharmony_ci		/*
7288c2ecf20Sopenharmony_ci		 * only use chain 'A' in bt high traffic load or
7298c2ecf20Sopenharmony_ci		 * full concurrency mode
7308c2ecf20Sopenharmony_ci		 */
7318c2ecf20Sopenharmony_ci		return IWL_NUM_RX_CHAINS_SINGLE;
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_ci	/* # of Rx chains to use when expecting MIMO. */
7348c2ecf20Sopenharmony_ci	if (is_single_rx_stream(priv))
7358c2ecf20Sopenharmony_ci		return IWL_NUM_RX_CHAINS_SINGLE;
7368c2ecf20Sopenharmony_ci	else
7378c2ecf20Sopenharmony_ci		return IWL_NUM_RX_CHAINS_MULTIPLE;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci/*
7418c2ecf20Sopenharmony_ci * When we are in power saving mode, unless device support spatial
7428c2ecf20Sopenharmony_ci * multiplexing power save, use the active count for rx chain count.
7438c2ecf20Sopenharmony_ci */
7448c2ecf20Sopenharmony_cistatic int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	/* # Rx chains when idling, depending on SMPS mode */
7478c2ecf20Sopenharmony_ci	switch (priv->current_ht_config.smps) {
7488c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_STATIC:
7498c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_DYNAMIC:
7508c2ecf20Sopenharmony_ci		return IWL_NUM_IDLE_CHAINS_SINGLE;
7518c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_AUTOMATIC:
7528c2ecf20Sopenharmony_ci	case IEEE80211_SMPS_OFF:
7538c2ecf20Sopenharmony_ci		return active_cnt;
7548c2ecf20Sopenharmony_ci	default:
7558c2ecf20Sopenharmony_ci		WARN(1, "invalid SMPS mode %d",
7568c2ecf20Sopenharmony_ci		     priv->current_ht_config.smps);
7578c2ecf20Sopenharmony_ci		return active_cnt;
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci/* up to 4 chains */
7628c2ecf20Sopenharmony_cistatic u8 iwl_count_chain_bitmap(u32 chain_bitmap)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	u8 res;
7658c2ecf20Sopenharmony_ci	res = (chain_bitmap & BIT(0)) >> 0;
7668c2ecf20Sopenharmony_ci	res += (chain_bitmap & BIT(1)) >> 1;
7678c2ecf20Sopenharmony_ci	res += (chain_bitmap & BIT(2)) >> 2;
7688c2ecf20Sopenharmony_ci	res += (chain_bitmap & BIT(3)) >> 3;
7698c2ecf20Sopenharmony_ci	return res;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci/*
7738c2ecf20Sopenharmony_ci * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
7748c2ecf20Sopenharmony_ci *
7758c2ecf20Sopenharmony_ci * Selects how many and which Rx receivers/antennas/chains to use.
7768c2ecf20Sopenharmony_ci * This should not be used for scan command ... it puts data in wrong place.
7778c2ecf20Sopenharmony_ci */
7788c2ecf20Sopenharmony_civoid iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	bool is_single = is_single_rx_stream(priv);
7818c2ecf20Sopenharmony_ci	bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
7828c2ecf20Sopenharmony_ci	u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
7838c2ecf20Sopenharmony_ci	u32 active_chains;
7848c2ecf20Sopenharmony_ci	u16 rx_chain;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* Tell uCode which antennas are actually connected.
7878c2ecf20Sopenharmony_ci	 * Before first association, we assume all antennas are connected.
7888c2ecf20Sopenharmony_ci	 * Just after first association, iwl_chain_noise_calibration()
7898c2ecf20Sopenharmony_ci	 *    checks which antennas actually *are* connected. */
7908c2ecf20Sopenharmony_ci	if (priv->chain_noise_data.active_chains)
7918c2ecf20Sopenharmony_ci		active_chains = priv->chain_noise_data.active_chains;
7928c2ecf20Sopenharmony_ci	else
7938c2ecf20Sopenharmony_ci		active_chains = priv->nvm_data->valid_rx_ant;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (priv->lib->bt_params &&
7968c2ecf20Sopenharmony_ci	    priv->lib->bt_params->advanced_bt_coexist &&
7978c2ecf20Sopenharmony_ci	    (priv->bt_full_concurrent ||
7988c2ecf20Sopenharmony_ci	     priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
7998c2ecf20Sopenharmony_ci		/*
8008c2ecf20Sopenharmony_ci		 * only use chain 'A' in bt high traffic load or
8018c2ecf20Sopenharmony_ci		 * full concurrency mode
8028c2ecf20Sopenharmony_ci		 */
8038c2ecf20Sopenharmony_ci		active_chains = first_antenna(active_chains);
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* How many receivers should we use? */
8098c2ecf20Sopenharmony_ci	active_rx_cnt = iwl_get_active_rx_chain_count(priv);
8108c2ecf20Sopenharmony_ci	idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* correct rx chain count according hw settings
8148c2ecf20Sopenharmony_ci	 * and chain noise calibration
8158c2ecf20Sopenharmony_ci	 */
8168c2ecf20Sopenharmony_ci	valid_rx_cnt = iwl_count_chain_bitmap(active_chains);
8178c2ecf20Sopenharmony_ci	if (valid_rx_cnt < active_rx_cnt)
8188c2ecf20Sopenharmony_ci		active_rx_cnt = valid_rx_cnt;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (valid_rx_cnt < idle_rx_cnt)
8218c2ecf20Sopenharmony_ci		idle_rx_cnt = valid_rx_cnt;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
8248c2ecf20Sopenharmony_ci	rx_chain |= idle_rx_cnt  << RXON_RX_CHAIN_CNT_POS;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	ctx->staging.rx_chain = cpu_to_le16(rx_chain);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam)
8298c2ecf20Sopenharmony_ci		ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
8308c2ecf20Sopenharmony_ci	else
8318c2ecf20Sopenharmony_ci		ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n",
8348c2ecf20Sopenharmony_ci			ctx->staging.rx_chain,
8358c2ecf20Sopenharmony_ci			active_rx_cnt, idle_rx_cnt);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 ||
8388c2ecf20Sopenharmony_ci		active_rx_cnt < idle_rx_cnt);
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ciu8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	int i;
8448c2ecf20Sopenharmony_ci	u8 ind = ant;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (priv->band == NL80211_BAND_2GHZ &&
8478c2ecf20Sopenharmony_ci	    priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
8488c2ecf20Sopenharmony_ci		return 0;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	for (i = 0; i < RATE_ANT_NUM - 1; i++) {
8518c2ecf20Sopenharmony_ci		ind = (ind + 1) < RATE_ANT_NUM ?  ind + 1 : 0;
8528c2ecf20Sopenharmony_ci		if (valid & BIT(ind))
8538c2ecf20Sopenharmony_ci			return ind;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci	return ant;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
8598c2ecf20Sopenharmony_cistatic void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	int i;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	for (i = 0; i < IWLAGN_P1K_SIZE; i++)
8648c2ecf20Sopenharmony_ci		out[i] = cpu_to_le16(p1k[i]);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistruct wowlan_key_data {
8688c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx;
8698c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc;
8708c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_tkip_params_cmd *tkip;
8718c2ecf20Sopenharmony_ci	const u8 *bssid;
8728c2ecf20Sopenharmony_ci	bool error, use_rsc_tsc, use_tkip;
8738c2ecf20Sopenharmony_ci};
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_cistatic void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw,
8778c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif,
8788c2ecf20Sopenharmony_ci			       struct ieee80211_sta *sta,
8798c2ecf20Sopenharmony_ci			       struct ieee80211_key_conf *key,
8808c2ecf20Sopenharmony_ci			       void *_data)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
8838c2ecf20Sopenharmony_ci	struct wowlan_key_data *data = _data;
8848c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx = data->ctx;
8858c2ecf20Sopenharmony_ci	struct aes_sc *aes_sc, *aes_tx_sc = NULL;
8868c2ecf20Sopenharmony_ci	struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
8878c2ecf20Sopenharmony_ci	struct iwlagn_p1k_cache *rx_p1ks;
8888c2ecf20Sopenharmony_ci	u8 *rx_mic_key;
8898c2ecf20Sopenharmony_ci	struct ieee80211_key_seq seq;
8908c2ecf20Sopenharmony_ci	u32 cur_rx_iv32 = 0;
8918c2ecf20Sopenharmony_ci	u16 p1k[IWLAGN_P1K_SIZE];
8928c2ecf20Sopenharmony_ci	int ret, i;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	mutex_lock(&priv->mutex);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
8978c2ecf20Sopenharmony_ci	     key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
8988c2ecf20Sopenharmony_ci	     !sta && !ctx->key_mapping_keys)
8998c2ecf20Sopenharmony_ci		ret = iwl_set_default_wep_key(priv, ctx, key);
9008c2ecf20Sopenharmony_ci	else
9018c2ecf20Sopenharmony_ci		ret = iwl_set_dynamic_key(priv, ctx, key, sta);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (ret) {
9048c2ecf20Sopenharmony_ci		IWL_ERR(priv, "Error setting key during suspend!\n");
9058c2ecf20Sopenharmony_ci		data->error = true;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	switch (key->cipher) {
9098c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
9108c2ecf20Sopenharmony_ci		if (sta) {
9118c2ecf20Sopenharmony_ci			u64 pn64;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci			tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
9148c2ecf20Sopenharmony_ci			tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci			rx_p1ks = data->tkip->rx_uni;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci			pn64 = atomic64_read(&key->tx_pn);
9198c2ecf20Sopenharmony_ci			tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64));
9208c2ecf20Sopenharmony_ci			tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64));
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci			ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
9238c2ecf20Sopenharmony_ci			iwlagn_convert_p1k(p1k, data->tkip->tx.p1k);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci			memcpy(data->tkip->mic_keys.tx,
9268c2ecf20Sopenharmony_ci			       &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
9278c2ecf20Sopenharmony_ci			       IWLAGN_MIC_KEY_SIZE);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci			rx_mic_key = data->tkip->mic_keys.rx_unicast;
9308c2ecf20Sopenharmony_ci		} else {
9318c2ecf20Sopenharmony_ci			tkip_sc =
9328c2ecf20Sopenharmony_ci				data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
9338c2ecf20Sopenharmony_ci			rx_p1ks = data->tkip->rx_multi;
9348c2ecf20Sopenharmony_ci			rx_mic_key = data->tkip->mic_keys.rx_mcast;
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		/*
9388c2ecf20Sopenharmony_ci		 * For non-QoS this relies on the fact that both the uCode and
9398c2ecf20Sopenharmony_ci		 * mac80211 use TID 0 (as they need to to avoid replay attacks)
9408c2ecf20Sopenharmony_ci		 * for checking the IV in the frames.
9418c2ecf20Sopenharmony_ci		 */
9428c2ecf20Sopenharmony_ci		for (i = 0; i < IWLAGN_NUM_RSC; i++) {
9438c2ecf20Sopenharmony_ci			ieee80211_get_key_rx_seq(key, i, &seq);
9448c2ecf20Sopenharmony_ci			tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
9458c2ecf20Sopenharmony_ci			tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
9468c2ecf20Sopenharmony_ci			/* wrapping isn't allowed, AP must rekey */
9478c2ecf20Sopenharmony_ci			if (seq.tkip.iv32 > cur_rx_iv32)
9488c2ecf20Sopenharmony_ci				cur_rx_iv32 = seq.tkip.iv32;
9498c2ecf20Sopenharmony_ci		}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k);
9528c2ecf20Sopenharmony_ci		iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k);
9538c2ecf20Sopenharmony_ci		ieee80211_get_tkip_rx_p1k(key, data->bssid,
9548c2ecf20Sopenharmony_ci					  cur_rx_iv32 + 1, p1k);
9558c2ecf20Sopenharmony_ci		iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		memcpy(rx_mic_key,
9588c2ecf20Sopenharmony_ci		       &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
9598c2ecf20Sopenharmony_ci		       IWLAGN_MIC_KEY_SIZE);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci		data->use_tkip = true;
9628c2ecf20Sopenharmony_ci		data->use_rsc_tsc = true;
9638c2ecf20Sopenharmony_ci		break;
9648c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
9658c2ecf20Sopenharmony_ci		if (sta) {
9668c2ecf20Sopenharmony_ci			u64 pn64;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci			aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
9698c2ecf20Sopenharmony_ci			aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci			pn64 = atomic64_read(&key->tx_pn);
9728c2ecf20Sopenharmony_ci			aes_tx_sc->pn = cpu_to_le64(pn64);
9738c2ecf20Sopenharmony_ci		} else
9748c2ecf20Sopenharmony_ci			aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		/*
9778c2ecf20Sopenharmony_ci		 * For non-QoS this relies on the fact that both the uCode and
9788c2ecf20Sopenharmony_ci		 * mac80211 use TID 0 for checking the IV in the frames.
9798c2ecf20Sopenharmony_ci		 */
9808c2ecf20Sopenharmony_ci		for (i = 0; i < IWLAGN_NUM_RSC; i++) {
9818c2ecf20Sopenharmony_ci			u8 *pn = seq.ccmp.pn;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci			ieee80211_get_key_rx_seq(key, i, &seq);
9848c2ecf20Sopenharmony_ci			aes_sc[i].pn = cpu_to_le64(
9858c2ecf20Sopenharmony_ci					(u64)pn[5] |
9868c2ecf20Sopenharmony_ci					((u64)pn[4] << 8) |
9878c2ecf20Sopenharmony_ci					((u64)pn[3] << 16) |
9888c2ecf20Sopenharmony_ci					((u64)pn[2] << 24) |
9898c2ecf20Sopenharmony_ci					((u64)pn[1] << 32) |
9908c2ecf20Sopenharmony_ci					((u64)pn[0] << 40));
9918c2ecf20Sopenharmony_ci		}
9928c2ecf20Sopenharmony_ci		data->use_rsc_tsc = true;
9938c2ecf20Sopenharmony_ci		break;
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	mutex_unlock(&priv->mutex);
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ciint iwlagn_send_patterns(struct iwl_priv *priv,
10008c2ecf20Sopenharmony_ci			struct cfg80211_wowlan *wowlan)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_patterns_cmd *pattern_cmd;
10038c2ecf20Sopenharmony_ci	struct iwl_host_cmd cmd = {
10048c2ecf20Sopenharmony_ci		.id = REPLY_WOWLAN_PATTERNS,
10058c2ecf20Sopenharmony_ci		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
10068c2ecf20Sopenharmony_ci	};
10078c2ecf20Sopenharmony_ci	int i, err;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (!wowlan->n_patterns)
10108c2ecf20Sopenharmony_ci		return 0;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
10158c2ecf20Sopenharmony_ci	if (!pattern_cmd)
10168c2ecf20Sopenharmony_ci		return -ENOMEM;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	for (i = 0; i < wowlan->n_patterns; i++) {
10218c2ecf20Sopenharmony_ci		int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		memcpy(&pattern_cmd->patterns[i].mask,
10248c2ecf20Sopenharmony_ci			wowlan->patterns[i].mask, mask_len);
10258c2ecf20Sopenharmony_ci		memcpy(&pattern_cmd->patterns[i].pattern,
10268c2ecf20Sopenharmony_ci			wowlan->patterns[i].pattern,
10278c2ecf20Sopenharmony_ci			wowlan->patterns[i].pattern_len);
10288c2ecf20Sopenharmony_ci		pattern_cmd->patterns[i].mask_size = mask_len;
10298c2ecf20Sopenharmony_ci		pattern_cmd->patterns[i].pattern_size =
10308c2ecf20Sopenharmony_ci			wowlan->patterns[i].pattern_len;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	cmd.data[0] = pattern_cmd;
10348c2ecf20Sopenharmony_ci	err = iwl_dvm_send_cmd(priv, &cmd);
10358c2ecf20Sopenharmony_ci	kfree(pattern_cmd);
10368c2ecf20Sopenharmony_ci	return err;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ciint iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd;
10428c2ecf20Sopenharmony_ci	struct iwl_rxon_cmd rxon;
10438c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
10448c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd;
10458c2ecf20Sopenharmony_ci	struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {};
10468c2ecf20Sopenharmony_ci	struct iwlagn_d3_config_cmd d3_cfg_cmd = {
10478c2ecf20Sopenharmony_ci		/*
10488c2ecf20Sopenharmony_ci		 * Program the minimum sleep time to 10 seconds, as many
10498c2ecf20Sopenharmony_ci		 * platforms have issues processing a wakeup signal while
10508c2ecf20Sopenharmony_ci		 * still being in the process of suspending.
10518c2ecf20Sopenharmony_ci		 */
10528c2ecf20Sopenharmony_ci		.min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
10538c2ecf20Sopenharmony_ci	};
10548c2ecf20Sopenharmony_ci	struct wowlan_key_data key_data = {
10558c2ecf20Sopenharmony_ci		.ctx = ctx,
10568c2ecf20Sopenharmony_ci		.bssid = ctx->active.bssid_addr,
10578c2ecf20Sopenharmony_ci		.use_rsc_tsc = false,
10588c2ecf20Sopenharmony_ci		.tkip = &tkip_cmd,
10598c2ecf20Sopenharmony_ci		.use_tkip = false,
10608c2ecf20Sopenharmony_ci	};
10618c2ecf20Sopenharmony_ci	int ret, i;
10628c2ecf20Sopenharmony_ci	u16 seq;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
10658c2ecf20Sopenharmony_ci	if (!key_data.rsc_tsc)
10668c2ecf20Sopenharmony_ci		return -ENOMEM;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd));
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	/*
10718c2ecf20Sopenharmony_ci	 * We know the last used seqno, and the uCode expects to know that
10728c2ecf20Sopenharmony_ci	 * one, it will increment before TX.
10738c2ecf20Sopenharmony_ci	 */
10748c2ecf20Sopenharmony_ci	seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ;
10758c2ecf20Sopenharmony_ci	wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/*
10788c2ecf20Sopenharmony_ci	 * For QoS counters, we store the one to use next, so subtract 0x10
10798c2ecf20Sopenharmony_ci	 * since the uCode will add 0x10 before using the value.
10808c2ecf20Sopenharmony_ci	 */
10818c2ecf20Sopenharmony_ci	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
10828c2ecf20Sopenharmony_ci		seq = priv->tid_data[IWL_AP_ID][i].seq_number;
10838c2ecf20Sopenharmony_ci		seq -= 0x10;
10848c2ecf20Sopenharmony_ci		wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq);
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	if (wowlan->disconnect)
10888c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
10898c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
10908c2ecf20Sopenharmony_ci				    IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE);
10918c2ecf20Sopenharmony_ci	if (wowlan->magic_pkt)
10928c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
10938c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET);
10948c2ecf20Sopenharmony_ci	if (wowlan->gtk_rekey_failure)
10958c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
10968c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
10978c2ecf20Sopenharmony_ci	if (wowlan->eap_identity_req)
10988c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
10998c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ);
11008c2ecf20Sopenharmony_ci	if (wowlan->four_way_handshake)
11018c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
11028c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
11038c2ecf20Sopenharmony_ci	if (wowlan->n_patterns)
11048c2ecf20Sopenharmony_ci		wakeup_filter_cmd.enabled |=
11058c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	if (wowlan->rfkill_release)
11088c2ecf20Sopenharmony_ci		d3_cfg_cmd.wakeup_flags |=
11098c2ecf20Sopenharmony_ci			cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	iwl_scan_cancel_timeout(priv, 200);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	memcpy(&rxon, &ctx->active, sizeof(rxon));
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	priv->ucode_loaded = false;
11168c2ecf20Sopenharmony_ci	iwl_trans_stop_device(priv->trans);
11178c2ecf20Sopenharmony_ci	ret = iwl_trans_start_hw(priv->trans);
11188c2ecf20Sopenharmony_ci	if (ret)
11198c2ecf20Sopenharmony_ci		goto out;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	priv->wowlan = true;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
11248c2ecf20Sopenharmony_ci	if (ret)
11258c2ecf20Sopenharmony_ci		goto out;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	/* now configure WoWLAN ucode */
11288c2ecf20Sopenharmony_ci	ret = iwl_alive_start(priv);
11298c2ecf20Sopenharmony_ci	if (ret)
11308c2ecf20Sopenharmony_ci		goto out;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	memcpy(&ctx->staging, &rxon, sizeof(rxon));
11338c2ecf20Sopenharmony_ci	ret = iwlagn_commit_rxon(priv, ctx);
11348c2ecf20Sopenharmony_ci	if (ret)
11358c2ecf20Sopenharmony_ci		goto out;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	ret = iwl_power_update_mode(priv, true);
11388c2ecf20Sopenharmony_ci	if (ret)
11398c2ecf20Sopenharmony_ci		goto out;
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	if (!iwlwifi_mod_params.swcrypto) {
11428c2ecf20Sopenharmony_ci		/* mark all keys clear */
11438c2ecf20Sopenharmony_ci		priv->ucode_key_table = 0;
11448c2ecf20Sopenharmony_ci		ctx->key_mapping_keys = 0;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		/*
11478c2ecf20Sopenharmony_ci		 * This needs to be unlocked due to lock ordering
11488c2ecf20Sopenharmony_ci		 * constraints. Since we're in the suspend path
11498c2ecf20Sopenharmony_ci		 * that isn't really a problem though.
11508c2ecf20Sopenharmony_ci		 */
11518c2ecf20Sopenharmony_ci		mutex_unlock(&priv->mutex);
11528c2ecf20Sopenharmony_ci		ieee80211_iter_keys(priv->hw, ctx->vif,
11538c2ecf20Sopenharmony_ci				    iwlagn_wowlan_program_keys,
11548c2ecf20Sopenharmony_ci				    &key_data);
11558c2ecf20Sopenharmony_ci		mutex_lock(&priv->mutex);
11568c2ecf20Sopenharmony_ci		if (key_data.error) {
11578c2ecf20Sopenharmony_ci			ret = -EIO;
11588c2ecf20Sopenharmony_ci			goto out;
11598c2ecf20Sopenharmony_ci		}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci		if (key_data.use_rsc_tsc) {
11628c2ecf20Sopenharmony_ci			struct iwl_host_cmd rsc_tsc_cmd = {
11638c2ecf20Sopenharmony_ci				.id = REPLY_WOWLAN_TSC_RSC_PARAMS,
11648c2ecf20Sopenharmony_ci				.data[0] = key_data.rsc_tsc,
11658c2ecf20Sopenharmony_ci				.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
11668c2ecf20Sopenharmony_ci				.len[0] = sizeof(*key_data.rsc_tsc),
11678c2ecf20Sopenharmony_ci			};
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci			ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd);
11708c2ecf20Sopenharmony_ci			if (ret)
11718c2ecf20Sopenharmony_ci				goto out;
11728c2ecf20Sopenharmony_ci		}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		if (key_data.use_tkip) {
11758c2ecf20Sopenharmony_ci			ret = iwl_dvm_send_cmd_pdu(priv,
11768c2ecf20Sopenharmony_ci						 REPLY_WOWLAN_TKIP_PARAMS,
11778c2ecf20Sopenharmony_ci						 0, sizeof(tkip_cmd),
11788c2ecf20Sopenharmony_ci						 &tkip_cmd);
11798c2ecf20Sopenharmony_ci			if (ret)
11808c2ecf20Sopenharmony_ci				goto out;
11818c2ecf20Sopenharmony_ci		}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci		if (priv->have_rekey_data) {
11848c2ecf20Sopenharmony_ci			memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
11858c2ecf20Sopenharmony_ci			memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN);
11868c2ecf20Sopenharmony_ci			kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
11878c2ecf20Sopenharmony_ci			memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN);
11888c2ecf20Sopenharmony_ci			kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
11898c2ecf20Sopenharmony_ci			kek_kck_cmd.replay_ctr = priv->replay_ctr;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci			ret = iwl_dvm_send_cmd_pdu(priv,
11928c2ecf20Sopenharmony_ci						 REPLY_WOWLAN_KEK_KCK_MATERIAL,
11938c2ecf20Sopenharmony_ci						 0, sizeof(kek_kck_cmd),
11948c2ecf20Sopenharmony_ci						 &kek_kck_cmd);
11958c2ecf20Sopenharmony_ci			if (ret)
11968c2ecf20Sopenharmony_ci				goto out;
11978c2ecf20Sopenharmony_ci		}
11988c2ecf20Sopenharmony_ci	}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0,
12018c2ecf20Sopenharmony_ci				     sizeof(d3_cfg_cmd), &d3_cfg_cmd);
12028c2ecf20Sopenharmony_ci	if (ret)
12038c2ecf20Sopenharmony_ci		goto out;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER,
12068c2ecf20Sopenharmony_ci				 0, sizeof(wakeup_filter_cmd),
12078c2ecf20Sopenharmony_ci				 &wakeup_filter_cmd);
12088c2ecf20Sopenharmony_ci	if (ret)
12098c2ecf20Sopenharmony_ci		goto out;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	ret = iwlagn_send_patterns(priv, wowlan);
12128c2ecf20Sopenharmony_ci out:
12138c2ecf20Sopenharmony_ci	kfree(key_data.rsc_tsc);
12148c2ecf20Sopenharmony_ci	return ret;
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci#endif
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ciint iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
12218c2ecf20Sopenharmony_ci		IWL_WARN(priv, "Not sending command - %s KILL\n",
12228c2ecf20Sopenharmony_ci			 iwl_is_rfkill(priv) ? "RF" : "CT");
12238c2ecf20Sopenharmony_ci		return -EIO;
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (test_bit(STATUS_FW_ERROR, &priv->status)) {
12278c2ecf20Sopenharmony_ci		IWL_ERR(priv, "Command %s failed: FW Error\n",
12288c2ecf20Sopenharmony_ci			iwl_get_cmd_string(priv->trans, cmd->id));
12298c2ecf20Sopenharmony_ci		return -EIO;
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/*
12338c2ecf20Sopenharmony_ci	 * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag
12348c2ecf20Sopenharmony_ci	 * in iwl_down but cancel the workers only later.
12358c2ecf20Sopenharmony_ci	 */
12368c2ecf20Sopenharmony_ci	if (!priv->ucode_loaded) {
12378c2ecf20Sopenharmony_ci		IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id);
12388c2ecf20Sopenharmony_ci		return -EIO;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/*
12428c2ecf20Sopenharmony_ci	 * Synchronous commands from this op-mode must hold
12438c2ecf20Sopenharmony_ci	 * the mutex, this ensures we don't try to send two
12448c2ecf20Sopenharmony_ci	 * (or more) synchronous commands at a time.
12458c2ecf20Sopenharmony_ci	 */
12468c2ecf20Sopenharmony_ci	if (!(cmd->flags & CMD_ASYNC))
12478c2ecf20Sopenharmony_ci		lockdep_assert_held(&priv->mutex);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	return iwl_trans_send_cmd(priv->trans, cmd);
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ciint iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id,
12538c2ecf20Sopenharmony_ci			 u32 flags, u16 len, const void *data)
12548c2ecf20Sopenharmony_ci{
12558c2ecf20Sopenharmony_ci	struct iwl_host_cmd cmd = {
12568c2ecf20Sopenharmony_ci		.id = id,
12578c2ecf20Sopenharmony_ci		.len = { len, },
12588c2ecf20Sopenharmony_ci		.data = { data, },
12598c2ecf20Sopenharmony_ci		.flags = flags,
12608c2ecf20Sopenharmony_ci	};
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	return iwl_dvm_send_cmd(priv, &cmd);
12638c2ecf20Sopenharmony_ci}
1264