162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright(c) 2003 - 2014, 2022 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well
762306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files.
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <net/mac80211.h>
1162306a36Sopenharmony_ci#include "iwl-trans.h"
1262306a36Sopenharmony_ci#include "dev.h"
1362306a36Sopenharmony_ci#include "agn.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciconst u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	lockdep_assert_held(&priv->sta_lock);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (sta_id >= IWLAGN_STATION_COUNT) {
2262306a36Sopenharmony_ci		IWL_ERR(priv, "invalid sta_id %u\n", sta_id);
2362306a36Sopenharmony_ci		return -EINVAL;
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci	if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
2662306a36Sopenharmony_ci		IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u "
2762306a36Sopenharmony_ci			"addr %pM\n",
2862306a36Sopenharmony_ci			sta_id, priv->stations[sta_id].sta.sta.addr);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) {
3162306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv,
3262306a36Sopenharmony_ci				"STA id %u addr %pM already present in uCode "
3362306a36Sopenharmony_ci				"(according to driver)\n",
3462306a36Sopenharmony_ci				sta_id, priv->stations[sta_id].sta.sta.addr);
3562306a36Sopenharmony_ci	} else {
3662306a36Sopenharmony_ci		priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
3762306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n",
3862306a36Sopenharmony_ci				sta_id, priv->stations[sta_id].sta.sta.addr);
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void iwl_process_add_sta_resp(struct iwl_priv *priv,
4462306a36Sopenharmony_ci				     struct iwl_rx_packet *pkt)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Processing response for adding station\n");
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	switch (add_sta_resp->status) {
5362306a36Sopenharmony_ci	case ADD_STA_SUCCESS_MSK:
5462306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
5562306a36Sopenharmony_ci		break;
5662306a36Sopenharmony_ci	case ADD_STA_NO_ROOM_IN_TABLE:
5762306a36Sopenharmony_ci		IWL_ERR(priv, "Adding station failed, no room in table.\n");
5862306a36Sopenharmony_ci		break;
5962306a36Sopenharmony_ci	case ADD_STA_NO_BLOCK_ACK_RESOURCE:
6062306a36Sopenharmony_ci		IWL_ERR(priv,
6162306a36Sopenharmony_ci			"Adding station failed, no block ack resource.\n");
6262306a36Sopenharmony_ci		break;
6362306a36Sopenharmony_ci	case ADD_STA_MODIFY_NON_EXIST_STA:
6462306a36Sopenharmony_ci		IWL_ERR(priv, "Attempting to modify non-existing station\n");
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	default:
6762306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
6862306a36Sopenharmony_ci				add_sta_resp->status);
6962306a36Sopenharmony_ci		break;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	iwl_process_add_sta_resp(priv, pkt);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint iwl_send_add_sta(struct iwl_priv *priv,
8362306a36Sopenharmony_ci		     struct iwl_addsta_cmd *sta, u8 flags)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int ret = 0;
8662306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
8762306a36Sopenharmony_ci		.id = REPLY_ADD_STA,
8862306a36Sopenharmony_ci		.flags = flags,
8962306a36Sopenharmony_ci		.data = { sta, },
9062306a36Sopenharmony_ci		.len = { sizeof(*sta), },
9162306a36Sopenharmony_ci	};
9262306a36Sopenharmony_ci	u8 sta_id __maybe_unused = sta->sta.sta_id;
9362306a36Sopenharmony_ci	struct iwl_rx_packet *pkt;
9462306a36Sopenharmony_ci	struct iwl_add_sta_resp *add_sta_resp;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
9762306a36Sopenharmony_ci		       sta_id, sta->sta.addr, flags & CMD_ASYNC ?  "a" : "");
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!(flags & CMD_ASYNC)) {
10062306a36Sopenharmony_ci		cmd.flags |= CMD_WANT_SKB;
10162306a36Sopenharmony_ci		might_sleep();
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd(priv, &cmd);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (ret || (flags & CMD_ASYNC))
10762306a36Sopenharmony_ci		return ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	pkt = cmd.resp_pkt;
11062306a36Sopenharmony_ci	add_sta_resp = (void *)pkt->data;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* debug messages are printed in the handler */
11362306a36Sopenharmony_ci	if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) {
11462306a36Sopenharmony_ci		spin_lock_bh(&priv->sta_lock);
11562306a36Sopenharmony_ci		ret = iwl_sta_ucode_activate(priv, sta_id);
11662306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
11762306a36Sopenharmony_ci	} else {
11862306a36Sopenharmony_ci		ret = -EIO;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	iwl_free_resp(&cmd);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return ret;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cibool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
12762306a36Sopenharmony_ci			    struct iwl_rxon_context *ctx,
12862306a36Sopenharmony_ci			    struct ieee80211_sta *sta)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (!ctx->ht.enabled || !ctx->ht.is_40mhz)
13162306a36Sopenharmony_ci		return false;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
13462306a36Sopenharmony_ci	if (priv->disable_ht40)
13562306a36Sopenharmony_ci		return false;
13662306a36Sopenharmony_ci#endif
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* special case for RXON */
13962306a36Sopenharmony_ci	if (!sta)
14062306a36Sopenharmony_ci		return true;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void iwl_sta_calc_ht_flags(struct iwl_priv *priv,
14662306a36Sopenharmony_ci				  struct ieee80211_sta *sta,
14762306a36Sopenharmony_ci				  struct iwl_rxon_context *ctx,
14862306a36Sopenharmony_ci				  __le32 *flags, __le32 *mask)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->deflink.ht_cap;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	*mask = STA_FLG_RTS_MIMO_PROT_MSK |
15362306a36Sopenharmony_ci		STA_FLG_MIMO_DIS_MSK |
15462306a36Sopenharmony_ci		STA_FLG_HT40_EN_MSK |
15562306a36Sopenharmony_ci		STA_FLG_MAX_AGG_SIZE_MSK |
15662306a36Sopenharmony_ci		STA_FLG_AGG_MPDU_DENSITY_MSK;
15762306a36Sopenharmony_ci	*flags = 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!sta || !sta_ht_inf->ht_supported)
16062306a36Sopenharmony_ci		return;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n",
16362306a36Sopenharmony_ci			sta->addr,
16462306a36Sopenharmony_ci			(sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) ?
16562306a36Sopenharmony_ci			"static" :
16662306a36Sopenharmony_ci			(sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC) ?
16762306a36Sopenharmony_ci			"dynamic" : "disabled");
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	switch (sta->deflink.smps_mode) {
17062306a36Sopenharmony_ci	case IEEE80211_SMPS_STATIC:
17162306a36Sopenharmony_ci		*flags |= STA_FLG_MIMO_DIS_MSK;
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case IEEE80211_SMPS_DYNAMIC:
17462306a36Sopenharmony_ci		*flags |= STA_FLG_RTS_MIMO_PROT_MSK;
17562306a36Sopenharmony_ci		break;
17662306a36Sopenharmony_ci	case IEEE80211_SMPS_OFF:
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	default:
17962306a36Sopenharmony_ci		IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->deflink.smps_mode);
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	*flags |= cpu_to_le32(
18462306a36Sopenharmony_ci		(u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	*flags |= cpu_to_le32(
18762306a36Sopenharmony_ci		(u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (iwl_is_ht40_tx_allowed(priv, ctx, sta))
19062306a36Sopenharmony_ci		*flags |= STA_FLG_HT40_EN_MSK;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciint iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
19462306a36Sopenharmony_ci		      struct ieee80211_sta *sta)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	u8 sta_id = iwl_sta_id(sta);
19762306a36Sopenharmony_ci	__le32 flags, mask;
19862306a36Sopenharmony_ci	struct iwl_addsta_cmd cmd;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
20162306a36Sopenharmony_ci		return -EINVAL;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
20662306a36Sopenharmony_ci	priv->stations[sta_id].sta.station_flags &= ~mask;
20762306a36Sopenharmony_ci	priv->stations[sta_id].sta.station_flags |= flags;
20862306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
21162306a36Sopenharmony_ci	cmd.mode = STA_CONTROL_MODIFY_MSK;
21262306a36Sopenharmony_ci	cmd.station_flags_msk = mask;
21362306a36Sopenharmony_ci	cmd.station_flags = flags;
21462306a36Sopenharmony_ci	cmd.sta.sta_id = sta_id;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &cmd, 0);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
22062306a36Sopenharmony_ci				   struct ieee80211_sta *sta,
22162306a36Sopenharmony_ci				   struct iwl_rxon_context *ctx)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	__le32 flags, mask;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	lockdep_assert_held(&priv->sta_lock);
22862306a36Sopenharmony_ci	priv->stations[index].sta.station_flags &= ~mask;
22962306a36Sopenharmony_ci	priv->stations[index].sta.station_flags |= flags;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/*
23362306a36Sopenharmony_ci * iwl_prep_station - Prepare station information for addition
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * should be called with sta_lock held
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_ciu8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
23862306a36Sopenharmony_ci		    const u8 *addr, bool is_ap, struct ieee80211_sta *sta)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct iwl_station_entry *station;
24162306a36Sopenharmony_ci	int i;
24262306a36Sopenharmony_ci	u8 sta_id = IWL_INVALID_STATION;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (is_ap)
24562306a36Sopenharmony_ci		sta_id = ctx->ap_sta_id;
24662306a36Sopenharmony_ci	else if (is_broadcast_ether_addr(addr))
24762306a36Sopenharmony_ci		sta_id = ctx->bcast_sta_id;
24862306a36Sopenharmony_ci	else
24962306a36Sopenharmony_ci		for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) {
25062306a36Sopenharmony_ci			if (ether_addr_equal(priv->stations[i].sta.sta.addr,
25162306a36Sopenharmony_ci					     addr)) {
25262306a36Sopenharmony_ci				sta_id = i;
25362306a36Sopenharmony_ci				break;
25462306a36Sopenharmony_ci			}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci			if (!priv->stations[i].used &&
25762306a36Sopenharmony_ci			    sta_id == IWL_INVALID_STATION)
25862306a36Sopenharmony_ci				sta_id = i;
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * These two conditions have the same outcome, but keep them
26362306a36Sopenharmony_ci	 * separate
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	if (unlikely(sta_id == IWL_INVALID_STATION))
26662306a36Sopenharmony_ci		return sta_id;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*
26962306a36Sopenharmony_ci	 * uCode is not able to deal with multiple requests to add a
27062306a36Sopenharmony_ci	 * station. Keep track if one is in progress so that we do not send
27162306a36Sopenharmony_ci	 * another.
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
27462306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "STA %d already in process of being "
27562306a36Sopenharmony_ci			       "added.\n", sta_id);
27662306a36Sopenharmony_ci		return sta_id;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
28062306a36Sopenharmony_ci	    (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
28162306a36Sopenharmony_ci	    ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) {
28262306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
28362306a36Sopenharmony_ci				"adding again.\n", sta_id, addr);
28462306a36Sopenharmony_ci		return sta_id;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	station = &priv->stations[sta_id];
28862306a36Sopenharmony_ci	station->used = IWL_STA_DRIVER_ACTIVE;
28962306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
29062306a36Sopenharmony_ci			sta_id, addr);
29162306a36Sopenharmony_ci	priv->num_stations++;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Set up the REPLY_ADD_STA command to send to device */
29462306a36Sopenharmony_ci	memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
29562306a36Sopenharmony_ci	memcpy(station->sta.sta.addr, addr, ETH_ALEN);
29662306a36Sopenharmony_ci	station->sta.mode = 0;
29762306a36Sopenharmony_ci	station->sta.sta.sta_id = sta_id;
29862306a36Sopenharmony_ci	station->sta.station_flags = ctx->station_flags;
29962306a36Sopenharmony_ci	station->ctxid = ctx->ctxid;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (sta) {
30262306a36Sopenharmony_ci		struct iwl_station_priv *sta_priv;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		sta_priv = (void *)sta->drv_priv;
30562306a36Sopenharmony_ci		sta_priv->ctx = ctx;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/*
30962306a36Sopenharmony_ci	 * OK to call unconditionally, since local stations (IBSS BSSID
31062306a36Sopenharmony_ci	 * STA and broadcast STA) pass in a NULL sta, and mac80211
31162306a36Sopenharmony_ci	 * doesn't allow HT IBSS.
31262306a36Sopenharmony_ci	 */
31362306a36Sopenharmony_ci	iwl_set_ht_add_station(priv, sta_id, sta, ctx);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return sta_id;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci#define STA_WAIT_TIMEOUT (HZ/2)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/*
32262306a36Sopenharmony_ci * iwl_add_station_common -
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_ciint iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
32562306a36Sopenharmony_ci			   const u8 *addr, bool is_ap,
32662306a36Sopenharmony_ci			   struct ieee80211_sta *sta, u8 *sta_id_r)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	int ret = 0;
32962306a36Sopenharmony_ci	u8 sta_id;
33062306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	*sta_id_r = 0;
33362306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
33462306a36Sopenharmony_ci	sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta);
33562306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION) {
33662306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
33762306a36Sopenharmony_ci			addr);
33862306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
33962306a36Sopenharmony_ci		return -EINVAL;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * uCode is not able to deal with multiple requests to add a
34462306a36Sopenharmony_ci	 * station. Keep track if one is in progress so that we do not send
34562306a36Sopenharmony_ci	 * another.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
34862306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "STA %d already in process of being "
34962306a36Sopenharmony_ci			       "added.\n", sta_id);
35062306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
35162306a36Sopenharmony_ci		return -EEXIST;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
35562306a36Sopenharmony_ci	    (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
35662306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not "
35762306a36Sopenharmony_ci				"adding again.\n", sta_id, addr);
35862306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
35962306a36Sopenharmony_ci		return -EEXIST;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
36362306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta,
36462306a36Sopenharmony_ci	       sizeof(struct iwl_addsta_cmd));
36562306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* Add station to device's station table */
36862306a36Sopenharmony_ci	ret = iwl_send_add_sta(priv, &sta_cmd, 0);
36962306a36Sopenharmony_ci	if (ret) {
37062306a36Sopenharmony_ci		spin_lock_bh(&priv->sta_lock);
37162306a36Sopenharmony_ci		IWL_ERR(priv, "Adding station %pM failed.\n",
37262306a36Sopenharmony_ci			priv->stations[sta_id].sta.sta.addr);
37362306a36Sopenharmony_ci		priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
37462306a36Sopenharmony_ci		priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
37562306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	*sta_id_r = sta_id;
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/*
38262306a36Sopenharmony_ci * iwl_sta_ucode_deactivate - deactivate ucode status for a station
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	lockdep_assert_held(&priv->sta_lock);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Ucode must be active and driver must be non active */
38962306a36Sopenharmony_ci	if ((priv->stations[sta_id].used &
39062306a36Sopenharmony_ci	     (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) !=
39162306a36Sopenharmony_ci	      IWL_STA_UCODE_ACTIVE)
39262306a36Sopenharmony_ci		IWL_ERR(priv, "removed non active STA %u\n", sta_id);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
39762306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int iwl_send_remove_station(struct iwl_priv *priv,
40162306a36Sopenharmony_ci				   const u8 *addr, int sta_id,
40262306a36Sopenharmony_ci				   bool temporary)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct iwl_rx_packet *pkt;
40562306a36Sopenharmony_ci	int ret;
40662306a36Sopenharmony_ci	struct iwl_rem_sta_cmd rm_sta_cmd;
40762306a36Sopenharmony_ci	struct iwl_rem_sta_resp *rem_sta_resp;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
41062306a36Sopenharmony_ci		.id = REPLY_REMOVE_STA,
41162306a36Sopenharmony_ci		.len = { sizeof(struct iwl_rem_sta_cmd), },
41262306a36Sopenharmony_ci		.data = { &rm_sta_cmd, },
41362306a36Sopenharmony_ci	};
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
41662306a36Sopenharmony_ci	rm_sta_cmd.num_sta = 1;
41762306a36Sopenharmony_ci	memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	cmd.flags |= CMD_WANT_SKB;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ret = iwl_dvm_send_cmd(priv, &cmd);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (ret)
42462306a36Sopenharmony_ci		return ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	pkt = cmd.resp_pkt;
42762306a36Sopenharmony_ci	rem_sta_resp = (void *)pkt->data;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	switch (rem_sta_resp->status) {
43062306a36Sopenharmony_ci	case REM_STA_SUCCESS_MSK:
43162306a36Sopenharmony_ci		if (!temporary) {
43262306a36Sopenharmony_ci			spin_lock_bh(&priv->sta_lock);
43362306a36Sopenharmony_ci			iwl_sta_ucode_deactivate(priv, sta_id);
43462306a36Sopenharmony_ci			spin_unlock_bh(&priv->sta_lock);
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci		IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
43762306a36Sopenharmony_ci		break;
43862306a36Sopenharmony_ci	default:
43962306a36Sopenharmony_ci		ret = -EIO;
44062306a36Sopenharmony_ci		IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	iwl_free_resp(&cmd);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return ret;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/*
45062306a36Sopenharmony_ci * iwl_remove_station - Remove driver's knowledge of station.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_ciint iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
45362306a36Sopenharmony_ci		       const u8 *addr)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	u8 tid;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (!iwl_is_ready(priv)) {
45862306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv,
45962306a36Sopenharmony_ci			"Unable to remove station %pM, device not ready.\n",
46062306a36Sopenharmony_ci			addr);
46162306a36Sopenharmony_ci		/*
46262306a36Sopenharmony_ci		 * It is typical for stations to be removed when we are
46362306a36Sopenharmony_ci		 * going down. Return success since device will be down
46462306a36Sopenharmony_ci		 * soon anyway
46562306a36Sopenharmony_ci		 */
46662306a36Sopenharmony_ci		return 0;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d  %pM\n",
47062306a36Sopenharmony_ci			sta_id, addr);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (WARN_ON(sta_id == IWL_INVALID_STATION))
47362306a36Sopenharmony_ci		return -EINVAL;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
47862306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
47962306a36Sopenharmony_ci				addr);
48062306a36Sopenharmony_ci		goto out_err;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
48462306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
48562306a36Sopenharmony_ci				addr);
48662306a36Sopenharmony_ci		goto out_err;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
49062306a36Sopenharmony_ci		kfree(priv->stations[sta_id].lq);
49162306a36Sopenharmony_ci		priv->stations[sta_id].lq = NULL;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
49562306a36Sopenharmony_ci		memset(&priv->tid_data[sta_id][tid], 0,
49662306a36Sopenharmony_ci			sizeof(priv->tid_data[sta_id][tid]));
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	priv->num_stations--;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (WARN_ON(priv->num_stations < 0))
50362306a36Sopenharmony_ci		priv->num_stations = 0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return iwl_send_remove_station(priv, addr, sta_id, false);
50862306a36Sopenharmony_ciout_err:
50962306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
51062306a36Sopenharmony_ci	return -EINVAL;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_civoid iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id,
51462306a36Sopenharmony_ci			    const u8 *addr)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	u8 tid;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (!iwl_is_ready(priv)) {
51962306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv,
52062306a36Sopenharmony_ci			"Unable to remove station %pM, device not ready.\n",
52162306a36Sopenharmony_ci			addr);
52262306a36Sopenharmony_ci		return;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
52862306a36Sopenharmony_ci		return;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE));
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
53562306a36Sopenharmony_ci		memset(&priv->tid_data[sta_id][tid], 0,
53662306a36Sopenharmony_ci			sizeof(priv->tid_data[sta_id][tid]));
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
53962306a36Sopenharmony_ci	priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	priv->num_stations--;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (WARN_ON_ONCE(priv->num_stations < 0))
54462306a36Sopenharmony_ci		priv->num_stations = 0;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
55062306a36Sopenharmony_ci			    u8 sta_id, struct iwl_link_quality_cmd *link_cmd)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	int i, r;
55362306a36Sopenharmony_ci	u32 rate_flags = 0;
55462306a36Sopenharmony_ci	__le32 rate_n_flags;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	memset(link_cmd, 0, sizeof(*link_cmd));
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Set up the rate scaling to start at selected rate, fall back
56162306a36Sopenharmony_ci	 * all the way down to 1M in IEEE order, and then spin on 1M */
56262306a36Sopenharmony_ci	if (priv->band == NL80211_BAND_5GHZ)
56362306a36Sopenharmony_ci		r = IWL_RATE_6M_INDEX;
56462306a36Sopenharmony_ci	else if (ctx && ctx->vif && ctx->vif->p2p)
56562306a36Sopenharmony_ci		r = IWL_RATE_6M_INDEX;
56662306a36Sopenharmony_ci	else
56762306a36Sopenharmony_ci		r = IWL_RATE_1M_INDEX;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
57062306a36Sopenharmony_ci		rate_flags |= RATE_MCS_CCK_MSK;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) <<
57362306a36Sopenharmony_ci				RATE_MCS_ANT_POS;
57462306a36Sopenharmony_ci	rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
57562306a36Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
57662306a36Sopenharmony_ci		link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	link_cmd->general_params.single_stream_ant_msk =
57962306a36Sopenharmony_ci			first_antenna(priv->nvm_data->valid_tx_ant);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	link_cmd->general_params.dual_stream_ant_msk =
58262306a36Sopenharmony_ci		priv->nvm_data->valid_tx_ant &
58362306a36Sopenharmony_ci		~first_antenna(priv->nvm_data->valid_tx_ant);
58462306a36Sopenharmony_ci	if (!link_cmd->general_params.dual_stream_ant_msk) {
58562306a36Sopenharmony_ci		link_cmd->general_params.dual_stream_ant_msk = ANT_AB;
58662306a36Sopenharmony_ci	} else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) {
58762306a36Sopenharmony_ci		link_cmd->general_params.dual_stream_ant_msk =
58862306a36Sopenharmony_ci			priv->nvm_data->valid_tx_ant;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	link_cmd->agg_params.agg_dis_start_th =
59262306a36Sopenharmony_ci		LINK_QUAL_AGG_DISABLE_START_DEF;
59362306a36Sopenharmony_ci	link_cmd->agg_params.agg_time_limit =
59462306a36Sopenharmony_ci		cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	link_cmd->sta_id = sta_id;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/*
60062306a36Sopenharmony_ci * iwl_clear_ucode_stations - clear ucode station table bits
60162306a36Sopenharmony_ci *
60262306a36Sopenharmony_ci * This function clears all the bits in the driver indicating
60362306a36Sopenharmony_ci * which stations are active in the ucode. Call when something
60462306a36Sopenharmony_ci * other than explicit station management would cause this in
60562306a36Sopenharmony_ci * the ucode, e.g. unassociated RXON.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_civoid iwl_clear_ucode_stations(struct iwl_priv *priv,
60862306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	int i;
61162306a36Sopenharmony_ci	bool cleared = false;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n");
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
61662306a36Sopenharmony_ci	for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
61762306a36Sopenharmony_ci		if (ctx && ctx->ctxid != priv->stations[i].ctxid)
61862306a36Sopenharmony_ci			continue;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
62162306a36Sopenharmony_ci			IWL_DEBUG_INFO(priv,
62262306a36Sopenharmony_ci				"Clearing ucode active for station %d\n", i);
62362306a36Sopenharmony_ci			priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
62462306a36Sopenharmony_ci			cleared = true;
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (!cleared)
63062306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv,
63162306a36Sopenharmony_ci			       "No active stations found to be cleared\n");
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/*
63562306a36Sopenharmony_ci * iwl_restore_stations() - Restore driver known stations to device
63662306a36Sopenharmony_ci *
63762306a36Sopenharmony_ci * All stations considered active by driver, but not present in ucode, is
63862306a36Sopenharmony_ci * restored.
63962306a36Sopenharmony_ci *
64062306a36Sopenharmony_ci * Function sleeps.
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_civoid iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
64562306a36Sopenharmony_ci	static const struct iwl_link_quality_cmd zero_lq = {};
64662306a36Sopenharmony_ci	struct iwl_link_quality_cmd lq;
64762306a36Sopenharmony_ci	int i;
64862306a36Sopenharmony_ci	bool found = false;
64962306a36Sopenharmony_ci	int ret;
65062306a36Sopenharmony_ci	bool send_lq;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (!iwl_is_ready(priv)) {
65362306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv,
65462306a36Sopenharmony_ci			       "Not ready yet, not restoring any stations.\n");
65562306a36Sopenharmony_ci		return;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
65962306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
66062306a36Sopenharmony_ci	for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
66162306a36Sopenharmony_ci		if (ctx->ctxid != priv->stations[i].ctxid)
66262306a36Sopenharmony_ci			continue;
66362306a36Sopenharmony_ci		if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
66462306a36Sopenharmony_ci			    !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
66562306a36Sopenharmony_ci			IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
66662306a36Sopenharmony_ci					priv->stations[i].sta.sta.addr);
66762306a36Sopenharmony_ci			priv->stations[i].sta.mode = 0;
66862306a36Sopenharmony_ci			priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
66962306a36Sopenharmony_ci			found = true;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
67462306a36Sopenharmony_ci		if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
67562306a36Sopenharmony_ci			memcpy(&sta_cmd, &priv->stations[i].sta,
67662306a36Sopenharmony_ci			       sizeof(struct iwl_addsta_cmd));
67762306a36Sopenharmony_ci			send_lq = false;
67862306a36Sopenharmony_ci			if (priv->stations[i].lq) {
67962306a36Sopenharmony_ci				if (priv->wowlan)
68062306a36Sopenharmony_ci					iwl_sta_fill_lq(priv, ctx, i, &lq);
68162306a36Sopenharmony_ci				else
68262306a36Sopenharmony_ci					memcpy(&lq, priv->stations[i].lq,
68362306a36Sopenharmony_ci					       sizeof(struct iwl_link_quality_cmd));
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci				if (memcmp(&lq, &zero_lq, sizeof(lq)))
68662306a36Sopenharmony_ci					send_lq = true;
68762306a36Sopenharmony_ci			}
68862306a36Sopenharmony_ci			spin_unlock_bh(&priv->sta_lock);
68962306a36Sopenharmony_ci			ret = iwl_send_add_sta(priv, &sta_cmd, 0);
69062306a36Sopenharmony_ci			if (ret) {
69162306a36Sopenharmony_ci				spin_lock_bh(&priv->sta_lock);
69262306a36Sopenharmony_ci				IWL_ERR(priv, "Adding station %pM failed.\n",
69362306a36Sopenharmony_ci					priv->stations[i].sta.sta.addr);
69462306a36Sopenharmony_ci				priv->stations[i].used &=
69562306a36Sopenharmony_ci						~IWL_STA_DRIVER_ACTIVE;
69662306a36Sopenharmony_ci				priv->stations[i].used &=
69762306a36Sopenharmony_ci						~IWL_STA_UCODE_INPROGRESS;
69862306a36Sopenharmony_ci				continue;
69962306a36Sopenharmony_ci			}
70062306a36Sopenharmony_ci			/*
70162306a36Sopenharmony_ci			 * Rate scaling has already been initialized, send
70262306a36Sopenharmony_ci			 * current LQ command
70362306a36Sopenharmony_ci			 */
70462306a36Sopenharmony_ci			if (send_lq)
70562306a36Sopenharmony_ci				iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
70662306a36Sopenharmony_ci			spin_lock_bh(&priv->sta_lock);
70762306a36Sopenharmony_ci			priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
71262306a36Sopenharmony_ci	if (!found)
71362306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
71462306a36Sopenharmony_ci			"no stations to be restored.\n");
71562306a36Sopenharmony_ci	else
71662306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Restoring all known stations .... "
71762306a36Sopenharmony_ci			"complete.\n");
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciint iwl_get_free_ucode_key_offset(struct iwl_priv *priv)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	int i;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	for (i = 0; i < priv->sta_key_max_num; i++)
72562306a36Sopenharmony_ci		if (!test_and_set_bit(i, &priv->ucode_key_table))
72662306a36Sopenharmony_ci			return i;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return WEP_INVALID_OFFSET;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_civoid iwl_dealloc_bcast_stations(struct iwl_priv *priv)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	int i;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
73662306a36Sopenharmony_ci	for (i = 0; i < IWLAGN_STATION_COUNT; i++) {
73762306a36Sopenharmony_ci		if (!(priv->stations[i].used & IWL_STA_BCAST))
73862306a36Sopenharmony_ci			continue;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
74162306a36Sopenharmony_ci		priv->num_stations--;
74262306a36Sopenharmony_ci		if (WARN_ON(priv->num_stations < 0))
74362306a36Sopenharmony_ci			priv->num_stations = 0;
74462306a36Sopenharmony_ci		kfree(priv->stations[i].lq);
74562306a36Sopenharmony_ci		priv->stations[i].lq = NULL;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG
75162306a36Sopenharmony_cistatic void iwl_dump_lq_cmd(struct iwl_priv *priv,
75262306a36Sopenharmony_ci			   struct iwl_link_quality_cmd *lq)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	int i;
75562306a36Sopenharmony_ci	IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id);
75662306a36Sopenharmony_ci	IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n",
75762306a36Sopenharmony_ci		       lq->general_params.single_stream_ant_msk,
75862306a36Sopenharmony_ci		       lq->general_params.dual_stream_ant_msk);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
76162306a36Sopenharmony_ci		IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n",
76262306a36Sopenharmony_ci			       i, lq->rs_table[i].rate_n_flags);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci#else
76562306a36Sopenharmony_cistatic inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
76662306a36Sopenharmony_ci				   struct iwl_link_quality_cmd *lq)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci#endif
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/*
77262306a36Sopenharmony_ci * is_lq_table_valid() - Test one aspect of LQ cmd for validity
77362306a36Sopenharmony_ci *
77462306a36Sopenharmony_ci * It sometimes happens when a HT rate has been in use and we
77562306a36Sopenharmony_ci * loose connectivity with AP then mac80211 will first tell us that the
77662306a36Sopenharmony_ci * current channel is not HT anymore before removing the station. In such a
77762306a36Sopenharmony_ci * scenario the RXON flags will be updated to indicate we are not
77862306a36Sopenharmony_ci * communicating HT anymore, but the LQ command may still contain HT rates.
77962306a36Sopenharmony_ci * Test for this to prevent driver from sending LQ command between the time
78062306a36Sopenharmony_ci * RXON flags are updated and when LQ command is updated.
78162306a36Sopenharmony_ci */
78262306a36Sopenharmony_cistatic bool is_lq_table_valid(struct iwl_priv *priv,
78362306a36Sopenharmony_ci			      struct iwl_rxon_context *ctx,
78462306a36Sopenharmony_ci			      struct iwl_link_quality_cmd *lq)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	int i;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (ctx->ht.enabled)
78962306a36Sopenharmony_ci		return true;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n",
79262306a36Sopenharmony_ci		       ctx->active.channel);
79362306a36Sopenharmony_ci	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
79462306a36Sopenharmony_ci		if (le32_to_cpu(lq->rs_table[i].rate_n_flags) &
79562306a36Sopenharmony_ci		    RATE_MCS_HT_MSK) {
79662306a36Sopenharmony_ci			IWL_DEBUG_INFO(priv,
79762306a36Sopenharmony_ci				       "index %d of LQ expects HT channel\n",
79862306a36Sopenharmony_ci				       i);
79962306a36Sopenharmony_ci			return false;
80062306a36Sopenharmony_ci		}
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci	return true;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/*
80662306a36Sopenharmony_ci * iwl_send_lq_cmd() - Send link quality command
80762306a36Sopenharmony_ci * @init: This command is sent as part of station initialization right
80862306a36Sopenharmony_ci *        after station has been added.
80962306a36Sopenharmony_ci *
81062306a36Sopenharmony_ci * The link quality command is sent as the last step of station creation.
81162306a36Sopenharmony_ci * This is the special case in which init is set and we call a callback in
81262306a36Sopenharmony_ci * this case to clear the state indicating that station creation is in
81362306a36Sopenharmony_ci * progress.
81462306a36Sopenharmony_ci */
81562306a36Sopenharmony_ciint iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
81662306a36Sopenharmony_ci		    struct iwl_link_quality_cmd *lq, u8 flags, bool init)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	int ret = 0;
81962306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
82062306a36Sopenharmony_ci		.id = REPLY_TX_LINK_QUALITY_CMD,
82162306a36Sopenharmony_ci		.len = { sizeof(struct iwl_link_quality_cmd), },
82262306a36Sopenharmony_ci		.flags = flags,
82362306a36Sopenharmony_ci		.data = { lq, },
82462306a36Sopenharmony_ci	};
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
82762306a36Sopenharmony_ci		return -EINVAL;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
83162306a36Sopenharmony_ci	if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
83262306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
83362306a36Sopenharmony_ci		return -EINVAL;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	iwl_dump_lq_cmd(priv, lq);
83862306a36Sopenharmony_ci	if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (is_lq_table_valid(priv, ctx, lq))
84262306a36Sopenharmony_ci		ret = iwl_dvm_send_cmd(priv, &cmd);
84362306a36Sopenharmony_ci	else
84462306a36Sopenharmony_ci		ret = -EINVAL;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (cmd.flags & CMD_ASYNC)
84762306a36Sopenharmony_ci		return ret;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	if (init) {
85062306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "init LQ command complete, "
85162306a36Sopenharmony_ci			       "clearing sta addition status for sta %d\n",
85262306a36Sopenharmony_ci			       lq->sta_id);
85362306a36Sopenharmony_ci		spin_lock_bh(&priv->sta_lock);
85462306a36Sopenharmony_ci		priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
85562306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci	return ret;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic struct iwl_link_quality_cmd *
86262306a36Sopenharmony_ciiwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
86362306a36Sopenharmony_ci		 u8 sta_id)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct iwl_link_quality_cmd *link_cmd;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
86862306a36Sopenharmony_ci	if (!link_cmd) {
86962306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n");
87062306a36Sopenharmony_ci		return NULL;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return link_cmd;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/*
87962306a36Sopenharmony_ci * iwlagn_add_bssid_station - Add the special IBSS BSSID station
88062306a36Sopenharmony_ci *
88162306a36Sopenharmony_ci * Function sleeps.
88262306a36Sopenharmony_ci */
88362306a36Sopenharmony_ciint iwlagn_add_bssid_station(struct iwl_priv *priv,
88462306a36Sopenharmony_ci			     struct iwl_rxon_context *ctx,
88562306a36Sopenharmony_ci			     const u8 *addr, u8 *sta_id_r)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	int ret;
88862306a36Sopenharmony_ci	u8 sta_id;
88962306a36Sopenharmony_ci	struct iwl_link_quality_cmd *link_cmd;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (sta_id_r)
89262306a36Sopenharmony_ci		*sta_id_r = IWL_INVALID_STATION;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id);
89562306a36Sopenharmony_ci	if (ret) {
89662306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to add station %pM\n", addr);
89762306a36Sopenharmony_ci		return ret;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (sta_id_r)
90162306a36Sopenharmony_ci		*sta_id_r = sta_id;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
90462306a36Sopenharmony_ci	priv->stations[sta_id].used |= IWL_STA_LOCAL;
90562306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Set up default rate scaling table in device's station table */
90862306a36Sopenharmony_ci	link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
90962306a36Sopenharmony_ci	if (!link_cmd) {
91062306a36Sopenharmony_ci		IWL_ERR(priv,
91162306a36Sopenharmony_ci			"Unable to initialize rate scaling for station %pM.\n",
91262306a36Sopenharmony_ci			addr);
91362306a36Sopenharmony_ci		return -ENOMEM;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true);
91762306a36Sopenharmony_ci	if (ret)
91862306a36Sopenharmony_ci		IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
92162306a36Sopenharmony_ci	priv->stations[sta_id].lq = link_cmd;
92262306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	return 0;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci/*
92862306a36Sopenharmony_ci * static WEP keys
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * For each context, the device has a table of 4 static WEP keys
93162306a36Sopenharmony_ci * (one for each key index) that is updated with the following
93262306a36Sopenharmony_ci * commands.
93362306a36Sopenharmony_ci */
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
93662306a36Sopenharmony_ci				      struct iwl_rxon_context *ctx,
93762306a36Sopenharmony_ci				      bool send_if_empty)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	int i, not_empty = 0;
94062306a36Sopenharmony_ci	u8 buff[sizeof(struct iwl_wep_cmd) +
94162306a36Sopenharmony_ci		sizeof(struct iwl_wep_key) * WEP_KEYS_MAX];
94262306a36Sopenharmony_ci	struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff;
94362306a36Sopenharmony_ci	size_t cmd_size  = sizeof(struct iwl_wep_cmd);
94462306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
94562306a36Sopenharmony_ci		.id = ctx->wep_key_cmd,
94662306a36Sopenharmony_ci		.data = { wep_cmd, },
94762306a36Sopenharmony_ci	};
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	might_sleep();
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	memset(wep_cmd, 0, cmd_size +
95262306a36Sopenharmony_ci			(sizeof(struct iwl_wep_key) * WEP_KEYS_MAX));
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	for (i = 0; i < WEP_KEYS_MAX ; i++) {
95562306a36Sopenharmony_ci		wep_cmd->key[i].key_index = i;
95662306a36Sopenharmony_ci		if (ctx->wep_keys[i].key_size) {
95762306a36Sopenharmony_ci			wep_cmd->key[i].key_offset = i;
95862306a36Sopenharmony_ci			not_empty = 1;
95962306a36Sopenharmony_ci		} else {
96062306a36Sopenharmony_ci			wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size;
96462306a36Sopenharmony_ci		memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key,
96562306a36Sopenharmony_ci				ctx->wep_keys[i].key_size);
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
96962306a36Sopenharmony_ci	wep_cmd->num_keys = WEP_KEYS_MAX;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	cmd.len[0] = cmd_size;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (not_empty || send_if_empty)
97662306a36Sopenharmony_ci		return iwl_dvm_send_cmd(priv, &cmd);
97762306a36Sopenharmony_ci	else
97862306a36Sopenharmony_ci		return 0;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ciint iwl_restore_default_wep_keys(struct iwl_priv *priv,
98262306a36Sopenharmony_ci				 struct iwl_rxon_context *ctx)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	return iwl_send_static_wepkey_cmd(priv, ctx, false);
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciint iwl_remove_default_wep_key(struct iwl_priv *priv,
99062306a36Sopenharmony_ci			       struct iwl_rxon_context *ctx,
99162306a36Sopenharmony_ci			       struct ieee80211_key_conf *keyconf)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	int ret;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
99862306a36Sopenharmony_ci		      keyconf->keyidx);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0]));
100162306a36Sopenharmony_ci	if (iwl_is_rfkill(priv)) {
100262306a36Sopenharmony_ci		IWL_DEBUG_WEP(priv,
100362306a36Sopenharmony_ci			"Not sending REPLY_WEPKEY command due to RFKILL.\n");
100462306a36Sopenharmony_ci		/* but keys in device are clear anyway so return success */
100562306a36Sopenharmony_ci		return 0;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci	ret = iwl_send_static_wepkey_cmd(priv, ctx, 1);
100862306a36Sopenharmony_ci	IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n",
100962306a36Sopenharmony_ci		      keyconf->keyidx, ret);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return ret;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ciint iwl_set_default_wep_key(struct iwl_priv *priv,
101562306a36Sopenharmony_ci			    struct iwl_rxon_context *ctx,
101662306a36Sopenharmony_ci			    struct ieee80211_key_conf *keyconf)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	int ret;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (keyconf->keylen != WEP_KEY_LEN_128 &&
102362306a36Sopenharmony_ci	    keyconf->keylen != WEP_KEY_LEN_64) {
102462306a36Sopenharmony_ci		IWL_DEBUG_WEP(priv,
102562306a36Sopenharmony_ci			      "Bad WEP key length %d\n", keyconf->keylen);
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
103262306a36Sopenharmony_ci	memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key,
103362306a36Sopenharmony_ci							keyconf->keylen);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	ret = iwl_send_static_wepkey_cmd(priv, ctx, false);
103662306a36Sopenharmony_ci	IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n",
103762306a36Sopenharmony_ci		keyconf->keylen, keyconf->keyidx, ret);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	return ret;
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/*
104362306a36Sopenharmony_ci * dynamic (per-station) keys
104462306a36Sopenharmony_ci *
104562306a36Sopenharmony_ci * The dynamic keys are a little more complicated. The device has
104662306a36Sopenharmony_ci * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys.
104762306a36Sopenharmony_ci * These are linked to stations by a table that contains an index
104862306a36Sopenharmony_ci * into the key table for each station/key index/{mcast,unicast},
104962306a36Sopenharmony_ci * i.e. it's basically an array of pointers like this:
105062306a36Sopenharmony_ci *	key_offset_t key_mapping[NUM_STATIONS][4][2];
105162306a36Sopenharmony_ci * (it really works differently, but you can think of it as such)
105262306a36Sopenharmony_ci *
105362306a36Sopenharmony_ci * The key uploading and linking happens in the same command, the
105462306a36Sopenharmony_ci * add station command with STA_MODIFY_KEY_MASK.
105562306a36Sopenharmony_ci */
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic u8 iwlagn_key_sta_id(struct iwl_priv *priv,
105862306a36Sopenharmony_ci			    struct ieee80211_vif *vif,
105962306a36Sopenharmony_ci			    struct ieee80211_sta *sta)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (sta)
106462306a36Sopenharmony_ci		return iwl_sta_id(sta);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/*
106762306a36Sopenharmony_ci	 * The device expects GTKs for station interfaces to be
106862306a36Sopenharmony_ci	 * installed as GTKs for the AP station. If we have no
106962306a36Sopenharmony_ci	 * station ID, then use the ap_sta_id in that case.
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx)
107262306a36Sopenharmony_ci		return vif_priv->ctx->ap_sta_id;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	return IWL_INVALID_STATION;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic int iwlagn_send_sta_key(struct iwl_priv *priv,
107862306a36Sopenharmony_ci			       struct ieee80211_key_conf *keyconf,
107962306a36Sopenharmony_ci			       u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
108062306a36Sopenharmony_ci			       u32 cmd_flags)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	__le16 key_flags;
108362306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
108462306a36Sopenharmony_ci	size_t to_copy;
108562306a36Sopenharmony_ci	int i;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
108862306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
108962306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
109262306a36Sopenharmony_ci	key_flags |= STA_KEY_FLG_MAP_KEY_MSK;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	switch (keyconf->cipher) {
109562306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
109662306a36Sopenharmony_ci		key_flags |= STA_KEY_FLG_CCMP;
109762306a36Sopenharmony_ci		memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen);
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
110062306a36Sopenharmony_ci		key_flags |= STA_KEY_FLG_TKIP;
110162306a36Sopenharmony_ci		sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
110262306a36Sopenharmony_ci		for (i = 0; i < 5; i++)
110362306a36Sopenharmony_ci			sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
110462306a36Sopenharmony_ci		/* keyconf may contain MIC rx/tx keys which iwl does not use */
110562306a36Sopenharmony_ci		to_copy = min_t(size_t, sizeof(sta_cmd.key.key), keyconf->keylen);
110662306a36Sopenharmony_ci		memcpy(sta_cmd.key.key, keyconf->key, to_copy);
110762306a36Sopenharmony_ci		break;
110862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
110962306a36Sopenharmony_ci		key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
111062306a36Sopenharmony_ci		fallthrough;
111162306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
111262306a36Sopenharmony_ci		key_flags |= STA_KEY_FLG_WEP;
111362306a36Sopenharmony_ci		memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen);
111462306a36Sopenharmony_ci		break;
111562306a36Sopenharmony_ci	default:
111662306a36Sopenharmony_ci		WARN_ON(1);
111762306a36Sopenharmony_ci		return -EINVAL;
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
112162306a36Sopenharmony_ci		key_flags |= STA_KEY_MULTICAST_MSK;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/* key pointer (offset) */
112462306a36Sopenharmony_ci	sta_cmd.key.key_offset = keyconf->hw_key_idx;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	sta_cmd.key.key_flags = key_flags;
112762306a36Sopenharmony_ci	sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
112862306a36Sopenharmony_ci	sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &sta_cmd, cmd_flags);
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_civoid iwl_update_tkip_key(struct iwl_priv *priv,
113462306a36Sopenharmony_ci			 struct ieee80211_vif *vif,
113562306a36Sopenharmony_ci			 struct ieee80211_key_conf *keyconf,
113662306a36Sopenharmony_ci			 struct ieee80211_sta *sta, u32 iv32, u16 *phase1key)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	u8 sta_id = iwlagn_key_sta_id(priv, vif, sta);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION)
114162306a36Sopenharmony_ci		return;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	if (iwl_scan_cancel(priv)) {
114462306a36Sopenharmony_ci		/* cancel scan failed, just live w/ bad key and rely
114562306a36Sopenharmony_ci		   briefly on SW decryption */
114662306a36Sopenharmony_ci		return;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	iwlagn_send_sta_key(priv, keyconf, sta_id,
115062306a36Sopenharmony_ci			    iv32, phase1key, CMD_ASYNC);
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ciint iwl_remove_dynamic_key(struct iwl_priv *priv,
115462306a36Sopenharmony_ci			   struct iwl_rxon_context *ctx,
115562306a36Sopenharmony_ci			   struct ieee80211_key_conf *keyconf,
115662306a36Sopenharmony_ci			   struct ieee80211_sta *sta)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
115962306a36Sopenharmony_ci	u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
116062306a36Sopenharmony_ci	__le16 key_flags;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	/* if station isn't there, neither is the key */
116362306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION)
116462306a36Sopenharmony_ci		return -ENOENT;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
116762306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd));
116862306a36Sopenharmony_ci	if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE))
116962306a36Sopenharmony_ci		sta_id = IWL_INVALID_STATION;
117062306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION)
117362306a36Sopenharmony_ci		return 0;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ctx->key_mapping_keys--;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n",
118062306a36Sopenharmony_ci		      keyconf->keyidx, sta_id);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table))
118362306a36Sopenharmony_ci		IWL_ERR(priv, "offset %d not used in uCode key table.\n",
118462306a36Sopenharmony_ci			keyconf->hw_key_idx);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
118762306a36Sopenharmony_ci	key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC |
118862306a36Sopenharmony_ci		     STA_KEY_FLG_INVALID;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
119162306a36Sopenharmony_ci		key_flags |= STA_KEY_MULTICAST_MSK;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	sta_cmd.key.key_flags = key_flags;
119462306a36Sopenharmony_ci	sta_cmd.key.key_offset = keyconf->hw_key_idx;
119562306a36Sopenharmony_ci	sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
119662306a36Sopenharmony_ci	sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &sta_cmd, 0);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ciint iwl_set_dynamic_key(struct iwl_priv *priv,
120262306a36Sopenharmony_ci			struct iwl_rxon_context *ctx,
120362306a36Sopenharmony_ci			struct ieee80211_key_conf *keyconf,
120462306a36Sopenharmony_ci			struct ieee80211_sta *sta)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct ieee80211_key_seq seq;
120762306a36Sopenharmony_ci	u16 p1k[5];
120862306a36Sopenharmony_ci	int ret;
120962306a36Sopenharmony_ci	u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta);
121062306a36Sopenharmony_ci	const u8 *addr;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION)
121362306a36Sopenharmony_ci		return -EINVAL;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv);
121862306a36Sopenharmony_ci	if (keyconf->hw_key_idx == WEP_INVALID_OFFSET)
121962306a36Sopenharmony_ci		return -ENOSPC;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	ctx->key_mapping_keys++;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	switch (keyconf->cipher) {
122462306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
122562306a36Sopenharmony_ci		if (sta)
122662306a36Sopenharmony_ci			addr = sta->addr;
122762306a36Sopenharmony_ci		else /* station mode case only */
122862306a36Sopenharmony_ci			addr = ctx->active.bssid_addr;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		/* pre-fill phase 1 key into device cache */
123162306a36Sopenharmony_ci		ieee80211_get_key_rx_seq(keyconf, 0, &seq);
123262306a36Sopenharmony_ci		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
123362306a36Sopenharmony_ci		ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
123462306a36Sopenharmony_ci					  seq.tkip.iv32, p1k, 0);
123562306a36Sopenharmony_ci		break;
123662306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
123762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
123862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
123962306a36Sopenharmony_ci		ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
124062306a36Sopenharmony_ci					  0, NULL, 0);
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci	default:
124362306a36Sopenharmony_ci		IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
124462306a36Sopenharmony_ci		ret = -EINVAL;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (ret) {
124862306a36Sopenharmony_ci		ctx->key_mapping_keys--;
124962306a36Sopenharmony_ci		clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
125362306a36Sopenharmony_ci		      keyconf->cipher, keyconf->keylen, keyconf->keyidx,
125462306a36Sopenharmony_ci		      sta ? sta->addr : NULL, ret);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return ret;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci/*
126062306a36Sopenharmony_ci * iwlagn_alloc_bcast_station - add broadcast station into driver's station table.
126162306a36Sopenharmony_ci *
126262306a36Sopenharmony_ci * This adds the broadcast station into the driver's station table
126362306a36Sopenharmony_ci * and marks it driver active, so that it will be restored to the
126462306a36Sopenharmony_ci * device at the next best time.
126562306a36Sopenharmony_ci */
126662306a36Sopenharmony_ciint iwlagn_alloc_bcast_station(struct iwl_priv *priv,
126762306a36Sopenharmony_ci			       struct iwl_rxon_context *ctx)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	struct iwl_link_quality_cmd *link_cmd;
127062306a36Sopenharmony_ci	u8 sta_id;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
127362306a36Sopenharmony_ci	sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL);
127462306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION) {
127562306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to prepare broadcast station\n");
127662306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_lock);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		return -EINVAL;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE;
128262306a36Sopenharmony_ci	priv->stations[sta_id].used |= IWL_STA_BCAST;
128362306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
128662306a36Sopenharmony_ci	if (!link_cmd) {
128762306a36Sopenharmony_ci		IWL_ERR(priv,
128862306a36Sopenharmony_ci			"Unable to initialize rate scaling for bcast station.\n");
128962306a36Sopenharmony_ci		return -ENOMEM;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
129362306a36Sopenharmony_ci	priv->stations[sta_id].lq = link_cmd;
129462306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return 0;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci/*
130062306a36Sopenharmony_ci * iwl_update_bcast_station - update broadcast station's LQ command
130162306a36Sopenharmony_ci *
130262306a36Sopenharmony_ci * Only used by iwlagn. Placed here to have all bcast station management
130362306a36Sopenharmony_ci * code together.
130462306a36Sopenharmony_ci */
130562306a36Sopenharmony_ciint iwl_update_bcast_station(struct iwl_priv *priv,
130662306a36Sopenharmony_ci			     struct iwl_rxon_context *ctx)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct iwl_link_quality_cmd *link_cmd;
130962306a36Sopenharmony_ci	u8 sta_id = ctx->bcast_sta_id;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id);
131262306a36Sopenharmony_ci	if (!link_cmd) {
131362306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n");
131462306a36Sopenharmony_ci		return -ENOMEM;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
131862306a36Sopenharmony_ci	if (priv->stations[sta_id].lq)
131962306a36Sopenharmony_ci		kfree(priv->stations[sta_id].lq);
132062306a36Sopenharmony_ci	else
132162306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n");
132262306a36Sopenharmony_ci	priv->stations[sta_id].lq = link_cmd;
132362306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	return 0;
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ciint iwl_update_bcast_stations(struct iwl_priv *priv)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
133162306a36Sopenharmony_ci	int ret = 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	for_each_context(priv, ctx) {
133462306a36Sopenharmony_ci		ret = iwl_update_bcast_station(priv, ctx);
133562306a36Sopenharmony_ci		if (ret)
133662306a36Sopenharmony_ci			break;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return ret;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/*
134362306a36Sopenharmony_ci * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_ciint iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* Remove "disable" flag, to enable Tx for this TID */
135262306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
135362306a36Sopenharmony_ci	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
135462306a36Sopenharmony_ci	priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid));
135562306a36Sopenharmony_ci	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
135662306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
135762306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &sta_cmd, 0);
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ciint iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
136362306a36Sopenharmony_ci			 int tid, u16 ssn)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	int sta_id;
136662306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	sta_id = iwl_sta_id(sta);
137162306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION)
137262306a36Sopenharmony_ci		return -ENXIO;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
137562306a36Sopenharmony_ci	priv->stations[sta_id].sta.station_flags_msk = 0;
137662306a36Sopenharmony_ci	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
137762306a36Sopenharmony_ci	priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
137862306a36Sopenharmony_ci	priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
137962306a36Sopenharmony_ci	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
138062306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
138162306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &sta_cmd, 0);
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ciint iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
138762306a36Sopenharmony_ci			int tid)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	int sta_id;
139062306a36Sopenharmony_ci	struct iwl_addsta_cmd sta_cmd;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	sta_id = iwl_sta_id(sta);
139562306a36Sopenharmony_ci	if (sta_id == IWL_INVALID_STATION) {
139662306a36Sopenharmony_ci		IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
139762306a36Sopenharmony_ci		return -ENXIO;
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
140162306a36Sopenharmony_ci	priv->stations[sta_id].sta.station_flags_msk = 0;
140262306a36Sopenharmony_ci	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
140362306a36Sopenharmony_ci	priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
140462306a36Sopenharmony_ci	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
140562306a36Sopenharmony_ci	memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
140662306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return iwl_send_add_sta(priv, &sta_cmd, 0);
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_civoid iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct iwl_addsta_cmd cmd = {
141662306a36Sopenharmony_ci		.mode = STA_CONTROL_MODIFY_MSK,
141762306a36Sopenharmony_ci		.station_flags = STA_FLG_PWR_SAVE_MSK,
141862306a36Sopenharmony_ci		.station_flags_msk = STA_FLG_PWR_SAVE_MSK,
141962306a36Sopenharmony_ci		.sta.sta_id = sta_id,
142062306a36Sopenharmony_ci		.sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK,
142162306a36Sopenharmony_ci		.sleep_tx_count = cpu_to_le16(cnt),
142262306a36Sopenharmony_ci	};
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	iwl_send_add_sta(priv, &cmd, CMD_ASYNC);
142562306a36Sopenharmony_ci}
1426