Exemplo n.º 1
0
/* Card State Notification indicated out of critical temperature
 * since Card State Notification will not provide any temperature reading
 * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
 * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
 */
static void iwl_bg_ct_exit(struct work_struct *work)
{
	struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit);
	struct iwl_tt_mgmt *tt = &priv->thermal_throttle;

	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
		return;

	if (!iwl_is_ready(priv))
		return;

	/* stop ct_kill_exit_tm timer */
	del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);

	if (tt->state == IWL_TI_CT_KILL) {
		IWL_ERR(priv,
			"Device temperature below critical"
			"- ucode awake!\n");
		/*
		 * exit from CT_KILL state
		 * reset the current temperature reading
		 */
		priv->temperature = 0;
		if (!priv->thermal_throttle.advanced_tt)
			iwl_legacy_tt_handler(priv,
					      IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
					      true);
		else
			iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
					       true);
	}
}
Exemplo n.º 2
0
/* Card State Notification indicated out of critical temperature
 * since Card State Notification will not provide any temperature reading
 * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature
 * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state
 */
static void iwl_bg_ct_exit(void *p)
{
	struct iwl_priv *priv = p;
	struct iwl_tt_mgmt *tt = &priv->thermal_throttle;

	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
		return;

	if (!iwl_is_ready(priv))
		return;

	/* stop ct_kill_exit_tm timer */
	del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);

	if (tt->state == IWL_TI_CT_KILL) {
		IWL_ERR(priv,
			"Device temperature below critical"
			"- ucode awake!\n");
		if (!priv->thermal_throttle.advanced_tt)
			iwl_legacy_tt_handler(priv,
					IWL_REDUCED_PERFORMANCE_THRESHOLD_2);
		else
			iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD);
	}
}
Exemplo n.º 3
0
/* Card State Notification indicated reach critical temperature
 * if PSP not enable, no Thermal Throttling function will be performed
 * just set the GP1 bit to acknowledge the event
 * otherwise, go into IWL_TI_CT_KILL state
 * since Card State Notification will not provide any temperature reading
 * for Legacy mode
 * so just pass the CT_KILL temperature to iwl_legacy_tt_handler()
 * for advance mode
 * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state
 */
static void iwl_bg_ct_enter(void *p)
{
	struct iwl_priv *priv = p;
	struct iwl_tt_mgmt *tt = &priv->thermal_throttle;

	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
		return;

	if (!iwl_is_ready(priv))
		return;

	if (tt->state != IWL_TI_CT_KILL) {
		IWL_ERR(priv, "Device reached critical temperature "
			      "- ucode going to sleep!\n");
		if (!priv->thermal_throttle.advanced_tt)
			iwl_legacy_tt_handler(priv,
					      IWL_MINIMAL_POWER_THRESHOLD);
		else
			iwl_advance_tt_handler(priv,
					       CT_KILL_THRESHOLD + 1);
	}
}
Exemplo n.º 4
0
/*
 * CARD_STATE_CMD
 *
 * Use: Sets the device's internal card state to enable, disable, or halt
 *
 * When in the 'enable' state the card operates as normal.
 * When in the 'disable' state, the card enters into a low power mode.
 * When in the 'halt' state, the card is shut down and must be fully
 * restarted to come back on.
 */
static int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag)
{
	struct iwl_host_cmd cmd = {
		.id = REPLY_CARD_STATE_CMD,
		.len = sizeof(u32),
		.data = &flags,
		.meta.flags = meta_flag,
	};

	return iwl_send_cmd(priv, &cmd);
}

void iwl_radio_kill_sw_disable_radio(struct iwl_priv *priv)
{
	unsigned long flags;

	if (test_bit(STATUS_RF_KILL_SW, &priv->status))
		return;

	IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO OFF\n");

	iwl_scan_cancel(priv);
	/* FIXME: This is a workaround for AP */
	if (priv->iw_mode != NL80211_IFTYPE_AP) {
		spin_lock_irqsave(&priv->lock, flags);
		iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
			    CSR_UCODE_SW_BIT_RFKILL);
		spin_unlock_irqrestore(&priv->lock, flags);
		/* call the host command only if no hw rf-kill set */
		if (!test_bit(STATUS_RF_KILL_HW, &priv->status) &&
		    iwl_is_ready(priv))
			iwl_send_card_state(priv,
				CARD_STATE_CMD_DISABLE, 0);
		set_bit(STATUS_RF_KILL_SW, &priv->status);
			/* make sure mac80211 stop sending Tx frame */
		if (priv->mac80211_registered)
			ieee80211_stop_queues(priv->hw);
	}
}
Exemplo n.º 5
0
static void iwl_bg_request_scan(struct work_struct *data)
{
	struct iwl_priv *priv =
	    container_of(data, struct iwl_priv, request_scan);
	struct iwl_host_cmd cmd = {
		.id = REPLY_SCAN_CMD,
		.len = sizeof(struct iwl_scan_cmd),
		.meta.flags = CMD_SIZE_HUGE,
	};
	struct iwl_scan_cmd *scan;
	struct ieee80211_conf *conf = NULL;
	int ret = 0;
	u32 tx_ant;
	u16 cmd_len;
	enum ieee80211_band band;
	u8 n_probes = 2;
	u8 rx_chain = priv->hw_params.valid_rx_ant;

	conf = ieee80211_get_hw_conf(priv->hw);

	mutex_lock(&priv->mutex);

	if (!iwl_is_ready(priv)) {
		IWL_WARNING("request scan called when driver not ready.\n");
		goto done;
	}

	/* Make sure the scan wasn't cancelled before this queued work
	 * was given the chance to run... */
	if (!test_bit(STATUS_SCANNING, &priv->status))
		goto done;

	/* This should never be called or scheduled if there is currently
	 * a scan active in the hardware. */
	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
		IWL_DEBUG_INFO("Multiple concurrent scan requests in parallel. "
			       "Ignoring second request.\n");
		ret = -EIO;
		goto done;
	}

	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
		IWL_DEBUG_SCAN("Aborting scan due to device shutdown\n");
		goto done;
	}

	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
		IWL_DEBUG_HC("Scan request while abort pending.  Queuing.\n");
		goto done;
	}

	if (iwl_is_rfkill(priv)) {
		IWL_DEBUG_HC("Aborting scan due to RF Kill activation\n");
		goto done;
	}

	if (!test_bit(STATUS_READY, &priv->status)) {
		IWL_DEBUG_HC("Scan request while uninitialized.  Queuing.\n");
		goto done;
	}

	if (!priv->scan_bands) {
		IWL_DEBUG_HC("Aborting scan due to no requested bands\n");
		goto done;
	}

	if (!priv->scan) {
		priv->scan = kmalloc(sizeof(struct iwl_scan_cmd) +
				     IWL_MAX_SCAN_SIZE, GFP_KERNEL);
		if (!priv->scan) {
			ret = -ENOMEM;
			goto done;
		}
	}
	scan = priv->scan;
	memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE);

	scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
	scan->quiet_time = IWL_ACTIVE_QUIET_TIME;

	if (iwl_is_associated(priv)) {
		u16 interval = 0;
		u32 extra;
		u32 suspend_time = 100;
		u32 scan_suspend_time = 100;
		unsigned long flags;

		IWL_DEBUG_INFO("Scanning while associated...\n");

		spin_lock_irqsave(&priv->lock, flags);
		interval = priv->beacon_int;
		spin_unlock_irqrestore(&priv->lock, flags);

		scan->suspend_time = 0;
		scan->max_out_time = cpu_to_le32(200 * 1024);
		if (!interval)
			interval = suspend_time;

		extra = (suspend_time / interval) << 22;
		scan_suspend_time = (extra |
		    ((suspend_time % interval) * 1024));
		scan->suspend_time = cpu_to_le32(scan_suspend_time);
		IWL_DEBUG_SCAN("suspend_time 0x%X beacon interval %d\n",
			       scan_suspend_time, interval);
	}

	/* We should add the ability for user to lock to PASSIVE ONLY */
	if (priv->one_direct_scan) {
		IWL_DEBUG_SCAN("Start direct scan for '%s'\n",
				iwl_escape_essid(priv->direct_ssid,
				priv->direct_ssid_len));
		scan->direct_scan[0].id = WLAN_EID_SSID;
		scan->direct_scan[0].len = priv->direct_ssid_len;
		memcpy(scan->direct_scan[0].ssid,
		       priv->direct_ssid, priv->direct_ssid_len);
		n_probes++;
	} else if (!iwl_is_associated(priv) && priv->essid_len) {
		IWL_DEBUG_SCAN("Start direct scan for '%s' (not associated)\n",
				iwl_escape_essid(priv->essid, priv->essid_len));
		scan->direct_scan[0].id = WLAN_EID_SSID;
		scan->direct_scan[0].len = priv->essid_len;
		memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len);
		n_probes++;
	} else {
		IWL_DEBUG_SCAN("Start indirect scan.\n");
	}

	scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
	scan->tx_cmd.sta_id = priv->hw_params.bcast_sta_id;
	scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;


	if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
		band = IEEE80211_BAND_2GHZ;
		scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
		tx_ant = iwl_scan_tx_ant(priv, band);
		if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK)
			scan->tx_cmd.rate_n_flags =
				iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
							tx_ant);
		else
			scan->tx_cmd.rate_n_flags =
				iwl_hw_set_rate_n_flags(IWL_RATE_1M_PLCP,
							tx_ant |
							RATE_MCS_CCK_MSK);
		scan->good_CRC_th = 0;
	} else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
		band = IEEE80211_BAND_5GHZ;
		tx_ant = iwl_scan_tx_ant(priv, band);
		scan->tx_cmd.rate_n_flags =
				iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
							tx_ant);
		scan->good_CRC_th = IWL_GOOD_CRC_TH;

		/* Force use of chains B and C (0x6) for scan Rx for 4965
		 * Avoid A (0x1) because of its off-channel reception on A-band.
		 * MIMO is not used here, but value is required */
		if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
			rx_chain = 0x6;
	} else {
		IWL_WARNING("Invalid scan band count\n");
		goto done;
	}

	scan->rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
				cpu_to_le16((0x7 << RXON_RX_CHAIN_VALID_POS) |
				(rx_chain << RXON_RX_CHAIN_FORCE_SEL_POS) |
				(0x7 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));

	cmd_len = iwl_fill_probe_req(priv, band,
				     (struct ieee80211_mgmt *)scan->data,
				     IWL_MAX_SCAN_SIZE - sizeof(*scan));

	scan->tx_cmd.len = cpu_to_le16(cmd_len);

	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR)
		scan->filter_flags = RXON_FILTER_PROMISC_MSK;

	scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
			       RXON_FILTER_BCON_AWARE_MSK);

	scan->channel_count =
		iwl_get_channels_for_scan(priv, band, 1, /* active */
			n_probes,
			(void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);

	if (scan->channel_count == 0) {
		IWL_DEBUG_SCAN("channel count %d\n", scan->channel_count);
		goto done;
	}

	cmd.len += le16_to_cpu(scan->tx_cmd.len) +
	    scan->channel_count * sizeof(struct iwl_scan_channel);
	cmd.data = scan;
	scan->len = cpu_to_le16(cmd.len);

	set_bit(STATUS_SCAN_HW, &priv->status);
	ret = iwl_send_cmd_sync(priv, &cmd);
	if (ret)
		goto done;

	queue_delayed_work(priv->workqueue, &priv->scan_check,
			   IWL_SCAN_CHECK_WATCHDOG);

	mutex_unlock(&priv->mutex);
	return;

 done:
	/* inform mac80211 scan aborted */
	queue_work(priv->workqueue, &priv->scan_completed);
	mutex_unlock(&priv->mutex);
}

static void iwl_bg_abort_scan(struct work_struct *work)
{
	struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);

	if (!iwl_is_ready(priv))
		return;

	mutex_lock(&priv->mutex);

	set_bit(STATUS_SCAN_ABORTING, &priv->status);
	iwl_send_scan_abort(priv);

	mutex_unlock(&priv->mutex);
}

void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
{
	/*  FIXME: move here when resolved PENDING
	 *  INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); */
	INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
	INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
	INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
}
Exemplo n.º 6
0
int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
{
	struct iwl_priv *priv = hw->priv;
	struct iwl_rxon_context *ctx;
	struct ieee80211_conf *conf = &hw->conf;
	struct ieee80211_channel *channel = conf->channel;
	const struct iwl_channel_info *ch_info;
	int ret = 0;

	IWL_DEBUG_MAC80211(priv, "changed %#x", changed);

	mutex_lock(&priv->mutex);

	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
		IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
		goto out;
	}

	if (!iwl_is_ready(priv)) {
		IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
		goto out;
	}

	if (changed & (IEEE80211_CONF_CHANGE_SMPS |
		       IEEE80211_CONF_CHANGE_CHANNEL)) {
		/* mac80211 uses static for non-HT which is what we want */
		priv->current_ht_config.smps = conf->smps_mode;

		/*
		 * Recalculate chain counts.
		 *
		 * If monitor mode is enabled then mac80211 will
		 * set up the SM PS mode to OFF if an HT channel is
		 * configured.
		 */
		if (priv->cfg->ops->hcmd->set_rxon_chain)
			for_each_context(priv, ctx)
				priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
	}

	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
		unsigned long flags;

		ch_info = iwl_get_channel_info(priv, channel->band,
					       channel->hw_value);
		if (!is_channel_valid(ch_info)) {
			IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
			ret = -EINVAL;
			goto out;
		}

		spin_lock_irqsave(&priv->lock, flags);

		for_each_context(priv, ctx) {
			/* Configure HT40 channels */
			if (ctx->ht.enabled != conf_is_ht(conf))
				ctx->ht.enabled = conf_is_ht(conf);

			if (ctx->ht.enabled) {
				if (conf_is_ht40_minus(conf)) {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_BELOW;
					ctx->ht.is_40mhz = true;
				} else if (conf_is_ht40_plus(conf)) {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
					ctx->ht.is_40mhz = true;
				} else {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_NONE;
					ctx->ht.is_40mhz = false;
				}
			} else
				ctx->ht.is_40mhz = false;

			/*
			 * Default to no protection. Protection mode will
			 * later be set from BSS config in iwl_ht_conf
			 */
			ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;

			/* if we are switching from ht to 2.4 clear flags
			 * from any ht related info since 2.4 does not
			 * support ht */
			if (le16_to_cpu(ctx->staging.channel) !=
			    channel->hw_value)
				ctx->staging.flags = 0;

			iwl_set_rxon_channel(priv, channel, ctx);
			iwl_set_rxon_ht(priv, &priv->current_ht_config);

			iwl_set_flags_for_band(priv, ctx, channel->band,
					       ctx->vif);
		}

		spin_unlock_irqrestore(&priv->lock, flags);

		iwl_update_bcast_stations(priv);

		/*
		 * The list of supported rates and rate mask can be different
		 * for each band; since the band may have changed, reset
		 * the rate mask to what mac80211 lists.
		 */
		iwl_set_rate(priv);
	}
Exemplo n.º 7
0
/**
 * iwl_restore_stations() - Restore driver known stations to device
 *
 * All stations considered active by driver, but not present in ucode, is
 * restored.
 *
 * Function sleeps.
 */
void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
	struct iwl_addsta_cmd sta_cmd;
	struct iwl_link_quality_cmd lq;
	unsigned long flags_spin;
	int i;
	bool found = false;
	int ret;
	bool send_lq;

	if (!iwl_is_ready(priv)) {
		IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n");
		return;
	}

	IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
	spin_lock_irqsave(&priv->sta_lock, flags_spin);
	for (i = 0; i < priv->hw_params.max_stations; i++) {
		if (ctx->ctxid != priv->stations[i].ctxid)
			continue;
		if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
			    !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
			IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
					priv->stations[i].sta.sta.addr);
			priv->stations[i].sta.mode = 0;
			priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
			found = true;
		}
	}

	for (i = 0; i < priv->hw_params.max_stations; i++) {
		if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
			memcpy(&sta_cmd, &priv->stations[i].sta,
			       sizeof(struct iwl_addsta_cmd));
			send_lq = false;
			if (priv->stations[i].lq) {
				memcpy(&lq, priv->stations[i].lq,
				       sizeof(struct iwl_link_quality_cmd));
				send_lq = true;
			}
			spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
			ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
			if (ret) {
				spin_lock_irqsave(&priv->sta_lock, flags_spin);
				IWL_ERR(priv, "Adding station %pM failed.\n",
					priv->stations[i].sta.sta.addr);
				priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE;
				priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
				spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
			}
			/*
			 * Rate scaling has already been initialized, send
			 * current LQ command
			 */
			if (send_lq)
				iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true);
			spin_lock_irqsave(&priv->sta_lock, flags_spin);
			priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
		}
	}

	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
	if (!found)
		IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n");
	else
		IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n");
}
Exemplo n.º 8
0
static int iwl_send_remove_station(struct iwl_priv *priv,
				   const u8 *addr, int sta_id,
				   bool temporary)
{
	struct iwl_rx_packet *pkt;
	int ret;

	unsigned long flags_spin;
	struct iwl_rem_sta_cmd rm_sta_cmd;

	struct iwl_host_cmd cmd = {
		.id = REPLY_REMOVE_STA,
		.len = sizeof(struct iwl_rem_sta_cmd),
		.flags = CMD_SYNC,
		.data = &rm_sta_cmd,
	};

	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
	rm_sta_cmd.num_sta = 1;
	memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN);

	cmd.flags |= CMD_WANT_SKB;

	ret = iwl_send_cmd(priv, &cmd);

	if (ret)
		return ret;

	pkt = (struct iwl_rx_packet *)cmd.reply_page;
	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
		IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
			  pkt->hdr.flags);
		ret = -EIO;
	}

	if (!ret) {
		switch (pkt->u.rem_sta.status) {
		case REM_STA_SUCCESS_MSK:
			if (!temporary) {
				spin_lock_irqsave(&priv->sta_lock, flags_spin);
				iwl_sta_ucode_deactivate(priv, sta_id);
				spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
			}
			IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
			break;
		default:
			ret = -EIO;
			IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
			break;
		}
	}
	iwl_free_pages(priv, cmd.reply_page);

	return ret;
}

/**
 * iwl_remove_station - Remove driver's knowledge of station.
 */
int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id,
		       const u8 *addr)
{
	unsigned long flags;

	if (!iwl_is_ready(priv)) {
		IWL_DEBUG_INFO(priv,
			"Unable to remove station %pM, device not ready.\n",
			addr);
		/*
		 * It is typical for stations to be removed when we are
		 * going down. Return success since device will be down
		 * soon anyway
		 */
		return 0;
	}

	IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d  %pM\n",
			sta_id, addr);

	if (WARN_ON(sta_id == IWL_INVALID_STATION))
		return -EINVAL;

	spin_lock_irqsave(&priv->sta_lock, flags);

	if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
		IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
				addr);
		goto out_err;
	}

	if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
		IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
				addr);
		goto out_err;
	}

	if (priv->stations[sta_id].used & IWL_STA_LOCAL) {
		kfree(priv->stations[sta_id].lq);
		priv->stations[sta_id].lq = NULL;
	}

	priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;

	priv->num_stations--;

	BUG_ON(priv->num_stations < 0);

	spin_unlock_irqrestore(&priv->sta_lock, flags);

	return iwl_send_remove_station(priv, addr, sta_id, false);
out_err:
	spin_unlock_irqrestore(&priv->sta_lock, flags);
	return -EINVAL;
}
/**
 * iwl_legacy_mac_config - mac80211 config callback
 */
int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed)
{
	struct iwl_priv *priv = hw->priv;
	const struct iwl_channel_info *ch_info;
	struct ieee80211_conf *conf = &hw->conf;
	struct ieee80211_channel *channel = conf->channel;
	struct iwl_ht_config *ht_conf = &priv->current_ht_config;
	struct iwl_rxon_context *ctx;
	unsigned long flags = 0;
	int ret = 0;
	u16 ch;
	int scan_active = 0;
	bool ht_changed[NUM_IWL_RXON_CTX] = {};

	if (WARN_ON(!priv->cfg->ops->legacy))
		return -EOPNOTSUPP;

	mutex_lock(&priv->mutex);

	IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
					channel->hw_value, changed);

	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
		scan_active = 1;
		IWL_DEBUG_MAC80211(priv, "scan active\n");
	}

	if (changed & (IEEE80211_CONF_CHANGE_SMPS |
		       IEEE80211_CONF_CHANGE_CHANNEL)) {
		/* mac80211 uses static for non-HT which is what we want */
		priv->current_ht_config.smps = conf->smps_mode;

		/*
		 * Recalculate chain counts.
		 *
		 * If monitor mode is enabled then mac80211 will
		 * set up the SM PS mode to OFF if an HT channel is
		 * configured.
		 */
		if (priv->cfg->ops->hcmd->set_rxon_chain)
			for_each_context(priv, ctx)
				priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
	}

	/* during scanning mac80211 will delay channel setting until
	 * scan finish with changed = 0
	 */
	if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
		if (scan_active)
			goto set_ch_out;

		ch = channel->hw_value;
		ch_info = iwl_get_channel_info(priv, channel->band, ch);
		if (!is_channel_valid(ch_info)) {
			IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
			ret = -EINVAL;
			goto set_ch_out;
		}

		spin_lock_irqsave(&priv->lock, flags);

		for_each_context(priv, ctx) {
			/* Configure HT40 channels */
			if (ctx->ht.enabled != conf_is_ht(conf)) {
				ctx->ht.enabled = conf_is_ht(conf);
				ht_changed[ctx->ctxid] = true;
			}
			if (ctx->ht.enabled) {
				if (conf_is_ht40_minus(conf)) {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_BELOW;
					ctx->ht.is_40mhz = true;
				} else if (conf_is_ht40_plus(conf)) {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
					ctx->ht.is_40mhz = true;
				} else {
					ctx->ht.extension_chan_offset =
						IEEE80211_HT_PARAM_CHA_SEC_NONE;
					ctx->ht.is_40mhz = false;
				}
			} else
				ctx->ht.is_40mhz = false;

			/*
			 * Default to no protection. Protection mode will
			 * later be set from BSS config in iwl_ht_conf
			 */
			ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;

			/* if we are switching from ht to 2.4 clear flags
			 * from any ht related info since 2.4 does not
			 * support ht */
			if ((le16_to_cpu(ctx->staging.channel) != ch))
				ctx->staging.flags = 0;

			iwl_set_rxon_channel(priv, channel, ctx);
			iwl_set_rxon_ht(priv, ht_conf);

			iwl_set_flags_for_band(priv, ctx, channel->band,
					       ctx->vif);
		}

		spin_unlock_irqrestore(&priv->lock, flags);

		if (priv->cfg->ops->legacy->update_bcast_stations)
			ret = priv->cfg->ops->legacy->update_bcast_stations(priv);

 set_ch_out:
		/* The list of supported rates and rate mask can be different
		 * for each band; since the band may have changed, reset
		 * the rate mask to what mac80211 lists */
		iwl_set_rate(priv);
	}

	if (changed & (IEEE80211_CONF_CHANGE_PS |
			IEEE80211_CONF_CHANGE_IDLE)) {
		ret = iwl_power_update_mode(priv, false);
		if (ret)
			IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
	}

	if (changed & IEEE80211_CONF_CHANGE_POWER) {
		IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
			priv->tx_power_user_lmt, conf->power_level);

		iwl_set_tx_power(priv, conf->power_level, false);
	}

	if (!iwl_is_ready(priv)) {
		IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
		goto out;
	}

	if (scan_active)
		goto out;

	for_each_context(priv, ctx) {
		if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)))
			iwlcore_commit_rxon(priv, ctx);
		else
			IWL_DEBUG_INFO(priv,
				"Not re-sending same RXON configuration.\n");
		if (ht_changed[ctx->ctxid])
			iwl_update_qos(priv, ctx);
	}

out:
	IWL_DEBUG_MAC80211(priv, "leave\n");
	mutex_unlock(&priv->mutex);
	return ret;
}