162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci * Copyright(c) 2015 Intel Deutschland GmbH
662306a36Sopenharmony_ci *****************************************************************************/
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/etherdevice.h>
962306a36Sopenharmony_ci#include "iwl-trans.h"
1062306a36Sopenharmony_ci#include "iwl-modparams.h"
1162306a36Sopenharmony_ci#include "dev.h"
1262306a36Sopenharmony_ci#include "agn.h"
1362306a36Sopenharmony_ci#include "calib.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * initialize rxon structure with default values from eeprom
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_civoid iwl_connection_init_rx_config(struct iwl_priv *priv,
1962306a36Sopenharmony_ci				   struct iwl_rxon_context *ctx)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	memset(&ctx->staging, 0, sizeof(ctx->staging));
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (!ctx->vif) {
2462306a36Sopenharmony_ci		ctx->staging.dev_type = ctx->unused_devtype;
2562306a36Sopenharmony_ci	} else
2662306a36Sopenharmony_ci	switch (ctx->vif->type) {
2762306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
2862306a36Sopenharmony_ci		ctx->staging.dev_type = ctx->ap_devtype;
2962306a36Sopenharmony_ci		break;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
3262306a36Sopenharmony_ci		ctx->staging.dev_type = ctx->station_devtype;
3362306a36Sopenharmony_ci		ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
3462306a36Sopenharmony_ci		break;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
3762306a36Sopenharmony_ci		ctx->staging.dev_type = ctx->ibss_devtype;
3862306a36Sopenharmony_ci		ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
3962306a36Sopenharmony_ci		ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK |
4062306a36Sopenharmony_ci						  RXON_FILTER_ACCEPT_GRP_MSK;
4162306a36Sopenharmony_ci		break;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
4462306a36Sopenharmony_ci		ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER;
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	default:
4862306a36Sopenharmony_ci		IWL_ERR(priv, "Unsupported interface type %d\n",
4962306a36Sopenharmony_ci			ctx->vif->type);
5062306a36Sopenharmony_ci		break;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#if 0
5462306a36Sopenharmony_ci	/* TODO:  Figure out when short_preamble would be set and cache from
5562306a36Sopenharmony_ci	 * that */
5662306a36Sopenharmony_ci	if (!hw_to_local(priv->hw)->short_preamble)
5762306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
6062306a36Sopenharmony_ci#endif
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	ctx->staging.channel =
6362306a36Sopenharmony_ci		cpu_to_le16(priv->hw->conf.chandef.chan->hw_value);
6462306a36Sopenharmony_ci	priv->band = priv->hw->conf.chandef.chan->band;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* clear both MIX and PURE40 mode flag */
6962306a36Sopenharmony_ci	ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
7062306a36Sopenharmony_ci					RXON_FLG_CHANNEL_MODE_PURE_40);
7162306a36Sopenharmony_ci	if (ctx->vif)
7262306a36Sopenharmony_ci		memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff;
7562306a36Sopenharmony_ci	ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff;
7662306a36Sopenharmony_ci	ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int iwlagn_disable_bss(struct iwl_priv *priv,
8062306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx,
8162306a36Sopenharmony_ci			      struct iwl_rxon_cmd *send)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	__le32 old_filter = send->filter_flags;
8462306a36Sopenharmony_ci	int ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
8762306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
8862306a36Sopenharmony_ci				0, sizeof(*send), send);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	send->filter_flags = old_filter;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (ret)
9362306a36Sopenharmony_ci		IWL_DEBUG_QUIET_RFKILL(priv,
9462306a36Sopenharmony_ci			"Error clearing ASSOC_MSK on BSS (%d)\n", ret);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return ret;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int iwlagn_disable_pan(struct iwl_priv *priv,
10062306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx,
10162306a36Sopenharmony_ci			      struct iwl_rxon_cmd *send)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct iwl_notification_wait disable_wait;
10462306a36Sopenharmony_ci	__le32 old_filter = send->filter_flags;
10562306a36Sopenharmony_ci	u8 old_dev_type = send->dev_type;
10662306a36Sopenharmony_ci	int ret;
10762306a36Sopenharmony_ci	static const u16 deactivate_cmd[] = {
10862306a36Sopenharmony_ci		REPLY_WIPAN_DEACTIVATION_COMPLETE
10962306a36Sopenharmony_ci	};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	iwl_init_notification_wait(&priv->notif_wait, &disable_wait,
11262306a36Sopenharmony_ci				   deactivate_cmd, ARRAY_SIZE(deactivate_cmd),
11362306a36Sopenharmony_ci				   NULL, NULL);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
11662306a36Sopenharmony_ci	send->dev_type = RXON_DEV_TYPE_P2P;
11762306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
11862306a36Sopenharmony_ci				0, sizeof(*send), send);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	send->filter_flags = old_filter;
12162306a36Sopenharmony_ci	send->dev_type = old_dev_type;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (ret) {
12462306a36Sopenharmony_ci		IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);
12562306a36Sopenharmony_ci		iwl_remove_notification(&priv->notif_wait, &disable_wait);
12662306a36Sopenharmony_ci	} else {
12762306a36Sopenharmony_ci		ret = iwl_wait_notification(&priv->notif_wait,
12862306a36Sopenharmony_ci					    &disable_wait, HZ);
12962306a36Sopenharmony_ci		if (ret)
13062306a36Sopenharmony_ci			IWL_ERR(priv, "Timed out waiting for PAN disable\n");
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return ret;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int iwlagn_disconn_pan(struct iwl_priv *priv,
13762306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx,
13862306a36Sopenharmony_ci			      struct iwl_rxon_cmd *send)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	__le32 old_filter = send->filter_flags;
14162306a36Sopenharmony_ci	int ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
14462306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
14562306a36Sopenharmony_ci				sizeof(*send), send);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	send->filter_flags = old_filter;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return ret;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void iwlagn_update_qos(struct iwl_priv *priv,
15362306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (!ctx->is_active)
15862306a36Sopenharmony_ci		return;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	ctx->qos_data.def_qos_parm.qos_flags = 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (ctx->qos_data.qos_active)
16362306a36Sopenharmony_ci		ctx->qos_data.def_qos_parm.qos_flags |=
16462306a36Sopenharmony_ci			QOS_PARAM_FLG_UPDATE_EDCA_MSK;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (ctx->ht.enabled)
16762306a36Sopenharmony_ci		ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
17062306a36Sopenharmony_ci		      ctx->qos_data.qos_active,
17162306a36Sopenharmony_ci		      ctx->qos_data.def_qos_parm.qos_flags);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0,
17462306a36Sopenharmony_ci			       sizeof(struct iwl_qosparam_cmd),
17562306a36Sopenharmony_ci			       &ctx->qos_data.def_qos_parm);
17662306a36Sopenharmony_ci	if (ret)
17762306a36Sopenharmony_ci		IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n");
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int iwlagn_update_beacon(struct iwl_priv *priv,
18162306a36Sopenharmony_ci				struct ieee80211_vif *vif)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	dev_kfree_skb(priv->beacon_skb);
18662306a36Sopenharmony_ci	priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif, 0);
18762306a36Sopenharmony_ci	if (!priv->beacon_skb)
18862306a36Sopenharmony_ci		return -ENOMEM;
18962306a36Sopenharmony_ci	return iwlagn_send_beacon_cmd(priv);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int iwlagn_send_rxon_assoc(struct iwl_priv *priv,
19362306a36Sopenharmony_ci				  struct iwl_rxon_context *ctx)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int ret = 0;
19662306a36Sopenharmony_ci	struct iwl_rxon_assoc_cmd rxon_assoc;
19762306a36Sopenharmony_ci	const struct iwl_rxon_cmd *rxon1 = &ctx->staging;
19862306a36Sopenharmony_ci	const struct iwl_rxon_cmd *rxon2 = &ctx->active;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if ((rxon1->flags == rxon2->flags) &&
20162306a36Sopenharmony_ci	    (rxon1->filter_flags == rxon2->filter_flags) &&
20262306a36Sopenharmony_ci	    (rxon1->cck_basic_rates == rxon2->cck_basic_rates) &&
20362306a36Sopenharmony_ci	    (rxon1->ofdm_ht_single_stream_basic_rates ==
20462306a36Sopenharmony_ci	     rxon2->ofdm_ht_single_stream_basic_rates) &&
20562306a36Sopenharmony_ci	    (rxon1->ofdm_ht_dual_stream_basic_rates ==
20662306a36Sopenharmony_ci	     rxon2->ofdm_ht_dual_stream_basic_rates) &&
20762306a36Sopenharmony_ci	    (rxon1->ofdm_ht_triple_stream_basic_rates ==
20862306a36Sopenharmony_ci	     rxon2->ofdm_ht_triple_stream_basic_rates) &&
20962306a36Sopenharmony_ci	    (rxon1->acquisition_data == rxon2->acquisition_data) &&
21062306a36Sopenharmony_ci	    (rxon1->rx_chain == rxon2->rx_chain) &&
21162306a36Sopenharmony_ci	    (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) {
21262306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC.  Not resending.\n");
21362306a36Sopenharmony_ci		return 0;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	rxon_assoc.flags = ctx->staging.flags;
21762306a36Sopenharmony_ci	rxon_assoc.filter_flags = ctx->staging.filter_flags;
21862306a36Sopenharmony_ci	rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates;
21962306a36Sopenharmony_ci	rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates;
22062306a36Sopenharmony_ci	rxon_assoc.reserved1 = 0;
22162306a36Sopenharmony_ci	rxon_assoc.reserved2 = 0;
22262306a36Sopenharmony_ci	rxon_assoc.reserved3 = 0;
22362306a36Sopenharmony_ci	rxon_assoc.ofdm_ht_single_stream_basic_rates =
22462306a36Sopenharmony_ci	    ctx->staging.ofdm_ht_single_stream_basic_rates;
22562306a36Sopenharmony_ci	rxon_assoc.ofdm_ht_dual_stream_basic_rates =
22662306a36Sopenharmony_ci	    ctx->staging.ofdm_ht_dual_stream_basic_rates;
22762306a36Sopenharmony_ci	rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain;
22862306a36Sopenharmony_ci	rxon_assoc.ofdm_ht_triple_stream_basic_rates =
22962306a36Sopenharmony_ci		 ctx->staging.ofdm_ht_triple_stream_basic_rates;
23062306a36Sopenharmony_ci	rxon_assoc.acquisition_data = ctx->staging.acquisition_data;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd,
23362306a36Sopenharmony_ci				CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc);
23462306a36Sopenharmony_ci	return ret;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	u16 new_val;
24062306a36Sopenharmony_ci	u16 beacon_factor;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/*
24362306a36Sopenharmony_ci	 * If mac80211 hasn't given us a beacon interval, program
24462306a36Sopenharmony_ci	 * the default into the device (not checking this here
24562306a36Sopenharmony_ci	 * would cause the adjustment below to return the maximum
24662306a36Sopenharmony_ci	 * value, which may break PAN.)
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	if (!beacon_val)
24962306a36Sopenharmony_ci		return DEFAULT_BEACON_INTERVAL;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/*
25262306a36Sopenharmony_ci	 * If the beacon interval we obtained from the peer
25362306a36Sopenharmony_ci	 * is too large, we'll have to wake up more often
25462306a36Sopenharmony_ci	 * (and in IBSS case, we'll beacon too much)
25562306a36Sopenharmony_ci	 *
25662306a36Sopenharmony_ci	 * For example, if max_beacon_val is 4096, and the
25762306a36Sopenharmony_ci	 * requested beacon interval is 7000, we'll have to
25862306a36Sopenharmony_ci	 * use 3500 to be able to wake up on the beacons.
25962306a36Sopenharmony_ci	 *
26062306a36Sopenharmony_ci	 * This could badly influence beacon detection stats.
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
26462306a36Sopenharmony_ci	new_val = beacon_val / beacon_factor;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!new_val)
26762306a36Sopenharmony_ci		new_val = max_beacon_val;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return new_val;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int iwl_send_rxon_timing(struct iwl_priv *priv,
27362306a36Sopenharmony_ci				struct iwl_rxon_context *ctx)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	u64 tsf;
27662306a36Sopenharmony_ci	s32 interval_tm, rem;
27762306a36Sopenharmony_ci	struct ieee80211_conf *conf = NULL;
27862306a36Sopenharmony_ci	u16 beacon_int;
27962306a36Sopenharmony_ci	struct ieee80211_vif *vif = ctx->vif;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	conf = &priv->hw->conf;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ctx->timing.timestamp = cpu_to_le64(priv->timestamp);
28862306a36Sopenharmony_ci	ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	beacon_int = vif ? vif->bss_conf.beacon_int : 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * TODO: For IBSS we need to get atim_window from mac80211,
29462306a36Sopenharmony_ci	 *	 for now just always use 0
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	ctx->timing.atim_window = 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (ctx->ctxid == IWL_RXON_CTX_PAN &&
29962306a36Sopenharmony_ci	    (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
30062306a36Sopenharmony_ci	    iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
30162306a36Sopenharmony_ci	    priv->contexts[IWL_RXON_CTX_BSS].vif &&
30262306a36Sopenharmony_ci	    priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
30362306a36Sopenharmony_ci		ctx->timing.beacon_interval =
30462306a36Sopenharmony_ci			priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
30562306a36Sopenharmony_ci		beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
30662306a36Sopenharmony_ci	} else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
30762306a36Sopenharmony_ci		   iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
30862306a36Sopenharmony_ci		   priv->contexts[IWL_RXON_CTX_PAN].vif &&
30962306a36Sopenharmony_ci		   priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
31062306a36Sopenharmony_ci		   (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
31162306a36Sopenharmony_ci		    !ctx->vif->bss_conf.beacon_int)) {
31262306a36Sopenharmony_ci		ctx->timing.beacon_interval =
31362306a36Sopenharmony_ci			priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
31462306a36Sopenharmony_ci		beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
31562306a36Sopenharmony_ci	} else {
31662306a36Sopenharmony_ci		beacon_int = iwl_adjust_beacon_interval(beacon_int,
31762306a36Sopenharmony_ci			IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
31862306a36Sopenharmony_ci		ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	ctx->beacon_int = beacon_int;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
32462306a36Sopenharmony_ci	interval_tm = beacon_int * TIME_UNIT;
32562306a36Sopenharmony_ci	rem = do_div(tsf, interval_tm);
32662306a36Sopenharmony_ci	ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv,
33162306a36Sopenharmony_ci			"beacon interval %d beacon timer %d beacon tim %d\n",
33262306a36Sopenharmony_ci			le16_to_cpu(ctx->timing.beacon_interval),
33362306a36Sopenharmony_ci			le32_to_cpu(ctx->timing.beacon_init_val),
33462306a36Sopenharmony_ci			le16_to_cpu(ctx->timing.atim_window));
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
33762306a36Sopenharmony_ci				0, sizeof(ctx->timing), &ctx->timing);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int iwlagn_rxon_disconn(struct iwl_priv *priv,
34162306a36Sopenharmony_ci			       struct iwl_rxon_context *ctx)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	int ret;
34462306a36Sopenharmony_ci	struct iwl_rxon_cmd *active = (void *)&ctx->active;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (ctx->ctxid == IWL_RXON_CTX_BSS) {
34762306a36Sopenharmony_ci		ret = iwlagn_disable_bss(priv, ctx, &ctx->staging);
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		ret = iwlagn_disable_pan(priv, ctx, &ctx->staging);
35062306a36Sopenharmony_ci		if (ret)
35162306a36Sopenharmony_ci			return ret;
35262306a36Sopenharmony_ci		if (ctx->vif) {
35362306a36Sopenharmony_ci			ret = iwl_send_rxon_timing(priv, ctx);
35462306a36Sopenharmony_ci			if (ret) {
35562306a36Sopenharmony_ci				IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
35662306a36Sopenharmony_ci				return ret;
35762306a36Sopenharmony_ci			}
35862306a36Sopenharmony_ci			ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging);
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	if (ret)
36262306a36Sopenharmony_ci		return ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/*
36562306a36Sopenharmony_ci	 * Un-assoc RXON clears the station table and WEP
36662306a36Sopenharmony_ci	 * keys, so we have to restore those afterwards.
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	iwl_clear_ucode_stations(priv, ctx);
36962306a36Sopenharmony_ci	/* update -- might need P2P now */
37062306a36Sopenharmony_ci	iwl_update_bcast_station(priv, ctx);
37162306a36Sopenharmony_ci	iwl_restore_stations(priv, ctx);
37262306a36Sopenharmony_ci	ret = iwl_restore_default_wep_keys(priv, ctx);
37362306a36Sopenharmony_ci	if (ret) {
37462306a36Sopenharmony_ci		IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret);
37562306a36Sopenharmony_ci		return ret;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	memcpy(active, &ctx->staging, sizeof(*active));
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int ret;
38562306a36Sopenharmony_ci	s8 prev_tx_power;
38662306a36Sopenharmony_ci	bool defer;
38762306a36Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED)
39062306a36Sopenharmony_ci		return 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (priv->tx_power_user_lmt == tx_power && !force)
39562306a36Sopenharmony_ci		return 0;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) {
39862306a36Sopenharmony_ci		IWL_WARN(priv,
39962306a36Sopenharmony_ci			 "Requested user TXPOWER %d below lower limit %d.\n",
40062306a36Sopenharmony_ci			 tx_power,
40162306a36Sopenharmony_ci			 IWLAGN_TX_POWER_TARGET_POWER_MIN);
40262306a36Sopenharmony_ci		return -EINVAL;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (tx_power > DIV_ROUND_UP(priv->nvm_data->max_tx_pwr_half_dbm, 2)) {
40662306a36Sopenharmony_ci		IWL_WARN(priv,
40762306a36Sopenharmony_ci			"Requested user TXPOWER %d above upper limit %d.\n",
40862306a36Sopenharmony_ci			 tx_power, priv->nvm_data->max_tx_pwr_half_dbm);
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
41362306a36Sopenharmony_ci		return -EIO;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* scan complete and commit_rxon use tx_power_next value,
41662306a36Sopenharmony_ci	 * it always need to be updated for newest request */
41762306a36Sopenharmony_ci	priv->tx_power_next = tx_power;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* do not set tx power when scanning or channel changing */
42062306a36Sopenharmony_ci	defer = test_bit(STATUS_SCANNING, &priv->status) ||
42162306a36Sopenharmony_ci		memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging));
42262306a36Sopenharmony_ci	if (defer && !force) {
42362306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Deferring tx power set\n");
42462306a36Sopenharmony_ci		return 0;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	prev_tx_power = priv->tx_power_user_lmt;
42862306a36Sopenharmony_ci	priv->tx_power_user_lmt = tx_power;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ret = iwlagn_send_tx_power(priv);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* if fail to set tx_power, restore the orig. tx power */
43362306a36Sopenharmony_ci	if (ret) {
43462306a36Sopenharmony_ci		priv->tx_power_user_lmt = prev_tx_power;
43562306a36Sopenharmony_ci		priv->tx_power_next = prev_tx_power;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	return ret;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int iwlagn_rxon_connect(struct iwl_priv *priv,
44162306a36Sopenharmony_ci			       struct iwl_rxon_context *ctx)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	int ret;
44462306a36Sopenharmony_ci	struct iwl_rxon_cmd *active = (void *)&ctx->active;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* RXON timing must be before associated RXON */
44762306a36Sopenharmony_ci	if (ctx->ctxid == IWL_RXON_CTX_BSS) {
44862306a36Sopenharmony_ci		ret = iwl_send_rxon_timing(priv, ctx);
44962306a36Sopenharmony_ci		if (ret) {
45062306a36Sopenharmony_ci			IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
45162306a36Sopenharmony_ci			return ret;
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci	/* QoS info may be cleared by previous un-assoc RXON */
45562306a36Sopenharmony_ci	iwlagn_update_qos(priv, ctx);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/*
45862306a36Sopenharmony_ci	 * We'll run into this code path when beaconing is
45962306a36Sopenharmony_ci	 * enabled, but then we also need to send the beacon
46062306a36Sopenharmony_ci	 * to the device.
46162306a36Sopenharmony_ci	 */
46262306a36Sopenharmony_ci	if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) {
46362306a36Sopenharmony_ci		ret = iwlagn_update_beacon(priv, ctx->vif);
46462306a36Sopenharmony_ci		if (ret) {
46562306a36Sopenharmony_ci			IWL_ERR(priv,
46662306a36Sopenharmony_ci				"Error sending required beacon (%d)!\n",
46762306a36Sopenharmony_ci				ret);
46862306a36Sopenharmony_ci			return ret;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	priv->start_calib = 0;
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * Apply the new configuration.
47562306a36Sopenharmony_ci	 *
47662306a36Sopenharmony_ci	 * Associated RXON doesn't clear the station table in uCode,
47762306a36Sopenharmony_ci	 * so we don't need to restore stations etc. after this.
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
48062306a36Sopenharmony_ci		      sizeof(struct iwl_rxon_cmd), &ctx->staging);
48162306a36Sopenharmony_ci	if (ret) {
48262306a36Sopenharmony_ci		IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
48362306a36Sopenharmony_ci		return ret;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	memcpy(active, &ctx->staging, sizeof(*active));
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* IBSS beacon needs to be sent after setting assoc */
48862306a36Sopenharmony_ci	if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC))
48962306a36Sopenharmony_ci		if (iwlagn_update_beacon(priv, ctx->vif))
49062306a36Sopenharmony_ci			IWL_ERR(priv, "Error sending IBSS beacon\n");
49162306a36Sopenharmony_ci	iwl_init_sensitivity(priv);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/*
49462306a36Sopenharmony_ci	 * If we issue a new RXON command which required a tune then
49562306a36Sopenharmony_ci	 * we must send a new TXPOWER command or we won't be able to
49662306a36Sopenharmony_ci	 * Tx any frames.
49762306a36Sopenharmony_ci	 *
49862306a36Sopenharmony_ci	 * It's expected we set power here if channel is changing.
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	ret = iwl_set_tx_power(priv, priv->tx_power_next, true);
50162306a36Sopenharmony_ci	if (ret) {
50262306a36Sopenharmony_ci		IWL_ERR(priv, "Error sending TX power (%d)\n", ret);
50362306a36Sopenharmony_ci		return ret;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciint iwlagn_set_pan_params(struct iwl_priv *priv)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct iwl_wipan_params_cmd cmd;
51262306a36Sopenharmony_ci	struct iwl_rxon_context *ctx_bss, *ctx_pan;
51362306a36Sopenharmony_ci	int slot0 = 300, slot1 = 0;
51462306a36Sopenharmony_ci	int ret;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS))
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
52462306a36Sopenharmony_ci	ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/*
52762306a36Sopenharmony_ci	 * If the PAN context is inactive, then we don't need
52862306a36Sopenharmony_ci	 * to update the PAN parameters, the last thing we'll
52962306a36Sopenharmony_ci	 * have done before it goes inactive is making the PAN
53062306a36Sopenharmony_ci	 * parameters be WLAN-only.
53162306a36Sopenharmony_ci	 */
53262306a36Sopenharmony_ci	if (!ctx_pan->is_active)
53362306a36Sopenharmony_ci		return 0;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* only 2 slots are currently allowed */
53862306a36Sopenharmony_ci	cmd.num_slots = 2;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	cmd.slots[0].type = 0; /* BSS */
54162306a36Sopenharmony_ci	cmd.slots[1].type = 1; /* PAN */
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (ctx_bss->vif && ctx_pan->vif) {
54462306a36Sopenharmony_ci		int bcnint = ctx_pan->beacon_int;
54562306a36Sopenharmony_ci		int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		/* should be set, but seems unused?? */
54862306a36Sopenharmony_ci		cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		if (ctx_pan->vif->type == NL80211_IFTYPE_AP &&
55162306a36Sopenharmony_ci		    bcnint &&
55262306a36Sopenharmony_ci		    bcnint != ctx_bss->beacon_int) {
55362306a36Sopenharmony_ci			IWL_ERR(priv,
55462306a36Sopenharmony_ci				"beacon intervals don't match (%d, %d)\n",
55562306a36Sopenharmony_ci				ctx_bss->beacon_int, ctx_pan->beacon_int);
55662306a36Sopenharmony_ci		} else
55762306a36Sopenharmony_ci			bcnint = max_t(int, bcnint,
55862306a36Sopenharmony_ci				       ctx_bss->beacon_int);
55962306a36Sopenharmony_ci		if (!bcnint)
56062306a36Sopenharmony_ci			bcnint = DEFAULT_BEACON_INTERVAL;
56162306a36Sopenharmony_ci		slot0 = bcnint / 2;
56262306a36Sopenharmony_ci		slot1 = bcnint - slot0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		if (test_bit(STATUS_SCAN_HW, &priv->status) ||
56562306a36Sopenharmony_ci		    (!ctx_bss->vif->cfg.idle &&
56662306a36Sopenharmony_ci		     !ctx_bss->vif->cfg.assoc)) {
56762306a36Sopenharmony_ci			slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
56862306a36Sopenharmony_ci			slot1 = IWL_MIN_SLOT_TIME;
56962306a36Sopenharmony_ci		} else if (!ctx_pan->vif->cfg.idle &&
57062306a36Sopenharmony_ci			   !ctx_pan->vif->cfg.assoc) {
57162306a36Sopenharmony_ci			slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME;
57262306a36Sopenharmony_ci			slot0 = IWL_MIN_SLOT_TIME;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci	} else if (ctx_pan->vif) {
57562306a36Sopenharmony_ci		slot0 = 0;
57662306a36Sopenharmony_ci		slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
57762306a36Sopenharmony_ci					ctx_pan->beacon_int;
57862306a36Sopenharmony_ci		slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (test_bit(STATUS_SCAN_HW, &priv->status)) {
58162306a36Sopenharmony_ci			slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME;
58262306a36Sopenharmony_ci			slot1 = IWL_MIN_SLOT_TIME;
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	cmd.slots[0].width = cpu_to_le16(slot0);
58762306a36Sopenharmony_ci	cmd.slots[1].width = cpu_to_le16(slot1);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0,
59062306a36Sopenharmony_ci			sizeof(cmd), &cmd);
59162306a36Sopenharmony_ci	if (ret)
59262306a36Sopenharmony_ci		IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return ret;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic void _iwl_set_rxon_ht(struct iwl_priv *priv,
59862306a36Sopenharmony_ci			     struct iwl_ht_config *ht_conf,
59962306a36Sopenharmony_ci			     struct iwl_rxon_context *ctx)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct iwl_rxon_cmd *rxon = &ctx->staging;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (!ctx->ht.enabled) {
60462306a36Sopenharmony_ci		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
60562306a36Sopenharmony_ci			RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
60662306a36Sopenharmony_ci			RXON_FLG_HT40_PROT_MSK |
60762306a36Sopenharmony_ci			RXON_FLG_HT_PROT_MSK);
60862306a36Sopenharmony_ci		return;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* FIXME: if the definition of ht.protection changed, the "translation"
61262306a36Sopenharmony_ci	 * will be needed for rxon->flags
61362306a36Sopenharmony_ci	 */
61462306a36Sopenharmony_ci	rxon->flags |= cpu_to_le32(ctx->ht.protection <<
61562306a36Sopenharmony_ci				   RXON_FLG_HT_OPERATING_MODE_POS);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* Set up channel bandwidth:
61862306a36Sopenharmony_ci	 * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
61962306a36Sopenharmony_ci	/* clear the HT channel mode before set the mode */
62062306a36Sopenharmony_ci	rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
62162306a36Sopenharmony_ci			 RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
62262306a36Sopenharmony_ci	if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) {
62362306a36Sopenharmony_ci		/* pure ht40 */
62462306a36Sopenharmony_ci		if (ctx->ht.protection ==
62562306a36Sopenharmony_ci		    IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
62662306a36Sopenharmony_ci			rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
62762306a36Sopenharmony_ci			/*
62862306a36Sopenharmony_ci			 * Note: control channel is opposite of extension
62962306a36Sopenharmony_ci			 * channel
63062306a36Sopenharmony_ci			 */
63162306a36Sopenharmony_ci			switch (ctx->ht.extension_chan_offset) {
63262306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
63362306a36Sopenharmony_ci				rxon->flags &=
63462306a36Sopenharmony_ci					~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
63562306a36Sopenharmony_ci				break;
63662306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
63762306a36Sopenharmony_ci				rxon->flags |=
63862306a36Sopenharmony_ci					RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
63962306a36Sopenharmony_ci				break;
64062306a36Sopenharmony_ci			}
64162306a36Sopenharmony_ci		} else {
64262306a36Sopenharmony_ci			/*
64362306a36Sopenharmony_ci			 * Note: control channel is opposite of extension
64462306a36Sopenharmony_ci			 * channel
64562306a36Sopenharmony_ci			 */
64662306a36Sopenharmony_ci			switch (ctx->ht.extension_chan_offset) {
64762306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
64862306a36Sopenharmony_ci				rxon->flags &=
64962306a36Sopenharmony_ci					~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
65062306a36Sopenharmony_ci				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
65162306a36Sopenharmony_ci				break;
65262306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
65362306a36Sopenharmony_ci				rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
65462306a36Sopenharmony_ci				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
65562306a36Sopenharmony_ci				break;
65662306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_NONE:
65762306a36Sopenharmony_ci			default:
65862306a36Sopenharmony_ci				/*
65962306a36Sopenharmony_ci				 * channel location only valid if in Mixed
66062306a36Sopenharmony_ci				 * mode
66162306a36Sopenharmony_ci				 */
66262306a36Sopenharmony_ci				IWL_ERR(priv,
66362306a36Sopenharmony_ci					"invalid extension channel offset\n");
66462306a36Sopenharmony_ci				break;
66562306a36Sopenharmony_ci			}
66662306a36Sopenharmony_ci		}
66762306a36Sopenharmony_ci	} else {
66862306a36Sopenharmony_ci		rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	iwlagn_set_rxon_chain(priv, ctx);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
67462306a36Sopenharmony_ci			"extension channel offset 0x%x\n",
67562306a36Sopenharmony_ci			le32_to_cpu(rxon->flags), ctx->ht.protection,
67662306a36Sopenharmony_ci			ctx->ht.extension_chan_offset);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_civoid iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	for_each_context(priv, ctx)
68462306a36Sopenharmony_ci		_iwl_set_rxon_ht(priv, ht_conf, ctx);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/*
68862306a36Sopenharmony_ci * iwl_set_rxon_channel - Set the band and channel values in staging RXON
68962306a36Sopenharmony_ci * @ch: requested channel as a pointer to struct ieee80211_channel
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci * NOTE:  Does not commit to the hardware; it sets appropriate bit fields
69262306a36Sopenharmony_ci * in the staging RXON flag structure based on the ch->band
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_civoid iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
69562306a36Sopenharmony_ci			 struct iwl_rxon_context *ctx)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	enum nl80211_band band = ch->band;
69862306a36Sopenharmony_ci	u16 channel = ch->hw_value;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if ((le16_to_cpu(ctx->staging.channel) == channel) &&
70162306a36Sopenharmony_ci	    (priv->band == band))
70262306a36Sopenharmony_ci		return;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	ctx->staging.channel = cpu_to_le16(channel);
70562306a36Sopenharmony_ci	if (band == NL80211_BAND_5GHZ)
70662306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK;
70762306a36Sopenharmony_ci	else
70862306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	priv->band = band;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_civoid iwl_set_flags_for_band(struct iwl_priv *priv,
71762306a36Sopenharmony_ci			    struct iwl_rxon_context *ctx,
71862306a36Sopenharmony_ci			    enum nl80211_band band,
71962306a36Sopenharmony_ci			    struct ieee80211_vif *vif)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	if (band == NL80211_BAND_5GHZ) {
72262306a36Sopenharmony_ci		ctx->staging.flags &=
72362306a36Sopenharmony_ci		    ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK
72462306a36Sopenharmony_ci		      | RXON_FLG_CCK_MSK);
72562306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
72662306a36Sopenharmony_ci	} else {
72762306a36Sopenharmony_ci		/* Copied from iwl_post_associate() */
72862306a36Sopenharmony_ci		if (vif && vif->bss_conf.use_short_slot)
72962306a36Sopenharmony_ci			ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
73062306a36Sopenharmony_ci		else
73162306a36Sopenharmony_ci			ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_BAND_24G_MSK;
73462306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK;
73562306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_CCK_MSK;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic void iwl_set_rxon_hwcrypto(struct iwl_priv *priv,
74062306a36Sopenharmony_ci				  struct iwl_rxon_context *ctx, int hw_decrypt)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct iwl_rxon_cmd *rxon = &ctx->staging;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (hw_decrypt)
74562306a36Sopenharmony_ci		rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
74662306a36Sopenharmony_ci	else
74762306a36Sopenharmony_ci		rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci/* validate RXON structure is valid */
75262306a36Sopenharmony_cistatic int iwl_check_rxon_cmd(struct iwl_priv *priv,
75362306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct iwl_rxon_cmd *rxon = &ctx->staging;
75662306a36Sopenharmony_ci	u32 errors = 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
75962306a36Sopenharmony_ci		if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
76062306a36Sopenharmony_ci			IWL_WARN(priv, "check 2.4G: wrong narrow\n");
76162306a36Sopenharmony_ci			errors |= BIT(0);
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci		if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
76462306a36Sopenharmony_ci			IWL_WARN(priv, "check 2.4G: wrong radar\n");
76562306a36Sopenharmony_ci			errors |= BIT(1);
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci	} else {
76862306a36Sopenharmony_ci		if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
76962306a36Sopenharmony_ci			IWL_WARN(priv, "check 5.2G: not short slot!\n");
77062306a36Sopenharmony_ci			errors |= BIT(2);
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci		if (rxon->flags & RXON_FLG_CCK_MSK) {
77362306a36Sopenharmony_ci			IWL_WARN(priv, "check 5.2G: CCK!\n");
77462306a36Sopenharmony_ci			errors |= BIT(3);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci	if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
77862306a36Sopenharmony_ci		IWL_WARN(priv, "mac/bssid mcast!\n");
77962306a36Sopenharmony_ci		errors |= BIT(4);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* make sure basic rates 6Mbps and 1Mbps are supported */
78362306a36Sopenharmony_ci	if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 &&
78462306a36Sopenharmony_ci	    (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) {
78562306a36Sopenharmony_ci		IWL_WARN(priv, "neither 1 nor 6 are basic\n");
78662306a36Sopenharmony_ci		errors |= BIT(5);
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (le16_to_cpu(rxon->assoc_id) > 2007) {
79062306a36Sopenharmony_ci		IWL_WARN(priv, "aid > 2007\n");
79162306a36Sopenharmony_ci		errors |= BIT(6);
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
79562306a36Sopenharmony_ci			== (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
79662306a36Sopenharmony_ci		IWL_WARN(priv, "CCK and short slot\n");
79762306a36Sopenharmony_ci		errors |= BIT(7);
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
80162306a36Sopenharmony_ci			== (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
80262306a36Sopenharmony_ci		IWL_WARN(priv, "CCK and auto detect\n");
80362306a36Sopenharmony_ci		errors |= BIT(8);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
80762306a36Sopenharmony_ci			    RXON_FLG_TGG_PROTECT_MSK)) ==
80862306a36Sopenharmony_ci			    RXON_FLG_TGG_PROTECT_MSK) {
80962306a36Sopenharmony_ci		IWL_WARN(priv, "TGg but no auto-detect\n");
81062306a36Sopenharmony_ci		errors |= BIT(9);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (rxon->channel == 0) {
81462306a36Sopenharmony_ci		IWL_WARN(priv, "zero channel is invalid\n");
81562306a36Sopenharmony_ci		errors |= BIT(10);
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	WARN(errors, "Invalid RXON (%#x), channel %d",
81962306a36Sopenharmony_ci	     errors, le16_to_cpu(rxon->channel));
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return errors ? -EINVAL : 0;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci/*
82562306a36Sopenharmony_ci * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
82662306a36Sopenharmony_ci * @priv: staging_rxon is compared to active_rxon
82762306a36Sopenharmony_ci *
82862306a36Sopenharmony_ci * If the RXON structure is changing enough to require a new tune,
82962306a36Sopenharmony_ci * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
83062306a36Sopenharmony_ci * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
83162306a36Sopenharmony_ci */
83262306a36Sopenharmony_cistatic int iwl_full_rxon_required(struct iwl_priv *priv,
83362306a36Sopenharmony_ci				  struct iwl_rxon_context *ctx)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	const struct iwl_rxon_cmd *staging = &ctx->staging;
83662306a36Sopenharmony_ci	const struct iwl_rxon_cmd *active = &ctx->active;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci#define CHK(cond)							\
83962306a36Sopenharmony_ci	if ((cond)) {							\
84062306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n");	\
84162306a36Sopenharmony_ci		return 1;						\
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci#define CHK_NEQ(c1, c2)						\
84562306a36Sopenharmony_ci	if ((c1) != (c2)) {					\
84662306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "need full RXON - "	\
84762306a36Sopenharmony_ci			       #c1 " != " #c2 " - %d != %d\n",	\
84862306a36Sopenharmony_ci			       (c1), (c2));			\
84962306a36Sopenharmony_ci		return 1;					\
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* These items are only settable from the full RXON command */
85362306a36Sopenharmony_ci	CHK(!iwl_is_associated_ctx(ctx));
85462306a36Sopenharmony_ci	CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr));
85562306a36Sopenharmony_ci	CHK(!ether_addr_equal(staging->node_addr, active->node_addr));
85662306a36Sopenharmony_ci	CHK(!ether_addr_equal(staging->wlap_bssid_addr,
85762306a36Sopenharmony_ci			      active->wlap_bssid_addr));
85862306a36Sopenharmony_ci	CHK_NEQ(staging->dev_type, active->dev_type);
85962306a36Sopenharmony_ci	CHK_NEQ(staging->channel, active->channel);
86062306a36Sopenharmony_ci	CHK_NEQ(staging->air_propagation, active->air_propagation);
86162306a36Sopenharmony_ci	CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
86262306a36Sopenharmony_ci		active->ofdm_ht_single_stream_basic_rates);
86362306a36Sopenharmony_ci	CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
86462306a36Sopenharmony_ci		active->ofdm_ht_dual_stream_basic_rates);
86562306a36Sopenharmony_ci	CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates,
86662306a36Sopenharmony_ci		active->ofdm_ht_triple_stream_basic_rates);
86762306a36Sopenharmony_ci	CHK_NEQ(staging->assoc_id, active->assoc_id);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
87062306a36Sopenharmony_ci	 * be updated with the RXON_ASSOC command -- however only some
87162306a36Sopenharmony_ci	 * flag transitions are allowed using RXON_ASSOC */
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Check if we are not switching bands */
87462306a36Sopenharmony_ci	CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
87562306a36Sopenharmony_ci		active->flags & RXON_FLG_BAND_24G_MSK);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Check if we are switching association toggle */
87862306a36Sopenharmony_ci	CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
87962306a36Sopenharmony_ci		active->filter_flags & RXON_FILTER_ASSOC_MSK);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci#undef CHK
88262306a36Sopenharmony_ci#undef CHK_NEQ
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return 0;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG
88862306a36Sopenharmony_civoid iwl_print_rx_config_cmd(struct iwl_priv *priv,
88962306a36Sopenharmony_ci			     enum iwl_rxon_context_id ctxid)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[ctxid];
89262306a36Sopenharmony_ci	struct iwl_rxon_cmd *rxon = &ctx->staging;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "RX CONFIG:\n");
89562306a36Sopenharmony_ci	iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
89662306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n",
89762306a36Sopenharmony_ci			le16_to_cpu(rxon->channel));
89862306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n",
89962306a36Sopenharmony_ci			le32_to_cpu(rxon->flags));
90062306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n",
90162306a36Sopenharmony_ci			le32_to_cpu(rxon->filter_flags));
90262306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type);
90362306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n",
90462306a36Sopenharmony_ci			rxon->ofdm_basic_rates);
90562306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n",
90662306a36Sopenharmony_ci			rxon->cck_basic_rates);
90762306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr);
90862306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
90962306a36Sopenharmony_ci	IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n",
91062306a36Sopenharmony_ci			le16_to_cpu(rxon->assoc_id));
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci#endif
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic void iwl_calc_basic_rates(struct iwl_priv *priv,
91562306a36Sopenharmony_ci				 struct iwl_rxon_context *ctx)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	int lowest_present_ofdm = 100;
91862306a36Sopenharmony_ci	int lowest_present_cck = 100;
91962306a36Sopenharmony_ci	u8 cck = 0;
92062306a36Sopenharmony_ci	u8 ofdm = 0;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (ctx->vif) {
92362306a36Sopenharmony_ci		struct ieee80211_supported_band *sband;
92462306a36Sopenharmony_ci		unsigned long basic = ctx->vif->bss_conf.basic_rates;
92562306a36Sopenharmony_ci		int i;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		sband = priv->hw->wiphy->bands[priv->hw->conf.chandef.chan->band];
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		for_each_set_bit(i, &basic, BITS_PER_LONG) {
93062306a36Sopenharmony_ci			int hw = sband->bitrates[i].hw_value;
93162306a36Sopenharmony_ci			if (hw >= IWL_FIRST_OFDM_RATE) {
93262306a36Sopenharmony_ci				ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
93362306a36Sopenharmony_ci				if (lowest_present_ofdm > hw)
93462306a36Sopenharmony_ci					lowest_present_ofdm = hw;
93562306a36Sopenharmony_ci			} else {
93662306a36Sopenharmony_ci				BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci				cck |= BIT(hw);
93962306a36Sopenharmony_ci				if (lowest_present_cck > hw)
94062306a36Sopenharmony_ci					lowest_present_cck = hw;
94162306a36Sopenharmony_ci			}
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/*
94662306a36Sopenharmony_ci	 * Now we've got the basic rates as bitmaps in the ofdm and cck
94762306a36Sopenharmony_ci	 * variables. This isn't sufficient though, as there might not
94862306a36Sopenharmony_ci	 * be all the right rates in the bitmap. E.g. if the only basic
94962306a36Sopenharmony_ci	 * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
95062306a36Sopenharmony_ci	 * and 6 Mbps because the 802.11-2007 standard says in 9.6:
95162306a36Sopenharmony_ci	 *
95262306a36Sopenharmony_ci	 *    [...] a STA responding to a received frame shall transmit
95362306a36Sopenharmony_ci	 *    its Control Response frame [...] at the highest rate in the
95462306a36Sopenharmony_ci	 *    BSSBasicRateSet parameter that is less than or equal to the
95562306a36Sopenharmony_ci	 *    rate of the immediately previous frame in the frame exchange
95662306a36Sopenharmony_ci	 *    sequence ([...]) and that is of the same modulation class
95762306a36Sopenharmony_ci	 *    ([...]) as the received frame. If no rate contained in the
95862306a36Sopenharmony_ci	 *    BSSBasicRateSet parameter meets these conditions, then the
95962306a36Sopenharmony_ci	 *    control frame sent in response to a received frame shall be
96062306a36Sopenharmony_ci	 *    transmitted at the highest mandatory rate of the PHY that is
96162306a36Sopenharmony_ci	 *    less than or equal to the rate of the received frame, and
96262306a36Sopenharmony_ci	 *    that is of the same modulation class as the received frame.
96362306a36Sopenharmony_ci	 *
96462306a36Sopenharmony_ci	 * As a consequence, we need to add all mandatory rates that are
96562306a36Sopenharmony_ci	 * lower than all of the basic rates to these bitmaps.
96662306a36Sopenharmony_ci	 */
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
96962306a36Sopenharmony_ci		ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE;
97062306a36Sopenharmony_ci	if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
97162306a36Sopenharmony_ci		ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE;
97262306a36Sopenharmony_ci	/* 6M already there or needed so always add */
97362306a36Sopenharmony_ci	ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/*
97662306a36Sopenharmony_ci	 * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
97762306a36Sopenharmony_ci	 * Note, however:
97862306a36Sopenharmony_ci	 *  - if no CCK rates are basic, it must be ERP since there must
97962306a36Sopenharmony_ci	 *    be some basic rates at all, so they're OFDM => ERP PHY
98062306a36Sopenharmony_ci	 *    (or we're in 5 GHz, and the cck bitmap will never be used)
98162306a36Sopenharmony_ci	 *  - if 11M is a basic rate, it must be ERP as well, so add 5.5M
98262306a36Sopenharmony_ci	 *  - if 5.5M is basic, 1M and 2M are mandatory
98362306a36Sopenharmony_ci	 *  - if 2M is basic, 1M is mandatory
98462306a36Sopenharmony_ci	 *  - if 1M is basic, that's the only valid ACK rate.
98562306a36Sopenharmony_ci	 * As a consequence, it's not as complicated as it sounds, just add
98662306a36Sopenharmony_ci	 * any lower rates to the ACK rate bitmap.
98762306a36Sopenharmony_ci	 */
98862306a36Sopenharmony_ci	if (IWL_RATE_11M_INDEX < lowest_present_cck)
98962306a36Sopenharmony_ci		cck |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE;
99062306a36Sopenharmony_ci	if (IWL_RATE_5M_INDEX < lowest_present_cck)
99162306a36Sopenharmony_ci		cck |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE;
99262306a36Sopenharmony_ci	if (IWL_RATE_2M_INDEX < lowest_present_cck)
99362306a36Sopenharmony_ci		cck |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE;
99462306a36Sopenharmony_ci	/* 1M already there or needed so always add */
99562306a36Sopenharmony_ci	cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n",
99862306a36Sopenharmony_ci		       cck, ofdm);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/* "basic_rates" is a misnomer here -- should be called ACK rates */
100162306a36Sopenharmony_ci	ctx->staging.cck_basic_rates = cck;
100262306a36Sopenharmony_ci	ctx->staging.ofdm_basic_rates = ofdm;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci/*
100662306a36Sopenharmony_ci * iwlagn_commit_rxon - commit staging_rxon to hardware
100762306a36Sopenharmony_ci *
100862306a36Sopenharmony_ci * The RXON command in staging_rxon is committed to the hardware and
100962306a36Sopenharmony_ci * the active_rxon structure is updated with the new data.  This
101062306a36Sopenharmony_ci * function correctly transitions out of the RXON_ASSOC_MSK state if
101162306a36Sopenharmony_ci * a HW tune is required based on the RXON structure changes.
101262306a36Sopenharmony_ci *
101362306a36Sopenharmony_ci * The connect/disconnect flow should be as the following:
101462306a36Sopenharmony_ci *
101562306a36Sopenharmony_ci * 1. make sure send RXON command with association bit unset if not connect
101662306a36Sopenharmony_ci *	this should include the channel and the band for the candidate
101762306a36Sopenharmony_ci *	to be connected to
101862306a36Sopenharmony_ci * 2. Add Station before RXON association with the AP
101962306a36Sopenharmony_ci * 3. RXON_timing has to send before RXON for connection
102062306a36Sopenharmony_ci * 4. full RXON command - associated bit set
102162306a36Sopenharmony_ci * 5. use RXON_ASSOC command to update any flags changes
102262306a36Sopenharmony_ci */
102362306a36Sopenharmony_ciint iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	/* cast away the const for active_rxon in this function */
102662306a36Sopenharmony_ci	struct iwl_rxon_cmd *active = (void *)&ctx->active;
102762306a36Sopenharmony_ci	bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
102862306a36Sopenharmony_ci	int ret;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (!iwl_is_alive(priv))
103362306a36Sopenharmony_ci		return -EBUSY;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* This function hardcodes a bunch of dual-mode assumptions */
103662306a36Sopenharmony_ci	BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!ctx->is_active)
103962306a36Sopenharmony_ci		return 0;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* always get timestamp with Rx frame */
104262306a36Sopenharmony_ci	ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* recalculate basic rates */
104562306a36Sopenharmony_ci	iwl_calc_basic_rates(priv, ctx);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/*
104862306a36Sopenharmony_ci	 * force CTS-to-self frames protection if RTS-CTS is not preferred
104962306a36Sopenharmony_ci	 * one aggregation protection method
105062306a36Sopenharmony_ci	 */
105162306a36Sopenharmony_ci	if (!priv->hw_params.use_rts_for_aggregation)
105262306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
105562306a36Sopenharmony_ci	    !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
105662306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
105762306a36Sopenharmony_ci	else
105862306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	iwl_print_rx_config_cmd(priv, ctx->ctxid);
106162306a36Sopenharmony_ci	ret = iwl_check_rxon_cmd(priv, ctx);
106262306a36Sopenharmony_ci	if (ret) {
106362306a36Sopenharmony_ci		IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n");
106462306a36Sopenharmony_ci		return -EINVAL;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/*
106862306a36Sopenharmony_ci	 * receive commit_rxon request
106962306a36Sopenharmony_ci	 * abort any previous channel switch if still in process
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
107262306a36Sopenharmony_ci	    (priv->switch_channel != ctx->staging.channel)) {
107362306a36Sopenharmony_ci		IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
107462306a36Sopenharmony_ci			      le16_to_cpu(priv->switch_channel));
107562306a36Sopenharmony_ci		iwl_chswitch_done(priv, false);
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/*
107962306a36Sopenharmony_ci	 * If we don't need to send a full RXON, we can use
108062306a36Sopenharmony_ci	 * iwl_rxon_assoc_cmd which is used to reconfigure filter
108162306a36Sopenharmony_ci	 * and other flags for the current radio configuration.
108262306a36Sopenharmony_ci	 */
108362306a36Sopenharmony_ci	if (!iwl_full_rxon_required(priv, ctx)) {
108462306a36Sopenharmony_ci		ret = iwlagn_send_rxon_assoc(priv, ctx);
108562306a36Sopenharmony_ci		if (ret) {
108662306a36Sopenharmony_ci			IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret);
108762306a36Sopenharmony_ci			return ret;
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		memcpy(active, &ctx->staging, sizeof(*active));
109162306a36Sopenharmony_ci		/*
109262306a36Sopenharmony_ci		 * We do not commit tx power settings while channel changing,
109362306a36Sopenharmony_ci		 * do it now if after settings changed.
109462306a36Sopenharmony_ci		 */
109562306a36Sopenharmony_ci		iwl_set_tx_power(priv, priv->tx_power_next, false);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		/* make sure we are in the right PS state */
109862306a36Sopenharmony_ci		iwl_power_update_mode(priv, true);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		return 0;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.swcrypto);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv,
110662306a36Sopenharmony_ci		       "Going to commit RXON\n"
110762306a36Sopenharmony_ci		       "  * with%s RXON_FILTER_ASSOC_MSK\n"
110862306a36Sopenharmony_ci		       "  * channel = %d\n"
110962306a36Sopenharmony_ci		       "  * bssid = %pM\n",
111062306a36Sopenharmony_ci		       (new_assoc ? "" : "out"),
111162306a36Sopenharmony_ci		       le16_to_cpu(ctx->staging.channel),
111262306a36Sopenharmony_ci		       ctx->staging.bssid_addr);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/*
111562306a36Sopenharmony_ci	 * Always clear associated first, but with the correct config.
111662306a36Sopenharmony_ci	 * This is required as for example station addition for the
111762306a36Sopenharmony_ci	 * AP station must be done after the BSSID is set to correctly
111862306a36Sopenharmony_ci	 * set up filters in the device.
111962306a36Sopenharmony_ci	 */
112062306a36Sopenharmony_ci	ret = iwlagn_rxon_disconn(priv, ctx);
112162306a36Sopenharmony_ci	if (ret)
112262306a36Sopenharmony_ci		return ret;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	ret = iwlagn_set_pan_params(priv);
112562306a36Sopenharmony_ci	if (ret)
112662306a36Sopenharmony_ci		return ret;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (new_assoc)
112962306a36Sopenharmony_ci		return iwlagn_rxon_connect(priv, ctx);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	return 0;
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_civoid iwlagn_config_ht40(struct ieee80211_conf *conf,
113562306a36Sopenharmony_ci			struct iwl_rxon_context *ctx)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	if (conf_is_ht40_minus(conf)) {
113862306a36Sopenharmony_ci		ctx->ht.extension_chan_offset =
113962306a36Sopenharmony_ci			IEEE80211_HT_PARAM_CHA_SEC_BELOW;
114062306a36Sopenharmony_ci		ctx->ht.is_40mhz = true;
114162306a36Sopenharmony_ci	} else if (conf_is_ht40_plus(conf)) {
114262306a36Sopenharmony_ci		ctx->ht.extension_chan_offset =
114362306a36Sopenharmony_ci			IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
114462306a36Sopenharmony_ci		ctx->ht.is_40mhz = true;
114562306a36Sopenharmony_ci	} else {
114662306a36Sopenharmony_ci		ctx->ht.extension_chan_offset =
114762306a36Sopenharmony_ci			IEEE80211_HT_PARAM_CHA_SEC_NONE;
114862306a36Sopenharmony_ci		ctx->ht.is_40mhz = false;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ciint iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
115562306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
115662306a36Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
115762306a36Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
115862306a36Sopenharmony_ci	int ret = 0;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
116562306a36Sopenharmony_ci		IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
116662306a36Sopenharmony_ci		goto out;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!iwl_is_ready(priv)) {
117062306a36Sopenharmony_ci		IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
117162306a36Sopenharmony_ci		goto out;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (changed & (IEEE80211_CONF_CHANGE_SMPS |
117562306a36Sopenharmony_ci		       IEEE80211_CONF_CHANGE_CHANNEL)) {
117662306a36Sopenharmony_ci		/* mac80211 uses static for non-HT which is what we want */
117762306a36Sopenharmony_ci		priv->current_ht_config.smps = conf->smps_mode;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		/*
118062306a36Sopenharmony_ci		 * Recalculate chain counts.
118162306a36Sopenharmony_ci		 *
118262306a36Sopenharmony_ci		 * If monitor mode is enabled then mac80211 will
118362306a36Sopenharmony_ci		 * set up the SM PS mode to OFF if an HT channel is
118462306a36Sopenharmony_ci		 * configured.
118562306a36Sopenharmony_ci		 */
118662306a36Sopenharmony_ci		for_each_context(priv, ctx)
118762306a36Sopenharmony_ci			iwlagn_set_rxon_chain(priv, ctx);
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
119162306a36Sopenharmony_ci		for_each_context(priv, ctx) {
119262306a36Sopenharmony_ci			/* Configure HT40 channels */
119362306a36Sopenharmony_ci			if (ctx->ht.enabled != conf_is_ht(conf))
119462306a36Sopenharmony_ci				ctx->ht.enabled = conf_is_ht(conf);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci			if (ctx->ht.enabled) {
119762306a36Sopenharmony_ci				/* if HT40 is used, it should not change
119862306a36Sopenharmony_ci				 * after associated except channel switch */
119962306a36Sopenharmony_ci				if (!ctx->ht.is_40mhz ||
120062306a36Sopenharmony_ci						!iwl_is_associated_ctx(ctx))
120162306a36Sopenharmony_ci					iwlagn_config_ht40(conf, ctx);
120262306a36Sopenharmony_ci			} else
120362306a36Sopenharmony_ci				ctx->ht.is_40mhz = false;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci			/*
120662306a36Sopenharmony_ci			 * Default to no protection. Protection mode will
120762306a36Sopenharmony_ci			 * later be set from BSS config in iwl_ht_conf
120862306a36Sopenharmony_ci			 */
120962306a36Sopenharmony_ci			ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci			/* if we are switching from ht to 2.4 clear flags
121262306a36Sopenharmony_ci			 * from any ht related info since 2.4 does not
121362306a36Sopenharmony_ci			 * support ht */
121462306a36Sopenharmony_ci			if (le16_to_cpu(ctx->staging.channel) !=
121562306a36Sopenharmony_ci			    channel->hw_value)
121662306a36Sopenharmony_ci				ctx->staging.flags = 0;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci			iwl_set_rxon_channel(priv, channel, ctx);
121962306a36Sopenharmony_ci			iwl_set_rxon_ht(priv, &priv->current_ht_config);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci			iwl_set_flags_for_band(priv, ctx, channel->band,
122262306a36Sopenharmony_ci					       ctx->vif);
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci		iwl_update_bcast_stations(priv);
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (changed & (IEEE80211_CONF_CHANGE_PS |
122962306a36Sopenharmony_ci			IEEE80211_CONF_CHANGE_IDLE)) {
123062306a36Sopenharmony_ci		ret = iwl_power_update_mode(priv, false);
123162306a36Sopenharmony_ci		if (ret)
123262306a36Sopenharmony_ci			IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_POWER) {
123662306a36Sopenharmony_ci		IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
123762306a36Sopenharmony_ci			priv->tx_power_user_lmt, conf->power_level);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		iwl_set_tx_power(priv, conf->power_level, false);
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	for_each_context(priv, ctx) {
124362306a36Sopenharmony_ci		if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
124462306a36Sopenharmony_ci			continue;
124562306a36Sopenharmony_ci		iwlagn_commit_rxon(priv, ctx);
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci out:
124862306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
124962306a36Sopenharmony_ci	IWL_DEBUG_MAC80211(priv, "leave\n");
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	return ret;
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic void iwlagn_check_needed_chains(struct iwl_priv *priv,
125562306a36Sopenharmony_ci				       struct iwl_rxon_context *ctx,
125662306a36Sopenharmony_ci				       struct ieee80211_bss_conf *bss_conf)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	struct ieee80211_vif *vif = ctx->vif;
125962306a36Sopenharmony_ci	struct iwl_rxon_context *tmp;
126062306a36Sopenharmony_ci	struct ieee80211_sta *sta;
126162306a36Sopenharmony_ci	struct iwl_ht_config *ht_conf = &priv->current_ht_config;
126262306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap *ht_cap;
126362306a36Sopenharmony_ci	bool need_multiple;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	switch (vif->type) {
126862306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
126962306a36Sopenharmony_ci		rcu_read_lock();
127062306a36Sopenharmony_ci		sta = ieee80211_find_sta(vif, bss_conf->bssid);
127162306a36Sopenharmony_ci		if (!sta) {
127262306a36Sopenharmony_ci			/*
127362306a36Sopenharmony_ci			 * If at all, this can only happen through a race
127462306a36Sopenharmony_ci			 * when the AP disconnects us while we're still
127562306a36Sopenharmony_ci			 * setting up the connection, in that case mac80211
127662306a36Sopenharmony_ci			 * will soon tell us about that.
127762306a36Sopenharmony_ci			 */
127862306a36Sopenharmony_ci			need_multiple = false;
127962306a36Sopenharmony_ci			rcu_read_unlock();
128062306a36Sopenharmony_ci			break;
128162306a36Sopenharmony_ci		}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		ht_cap = &sta->deflink.ht_cap;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		need_multiple = true;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		/*
128862306a36Sopenharmony_ci		 * If the peer advertises no support for receiving 2 and 3
128962306a36Sopenharmony_ci		 * stream MCS rates, it can't be transmitting them either.
129062306a36Sopenharmony_ci		 */
129162306a36Sopenharmony_ci		if (ht_cap->mcs.rx_mask[1] == 0 &&
129262306a36Sopenharmony_ci		    ht_cap->mcs.rx_mask[2] == 0) {
129362306a36Sopenharmony_ci			need_multiple = false;
129462306a36Sopenharmony_ci		} else if (!(ht_cap->mcs.tx_params &
129562306a36Sopenharmony_ci						IEEE80211_HT_MCS_TX_DEFINED)) {
129662306a36Sopenharmony_ci			/* If it can't TX MCS at all ... */
129762306a36Sopenharmony_ci			need_multiple = false;
129862306a36Sopenharmony_ci		} else if (ht_cap->mcs.tx_params &
129962306a36Sopenharmony_ci						IEEE80211_HT_MCS_TX_RX_DIFF) {
130062306a36Sopenharmony_ci			int maxstreams;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci			/*
130362306a36Sopenharmony_ci			 * But if it can receive them, it might still not
130462306a36Sopenharmony_ci			 * be able to transmit them, which is what we need
130562306a36Sopenharmony_ci			 * to check here -- so check the number of streams
130662306a36Sopenharmony_ci			 * it advertises for TX (if different from RX).
130762306a36Sopenharmony_ci			 */
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci			maxstreams = (ht_cap->mcs.tx_params &
131062306a36Sopenharmony_ci				 IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK);
131162306a36Sopenharmony_ci			maxstreams >>=
131262306a36Sopenharmony_ci				IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
131362306a36Sopenharmony_ci			maxstreams += 1;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci			if (maxstreams <= 1)
131662306a36Sopenharmony_ci				need_multiple = false;
131762306a36Sopenharmony_ci		}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		rcu_read_unlock();
132062306a36Sopenharmony_ci		break;
132162306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
132262306a36Sopenharmony_ci		/* currently */
132362306a36Sopenharmony_ci		need_multiple = false;
132462306a36Sopenharmony_ci		break;
132562306a36Sopenharmony_ci	default:
132662306a36Sopenharmony_ci		/* only AP really */
132762306a36Sopenharmony_ci		need_multiple = true;
132862306a36Sopenharmony_ci		break;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ctx->ht_need_multiple_chains = need_multiple;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (!need_multiple) {
133462306a36Sopenharmony_ci		/* check all contexts */
133562306a36Sopenharmony_ci		for_each_context(priv, tmp) {
133662306a36Sopenharmony_ci			if (!tmp->vif)
133762306a36Sopenharmony_ci				continue;
133862306a36Sopenharmony_ci			if (tmp->ht_need_multiple_chains) {
133962306a36Sopenharmony_ci				need_multiple = true;
134062306a36Sopenharmony_ci				break;
134162306a36Sopenharmony_ci			}
134262306a36Sopenharmony_ci		}
134362306a36Sopenharmony_ci	}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	ht_conf->single_chain_sufficient = !need_multiple;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic void iwlagn_chain_noise_reset(struct iwl_priv *priv)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	struct iwl_chain_noise_data *data = &priv->chain_noise_data;
135162306a36Sopenharmony_ci	int ret;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
135462306a36Sopenharmony_ci		return;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if ((data->state == IWL_CHAIN_NOISE_ALIVE) &&
135762306a36Sopenharmony_ci	    iwl_is_any_associated(priv)) {
135862306a36Sopenharmony_ci		struct iwl_calib_chain_noise_reset_cmd cmd;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		/* clear data for chain noise calibration algorithm */
136162306a36Sopenharmony_ci		data->chain_noise_a = 0;
136262306a36Sopenharmony_ci		data->chain_noise_b = 0;
136362306a36Sopenharmony_ci		data->chain_noise_c = 0;
136462306a36Sopenharmony_ci		data->chain_signal_a = 0;
136562306a36Sopenharmony_ci		data->chain_signal_b = 0;
136662306a36Sopenharmony_ci		data->chain_signal_c = 0;
136762306a36Sopenharmony_ci		data->beacon_count = 0;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci		memset(&cmd, 0, sizeof(cmd));
137062306a36Sopenharmony_ci		iwl_set_calib_hdr(&cmd.hdr,
137162306a36Sopenharmony_ci			priv->phy_calib_chain_noise_reset_cmd);
137262306a36Sopenharmony_ci		ret = iwl_dvm_send_cmd_pdu(priv,
137362306a36Sopenharmony_ci					REPLY_PHY_CALIBRATION_CMD,
137462306a36Sopenharmony_ci					0, sizeof(cmd), &cmd);
137562306a36Sopenharmony_ci		if (ret)
137662306a36Sopenharmony_ci			IWL_ERR(priv,
137762306a36Sopenharmony_ci				"Could not send REPLY_PHY_CALIBRATION_CMD\n");
137862306a36Sopenharmony_ci		data->state = IWL_CHAIN_NOISE_ACCUMULATE;
137962306a36Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n");
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_civoid iwlagn_bss_info_changed(struct ieee80211_hw *hw,
138462306a36Sopenharmony_ci			     struct ieee80211_vif *vif,
138562306a36Sopenharmony_ci			     struct ieee80211_bss_conf *bss_conf,
138662306a36Sopenharmony_ci			     u64 changes)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
138962306a36Sopenharmony_ci	struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
139062306a36Sopenharmony_ci	int ret;
139162306a36Sopenharmony_ci	bool force = false;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (changes & BSS_CHANGED_IDLE && vif->cfg.idle) {
139662306a36Sopenharmony_ci		/*
139762306a36Sopenharmony_ci		 * If we go idle, then clearly no "passive-no-rx"
139862306a36Sopenharmony_ci		 * workaround is needed any more, this is a reset.
139962306a36Sopenharmony_ci		 */
140062306a36Sopenharmony_ci		iwlagn_lift_passive_no_rx(priv);
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	if (unlikely(!iwl_is_ready(priv))) {
140462306a36Sopenharmony_ci		IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
140562306a36Sopenharmony_ci		mutex_unlock(&priv->mutex);
140662306a36Sopenharmony_ci		return;
140762306a36Sopenharmony_ci        }
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	if (unlikely(!ctx->vif)) {
141062306a36Sopenharmony_ci		IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n");
141162306a36Sopenharmony_ci		mutex_unlock(&priv->mutex);
141262306a36Sopenharmony_ci		return;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON_INT)
141662306a36Sopenharmony_ci		force = true;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	if (changes & BSS_CHANGED_QOS) {
141962306a36Sopenharmony_ci		ctx->qos_data.qos_active = bss_conf->qos;
142062306a36Sopenharmony_ci		iwlagn_update_qos(priv, ctx);
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	ctx->staging.assoc_id = cpu_to_le16(vif->cfg.aid);
142462306a36Sopenharmony_ci	if (vif->bss_conf.use_short_preamble)
142562306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
142662306a36Sopenharmony_ci	else
142762306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC) {
143062306a36Sopenharmony_ci		if (vif->cfg.assoc) {
143162306a36Sopenharmony_ci			priv->timestamp = bss_conf->sync_tsf;
143262306a36Sopenharmony_ci			ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
143362306a36Sopenharmony_ci		} else {
143462306a36Sopenharmony_ci			ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci			if (ctx->ctxid == IWL_RXON_CTX_BSS)
143762306a36Sopenharmony_ci				priv->have_rekey_data = false;
143862306a36Sopenharmony_ci		}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		iwlagn_bt_coex_rssi_monitor(priv);
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (ctx->ht.enabled) {
144462306a36Sopenharmony_ci		ctx->ht.protection = bss_conf->ht_operation_mode &
144562306a36Sopenharmony_ci					IEEE80211_HT_OP_MODE_PROTECTION;
144662306a36Sopenharmony_ci		ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode &
144762306a36Sopenharmony_ci					IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
144862306a36Sopenharmony_ci		iwlagn_check_needed_chains(priv, ctx, bss_conf);
144962306a36Sopenharmony_ci		iwl_set_rxon_ht(priv, &priv->current_ht_config);
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	iwlagn_set_rxon_chain(priv, ctx);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (bss_conf->use_cts_prot && (priv->band != NL80211_BAND_5GHZ))
145562306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
145662306a36Sopenharmony_ci	else
145762306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	if (bss_conf->use_cts_prot)
146062306a36Sopenharmony_ci		ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
146162306a36Sopenharmony_ci	else
146262306a36Sopenharmony_ci		ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP ||
146762306a36Sopenharmony_ci	    vif->type == NL80211_IFTYPE_ADHOC) {
146862306a36Sopenharmony_ci		if (vif->bss_conf.enable_beacon) {
146962306a36Sopenharmony_ci			ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
147062306a36Sopenharmony_ci			priv->beacon_ctx = ctx;
147162306a36Sopenharmony_ci		} else {
147262306a36Sopenharmony_ci			ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
147362306a36Sopenharmony_ci			priv->beacon_ctx = NULL;
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	/*
147862306a36Sopenharmony_ci	 * If the ucode decides to do beacon filtering before
147962306a36Sopenharmony_ci	 * association, it will lose beacons that are needed
148062306a36Sopenharmony_ci	 * before sending frames out on passive channels. This
148162306a36Sopenharmony_ci	 * causes association failures on those channels. Enable
148262306a36Sopenharmony_ci	 * receiving beacons in such cases.
148362306a36Sopenharmony_ci	 */
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION) {
148662306a36Sopenharmony_ci		if (!vif->cfg.assoc)
148762306a36Sopenharmony_ci			ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK;
148862306a36Sopenharmony_ci		else
148962306a36Sopenharmony_ci			ctx->staging.filter_flags &=
149062306a36Sopenharmony_ci						    ~RXON_FILTER_BCON_AWARE_MSK;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
149462306a36Sopenharmony_ci		iwlagn_commit_rxon(priv, ctx);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
149762306a36Sopenharmony_ci		/*
149862306a36Sopenharmony_ci		 * The chain noise calibration will enable PM upon
149962306a36Sopenharmony_ci		 * completion. If calibration has already been run
150062306a36Sopenharmony_ci		 * then we need to enable power management here.
150162306a36Sopenharmony_ci		 */
150262306a36Sopenharmony_ci		if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
150362306a36Sopenharmony_ci			iwl_power_update_mode(priv, false);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		/* Enable RX differential gain and sensitivity calibrations */
150662306a36Sopenharmony_ci		iwlagn_chain_noise_reset(priv);
150762306a36Sopenharmony_ci		priv->start_calib = 1;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (changes & BSS_CHANGED_IBSS) {
151162306a36Sopenharmony_ci		ret = iwlagn_manage_ibss_station(priv, vif,
151262306a36Sopenharmony_ci						 vif->cfg.ibss_joined);
151362306a36Sopenharmony_ci		if (ret)
151462306a36Sopenharmony_ci			IWL_ERR(priv, "failed to %s IBSS station %pM\n",
151562306a36Sopenharmony_ci				vif->cfg.ibss_joined ? "add" : "remove",
151662306a36Sopenharmony_ci				bss_conf->bssid);
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) {
152062306a36Sopenharmony_ci		if (iwlagn_update_beacon(priv, vif))
152162306a36Sopenharmony_ci			IWL_ERR(priv, "Error updating beacon\n");
152262306a36Sopenharmony_ci	}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_civoid iwlagn_post_scan(struct iwl_priv *priv)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/*
153262306a36Sopenharmony_ci	 * We do not commit power settings while scan is pending,
153362306a36Sopenharmony_ci	 * do it now if the settings changed.
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci	iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false);
153662306a36Sopenharmony_ci	iwl_set_tx_power(priv, priv->tx_power_next, false);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/*
153962306a36Sopenharmony_ci	 * Since setting the RXON may have been deferred while
154062306a36Sopenharmony_ci	 * performing the scan, fire one off if needed
154162306a36Sopenharmony_ci	 */
154262306a36Sopenharmony_ci	for_each_context(priv, ctx)
154362306a36Sopenharmony_ci		if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
154462306a36Sopenharmony_ci			iwlagn_commit_rxon(priv, ctx);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	iwlagn_set_pan_params(priv);
154762306a36Sopenharmony_ci}
1548