diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/mac80211.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 1243 | 
1 files changed, 1066 insertions, 177 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9833cdf6177..98556d03c1e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -5,7 +5,7 @@   *   * GPL LICENSE SUMMARY   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@   *   * BSD LICENSE   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -66,7 +66,9 @@  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/ip.h> +#include <linux/if_arp.h>  #include <net/mac80211.h> +#include <net/ieee80211_radiotap.h>  #include <net/tcp.h>  #include "iwl-op-mode.h" @@ -77,6 +79,7 @@  #include "iwl-eeprom-parse.h"  #include "fw-api-scan.h"  #include "iwl-phy-db.h" +#include "testmode.h"  static const struct ieee80211_iface_limit iwl_mvm_limits[] = {  	{ @@ -127,6 +130,117 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {  };  #endif +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +/* + * Use the reserved field to indicate magic values. + * these values will only be used internally by the driver, + * and won't make it to the fw (reserved will be 0). + * BC_FILTER_MAGIC_IP - configure the val of this attribute to + *	be the vif's ip address. in case there is not a single + *	ip address (0, or more than 1), this attribute will + *	be skipped. + * BC_FILTER_MAGIC_MAC - set the val of this attribute to + *	the LSB bytes of the vif's mac address + */ +enum { +	BC_FILTER_MAGIC_NONE = 0, +	BC_FILTER_MAGIC_IP, +	BC_FILTER_MAGIC_MAC, +}; + +static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { +	{ +		/* arp */ +		.discard = 0, +		.frame_type = BCAST_FILTER_FRAME_TYPE_ALL, +		.attrs = { +			{ +				/* frame type - arp, hw type - ethernet */ +				.offset_type = +					BCAST_FILTER_OFFSET_PAYLOAD_START, +				.offset = sizeof(rfc1042_header), +				.val = cpu_to_be32(0x08060001), +				.mask = cpu_to_be32(0xffffffff), +			}, +			{ +				/* arp dest ip */ +				.offset_type = +					BCAST_FILTER_OFFSET_PAYLOAD_START, +				.offset = sizeof(rfc1042_header) + 2 + +					  sizeof(struct arphdr) + +					  ETH_ALEN + sizeof(__be32) + +					  ETH_ALEN, +				.mask = cpu_to_be32(0xffffffff), +				/* mark it as special field */ +				.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP), +			}, +		}, +	}, +	{ +		/* dhcp offer bcast */ +		.discard = 0, +		.frame_type = BCAST_FILTER_FRAME_TYPE_IPV4, +		.attrs = { +			{ +				/* udp dest port - 68 (bootp client)*/ +				.offset_type = BCAST_FILTER_OFFSET_IP_END, +				.offset = offsetof(struct udphdr, dest), +				.val = cpu_to_be32(0x00440000), +				.mask = cpu_to_be32(0xffff0000), +			}, +			{ +				/* dhcp - lsb bytes of client hw address */ +				.offset_type = BCAST_FILTER_OFFSET_IP_END, +				.offset = 38, +				.mask = cpu_to_be32(0xffffffff), +				/* mark it as special field */ +				.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC), +			}, +		}, +	}, +	/* last filter must be empty */ +	{}, +}; +#endif + +void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ +	if (!iwl_mvm_is_d0i3_supported(mvm)) +		return; + +	IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); +	WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap)); +	iwl_trans_ref(mvm->trans); +} + +void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ +	if (!iwl_mvm_is_d0i3_supported(mvm)) +		return; + +	IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); +	WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap)); +	iwl_trans_unref(mvm->trans); +} + +static void +iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref) +{ +	int i; + +	if (!iwl_mvm_is_d0i3_supported(mvm)) +		return; + +	for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) { +		if (ref == i) +			continue; + +		IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i); +		clear_bit(i, mvm->ref_bitmap); +		iwl_trans_unref(mvm->trans); +	} +} +  static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)  {  	int i; @@ -138,6 +252,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)  	}  } +static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) +{ +	/* we create the 802.11 header and SSID element */ +	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID) +		return mvm->fw->ucode_capa.max_probe_length - 24 - 2; +	return mvm->fw->ucode_capa.max_probe_length - 24 - 34; +} +  int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  {  	struct ieee80211_hw *hw = mvm->hw; @@ -155,11 +277,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  		    IEEE80211_HW_TIMING_BEACON_ONLY |  		    IEEE80211_HW_CONNECTION_MONITOR |  		    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | -		    IEEE80211_HW_SUPPORTS_STATIC_SMPS | -		    IEEE80211_HW_SUPPORTS_UAPSD; +		    IEEE80211_HW_SUPPORTS_STATIC_SMPS; -	hw->queues = IWL_MVM_FIRST_AGG_QUEUE; +	hw->queues = mvm->first_agg_queue;  	hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; +	hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | +				    IEEE80211_RADIOTAP_MCS_HAVE_STBC; +	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;  	hw->rate_control_algorithm = "iwl-mvm-rs";  	/* @@ -171,6 +295,14 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	    !iwlwifi_mod_params.sw_crypto)  		hw->flags |= IEEE80211_HW_MFP_CAPABLE; +	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && +	    IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 && +	    !iwlwifi_mod_params.uapsd_disable) { +		hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; +		hw->uapsd_queues = IWL_UAPSD_AC_INFO; +		hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; +	} +  	hw->sta_data_size = sizeof(struct iwl_mvm_sta);  	hw->vif_data_size = sizeof(struct iwl_mvm_vif);  	hw->chanctx_data_size = sizeof(u16); @@ -179,11 +311,18 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  		BIT(NL80211_IFTYPE_P2P_CLIENT) |  		BIT(NL80211_IFTYPE_AP) |  		BIT(NL80211_IFTYPE_P2P_GO) | -		BIT(NL80211_IFTYPE_P2P_DEVICE); +		BIT(NL80211_IFTYPE_P2P_DEVICE) | +		BIT(NL80211_IFTYPE_ADHOC); -	hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | -			    WIPHY_FLAG_DISABLE_BEACON_HINTS | -			    WIPHY_FLAG_IBSS_RSN; +	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; +	hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | +				       REGULATORY_DISABLE_BEACON_HINTS; + +	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) +		hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + +	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW) +		hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;  	hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;  	hw->wiphy->n_iface_combinations = @@ -191,8 +330,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	hw->wiphy->max_remain_on_channel_duration = 10000;  	hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; -	hw->uapsd_queues = IWL_UAPSD_AC_INFO; -	hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;  	/* Extract MAC address */  	memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -212,9 +349,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	iwl_mvm_reset_phy_ctxts(mvm); -	/* we create the 802.11 header and a max-length SSID element */ -	hw->wiphy->max_scan_ie_len = -		mvm->fw->ucode_capa.max_probe_length - 24 - 34; +	hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); +  	hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;  	if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) @@ -231,13 +367,29 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	else  		hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +	hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +	hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; +	hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; +	/* we create the 802.11 header and zero length SSID IE. */ +	hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; +  	hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |  			       NL80211_FEATURE_P2P_GO_OPPPS;  	mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; +	/* currently FW API supports only one optional cipher scheme */ +	if (mvm->fw->cs[0].cipher) { +		mvm->hw->n_cipher_schemes = 1; +		mvm->hw->cipher_schemes = &mvm->fw->cs[0]; +	} +  #ifdef CONFIG_PM_SLEEP -	if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && +	if (iwl_mvm_is_d0i3_supported(mvm) && +	    device_can_wakeup(mvm->trans->dev)) { +		mvm->wowlan.flags = WIPHY_WOWLAN_ANY; +		hw->wiphy->wowlan = &mvm->wowlan; +	} else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&  	    mvm->trans->ops->d3_suspend &&  	    mvm->trans->ops->d3_resume &&  	    device_can_wakeup(mvm->trans->dev)) { @@ -258,6 +410,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	}  #endif +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +	/* assign default bcast filtering configuration */ +	mvm->bcast_filters = iwl_mvm_default_bcast_filters; +#endif +  	ret = iwl_mvm_leds_init(mvm);  	if (ret)  		return ret; @@ -269,11 +426,55 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	return ret;  } +static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, +			     struct ieee80211_sta *sta, +			     struct sk_buff *skb) +{ +	struct iwl_mvm_sta *mvmsta; +	bool defer = false; + +	/* +	 * double check the IN_D0I3 flag both before and after +	 * taking the spinlock, in order to prevent taking +	 * the spinlock when not needed. +	 */ +	if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))) +		return false; + +	spin_lock(&mvm->d0i3_tx_lock); +	/* +	 * testing the flag again ensures the skb dequeue +	 * loop (on d0i3 exit) hasn't run yet. +	 */ +	if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) +		goto out; + +	mvmsta = iwl_mvm_sta_from_mac80211(sta); +	if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || +	    mvmsta->sta_id != mvm->d0i3_ap_sta_id) +		goto out; + +	__skb_queue_tail(&mvm->d0i3_tx, skb); +	ieee80211_stop_queues(mvm->hw); + +	/* trigger wakeup */ +	iwl_mvm_ref(mvm, IWL_MVM_REF_TX); +	iwl_mvm_unref(mvm, IWL_MVM_REF_TX); + +	defer = true; +out: +	spin_unlock(&mvm->d0i3_tx_lock); +	return defer; +} +  static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  			   struct ieee80211_tx_control *control,  			   struct sk_buff *skb)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct ieee80211_sta *sta = control->sta; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr = (void *)skb->data;  	if (iwl_mvm_is_radio_killed(mvm)) {  		IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); @@ -284,8 +485,18 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  	    !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))  		goto drop; -	if (control->sta) { -		if (iwl_mvm_tx_skb(mvm, skb, control->sta)) +	/* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ +	if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && +		     ieee80211_is_mgmt(hdr->frame_control) && +		     !ieee80211_is_deauth(hdr->frame_control) && +		     !ieee80211_is_disassoc(hdr->frame_control) && +		     !ieee80211_is_action(hdr->frame_control))) +		sta = NULL; + +	if (sta) { +		if (iwl_mvm_defer_tx(mvm, sta, skb)) +			return; +		if (iwl_mvm_tx_skb(mvm, skb, sta))  			goto drop;  		return;  	} @@ -297,6 +508,24 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  	ieee80211_free_txskb(hw, skb);  } +static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) +{ +	if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) +		return false; +	return true; +} + +static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) +{ +	if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) +		return false; +	if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) +		return true; + +	/* enabled by default */ +	return true; +} +  static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  				    struct ieee80211_vif *vif,  				    enum ieee80211_ampdu_mlme_action action, @@ -305,6 +534,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; +	bool tx_agg_ref = false;  	IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",  		     sta->addr, tid, action); @@ -312,11 +542,40 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  	if (!(mvm->nvm_data->sku_cap_11n_enable))  		return -EACCES; +	/* return from D0i3 before starting a new Tx aggregation */ +	switch (action) { +	case IEEE80211_AMPDU_TX_START: +	case IEEE80211_AMPDU_TX_STOP_CONT: +	case IEEE80211_AMPDU_TX_STOP_FLUSH: +	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: +	case IEEE80211_AMPDU_TX_OPERATIONAL: +		iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); +		tx_agg_ref = true; + +		/* +		 * for tx start, wait synchronously until D0i3 exit to +		 * get the correct sequence number for the tid. +		 * additionally, some other ampdu actions use direct +		 * target access, which is not handled automatically +		 * by the trans layer (unlike commands), so wait for +		 * d0i3 exit in these cases as well. +		 */ +		if (!wait_event_timeout(mvm->d0i3_exit_waitq, +			  !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) { +			WARN_ON_ONCE(1); +			iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); +			return -EIO; +		} +		break; +	default: +		break; +	} +  	mutex_lock(&mvm->mutex);  	switch (action) {  	case IEEE80211_AMPDU_RX_START: -		if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) { +		if (!iwl_enable_rx_ampdu(mvm->cfg)) {  			ret = -EINVAL;  			break;  		} @@ -326,7 +585,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  		ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);  		break;  	case IEEE80211_AMPDU_TX_START: -		if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) { +		if (!iwl_enable_tx_ampdu(mvm->cfg)) {  			ret = -EINVAL;  			break;  		} @@ -349,6 +608,13 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  	}  	mutex_unlock(&mvm->mutex); +	/* +	 * If the tid is marked as started, we won't use it for offloaded +	 * traffic on the next D0i3 entry. It's safe to unref. +	 */ +	if (tx_agg_ref) +		iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); +  	return ret;  } @@ -373,8 +639,16 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,  static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)  { +#ifdef CONFIG_IWLWIFI_DEBUGFS +	static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL }; + +	iwl_mvm_fw_error_dump(mvm); + +	/* notify the userspace about the error we had */ +	kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env); +#endif +  	iwl_trans_stop_device(mvm->trans); -	iwl_trans_stop_hw(mvm->trans, false);  	mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -386,6 +660,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)  		iwl_mvm_cleanup_iterator, mvm);  	mvm->p2p_device_vif = NULL; +	mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;  	iwl_mvm_reset_phy_ctxts(mvm);  	memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); @@ -393,6 +668,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)  	ieee80211_wake_queues(mvm->hw); +	/* cleanup all stale references (scan, roc), but keep the +	 * ucode_down ref until reconfig is complete */ +	iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); +  	mvm->vif_count = 0;  	mvm->rx_ba_sessions = 0;  } @@ -422,11 +701,15 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)  	mutex_lock(&mvm->mutex);  	clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); +	iwl_mvm_d0i3_enable_tx(mvm, NULL);  	ret = iwl_mvm_update_quotas(mvm, NULL);  	if (ret)  		IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",  			ret); +	/* allow transport/FW low power modes */ +	iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); +  	mutex_unlock(&mvm->mutex);  } @@ -434,9 +717,14 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	flush_work(&mvm->d0i3_exit_work);  	flush_work(&mvm->async_handlers_wk);  	mutex_lock(&mvm->mutex); + +	/* disallow low power states when the FW is down */ +	iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); +  	/* async_handlers_wk is now blocked */  	/* @@ -446,7 +734,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)  	cancel_work_sync(&mvm->roc_done_wk);  	iwl_trans_stop_device(mvm->trans); -	iwl_trans_stop_hw(mvm->trans, false);  	iwl_mvm_async_handlers_purge(mvm);  	/* async_handlers_list is empty and will stay empty: HW is stopped */ @@ -463,25 +750,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)  	cancel_work_sync(&mvm->async_handlers_wk);  } -static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac, -					struct ieee80211_vif *vif) -{ -	struct iwl_mvm *mvm = data; -	int ret; - -	ret = iwl_mvm_power_disable(mvm, vif); -	if (ret) -		IWL_ERR(mvm, "failed to disable power management\n"); -} - -static void iwl_mvm_power_update_iterator(void *data, u8 *mac, -					  struct ieee80211_vif *vif) -{ -	struct iwl_mvm *mvm = data; - -	iwl_mvm_power_update_mode(mvm, vif); -} -  static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)  {  	u16 i; @@ -496,6 +764,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)  	return NULL;  } +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				s8 tx_power) +{ +	/* FW is in charge of regulatory enforcement */ +	struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { +		.mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, +		.pwr_restriction = cpu_to_le16(tx_power), +	}; + +	return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, +				    sizeof(reduce_txpwr_cmd), +				    &reduce_txpwr_cmd); +} +  static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  				     struct ieee80211_vif *vif)  { @@ -516,26 +798,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	if (ret)  		goto out_unlock; -	/* -	 * TODO: remove this temporary code. -	 * Currently MVM FW supports power management only on single MAC. -	 * If new interface added, disable PM on existing interface. -	 * P2P device is a special case, since it is handled by FW similary to -	 * scan. If P2P deviced is added, PM remains enabled on existing -	 * interface. -	 * Note: the method below does not count the new interface being added -	 * at this moment. -	 */ +	/* Counting number of interfaces is needed for legacy PM */  	if (vif->type != NL80211_IFTYPE_P2P_DEVICE)  		mvm->vif_count++; -	if (mvm->vif_count > 1) { -		IWL_DEBUG_MAC80211(mvm, -				   "Disable power on existing interfaces\n"); -		ieee80211_iterate_active_interfaces_atomic( -					    mvm->hw, -					    IEEE80211_IFACE_ITER_NORMAL, -					    iwl_mvm_pm_disable_iterator, mvm); -	}  	/*  	 * The AP binding flow can be done only after the beacon @@ -548,10 +813,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	 * In short: there's not much we can do at this point, other than  	 * allocating resources :)  	 */ -	if (vif->type == NL80211_IFTYPE_AP) { +	if (vif->type == NL80211_IFTYPE_AP || +	    vif->type == NL80211_IFTYPE_ADHOC) {  		u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);  		ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, -					       qmask); +					       qmask, +					       ieee80211_vif_type_p2p(vif));  		if (ret) {  			IWL_ERR(mvm, "Failed to allocate bcast sta\n");  			goto out_release; @@ -565,20 +832,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	if (ret)  		goto out_release; -	/* -	 * Update power state on the new interface. Admittedly, based on -	 * mac80211 logics this power update will disable power management -	 */ -	iwl_mvm_power_update_mode(mvm, vif); +	ret = iwl_mvm_power_update_mac(mvm); +	if (ret) +		goto out_release;  	/* beacon filtering */ -	ret = iwl_mvm_disable_beacon_filter(mvm, vif); +	ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);  	if (ret)  		goto out_remove_mac;  	if (!mvm->bf_allowed_vif && -	    vif->type == NL80211_IFTYPE_STATION && !vif->p2p && -	    mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){ +	    vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {  		mvm->bf_allowed_vif = mvmvif;  		vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |  				     IEEE80211_VIF_SUPPORTS_CQM_RSSI; @@ -630,9 +894,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,   out_release:  	if (vif->type != NL80211_IFTYPE_P2P_DEVICE)  		mvm->vif_count--; -	ieee80211_iterate_active_interfaces( -		mvm->hw, IEEE80211_IFACE_ITER_NORMAL, -		iwl_mvm_power_update_iterator, mvm); +  	iwl_mvm_mac_ctxt_release(mvm, vif);   out_unlock:  	mutex_unlock(&mvm->mutex); @@ -698,7 +960,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,  	 * For AP/GO interface, the tear down of the resources allocated to the  	 * interface is be handled as part of the stop_ap flow.  	 */ -	if (vif->type == NL80211_IFTYPE_AP) { +	if (vif->type == NL80211_IFTYPE_AP || +	    vif->type == NL80211_IFTYPE_ADHOC) { +#ifdef CONFIG_NL80211_TESTMODE +		if (vif == mvm->noa_vif) { +			mvm->noa_vif = NULL; +			mvm->noa_duration = 0; +		} +#endif  		iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);  		goto out_release;  	} @@ -711,22 +980,10 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,  		mvmvif->phy_ctxt = NULL;  	} -	/* -	 * TODO: remove this temporary code. -	 * Currently MVM FW supports power management only on single MAC. -	 * Check if only one additional interface remains after removing -	 * current one. Update power mode on the remaining interface. -	 */  	if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)  		mvm->vif_count--; -	IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", -			   mvm->vif_count); -	if (mvm->vif_count == 1) { -		ieee80211_iterate_active_interfaces( -					mvm->hw, IEEE80211_IFACE_ITER_NORMAL, -					iwl_mvm_power_update_iterator, mvm); -	} +	iwl_mvm_power_update_mac(mvm);  	iwl_mvm_mac_ctxt_remove(mvm, vif);  out_release: @@ -734,23 +991,91 @@ out_release:  	mutex_unlock(&mvm->mutex);  } -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, -				s8 tx_power) +static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)  { -	/* FW is in charge of regulatory enforcement */ -	struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { -		.mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, -		.pwr_restriction = cpu_to_le16(tx_power), +	return 0; +} + +struct iwl_mvm_mc_iter_data { +	struct iwl_mvm *mvm; +	int port_id; +}; + +static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, +				      struct ieee80211_vif *vif) +{ +	struct iwl_mvm_mc_iter_data *data = _data; +	struct iwl_mvm *mvm = data->mvm; +	struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; +	int ret, len; + +	/* if we don't have free ports, mcast frames will be dropped */ +	if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) +		return; + +	if (vif->type != NL80211_IFTYPE_STATION || +	    !vif->bss_conf.assoc) +		return; + +	cmd->port_id = data->port_id++; +	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); +	len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + +	ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); +	if (ret) +		IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); +} + +static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) +{ +	struct iwl_mvm_mc_iter_data iter_data = { +		.mvm = mvm,  	}; -	return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, -				    sizeof(reduce_txpwr_cmd), -				    &reduce_txpwr_cmd); +	lockdep_assert_held(&mvm->mutex); + +	if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) +		return; + +	ieee80211_iterate_active_interfaces_atomic( +		mvm->hw, IEEE80211_IFACE_ITER_NORMAL, +		iwl_mvm_mc_iface_iterator, &iter_data);  } -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) +static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, +				     struct netdev_hw_addr_list *mc_list)  { -	return 0; +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mcast_filter_cmd *cmd; +	struct netdev_hw_addr *addr; +	int addr_count = netdev_hw_addr_list_count(mc_list); +	bool pass_all = false; +	int len; + +	if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) { +		pass_all = true; +		addr_count = 0; +	} + +	len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); +	cmd = kzalloc(len, GFP_ATOMIC); +	if (!cmd) +		return 0; + +	if (pass_all) { +		cmd->pass_all = 1; +		return (u64)(unsigned long)cmd; +	} + +	netdev_hw_addr_list_for_each(addr, mc_list) { +		IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", +				   cmd->count, addr->addr); +		memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], +		       addr->addr, ETH_ALEN); +		cmd->count++; +	} + +	return (u64)(unsigned long)cmd;  }  static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, @@ -758,22 +1083,177 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,  				     unsigned int *total_flags,  				     u64 multicast)  { +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; + +	mutex_lock(&mvm->mutex); + +	/* replace previous configuration */ +	kfree(mvm->mcast_filter_cmd); +	mvm->mcast_filter_cmd = cmd; + +	if (!cmd) +		goto out; + +	iwl_mvm_recalc_multicast(mvm); +out: +	mutex_unlock(&mvm->mutex);  	*total_flags = 0;  } -static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm, +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +struct iwl_bcast_iter_data { +	struct iwl_mvm *mvm; +	struct iwl_bcast_filter_cmd *cmd; +	u8 current_filter; +}; + +static void +iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif, +			 const struct iwl_fw_bcast_filter *in_filter, +			 struct iwl_fw_bcast_filter *out_filter) +{ +	struct iwl_fw_bcast_filter_attr *attr; +	int i; + +	memcpy(out_filter, in_filter, sizeof(*out_filter)); + +	for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) { +		attr = &out_filter->attrs[i]; + +		if (!attr->mask) +			break; + +		switch (attr->reserved1) { +		case cpu_to_le16(BC_FILTER_MAGIC_IP): +			if (vif->bss_conf.arp_addr_cnt != 1) { +				attr->mask = 0; +				continue; +			} + +			attr->val = vif->bss_conf.arp_addr_list[0]; +			break; +		case cpu_to_le16(BC_FILTER_MAGIC_MAC): +			attr->val = *(__be32 *)&vif->addr[2]; +			break; +		default: +			break; +		} +		attr->reserved1 = 0; +		out_filter->num_attrs++; +	} +} + +static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,  					  struct ieee80211_vif *vif)  { -	struct iwl_mcast_filter_cmd mcast_filter_cmd = { -		.pass_all = 1, +	struct iwl_bcast_iter_data *data = _data; +	struct iwl_mvm *mvm = data->mvm; +	struct iwl_bcast_filter_cmd *cmd = data->cmd; +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_fw_bcast_mac *bcast_mac; +	int i; + +	if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs))) +		return; + +	bcast_mac = &cmd->macs[mvmvif->id]; + +	/* +	 * enable filtering only for associated stations, but not for P2P +	 * Clients +	 */ +	if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || +	    !vif->bss_conf.assoc) +		return; + +	bcast_mac->default_discard = 1; + +	/* copy all configured filters */ +	for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) { +		/* +		 * Make sure we don't exceed our filters limit. +		 * if there is still a valid filter to be configured, +		 * be on the safe side and just allow bcast for this mac. +		 */ +		if (WARN_ON_ONCE(data->current_filter >= +				 ARRAY_SIZE(cmd->filters))) { +			bcast_mac->default_discard = 0; +			bcast_mac->attached_filters = 0; +			break; +		} + +		iwl_mvm_set_bcast_filter(vif, +					 &mvm->bcast_filters[i], +					 &cmd->filters[data->current_filter]); + +		/* skip current filter if it contains no attributes */ +		if (!cmd->filters[data->current_filter].num_attrs) +			continue; + +		/* attach the filter to current mac */ +		bcast_mac->attached_filters |= +				cpu_to_le16(BIT(data->current_filter)); + +		data->current_filter++; +	} +} + +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, +				    struct iwl_bcast_filter_cmd *cmd) +{ +	struct iwl_bcast_iter_data iter_data = { +		.mvm = mvm, +		.cmd = cmd,  	}; -	memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN); +	memset(cmd, 0, sizeof(*cmd)); +	cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters); +	cmd->max_macs = ARRAY_SIZE(cmd->macs); + +#ifdef CONFIG_IWLWIFI_DEBUGFS +	/* use debugfs filters/macs if override is configured */ +	if (mvm->dbgfs_bcast_filtering.override) { +		memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters, +		       sizeof(cmd->filters)); +		memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs, +		       sizeof(cmd->macs)); +		return true; +	} +#endif + +	/* if no filters are configured, do nothing */ +	if (!mvm->bcast_filters) +		return false; + +	/* configure and attach these filters for each associated sta vif */ +	ieee80211_iterate_active_interfaces( +		mvm->hw, IEEE80211_IFACE_ITER_NORMAL, +		iwl_mvm_bcast_filter_iterator, &iter_data); + +	return true; +} +static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, +					  struct ieee80211_vif *vif) +{ +	struct iwl_bcast_filter_cmd cmd; + +	if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) +		return 0; + +	if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) +		return 0; -	return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, -				    sizeof(mcast_filter_cmd), -				    &mcast_filter_cmd); +	return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, +				    sizeof(cmd), &cmd);  } +#else +static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, +						 struct ieee80211_vif *vif) +{ +	return 0; +} +#endif  static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  					     struct ieee80211_vif *vif, @@ -783,7 +1263,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	int ret; -	ret = iwl_mvm_mac_ctxt_changed(mvm, vif); +	/* +	 * Re-calculate the tsf id, as the master-slave relations depend on the +	 * beacon interval, which was not known when the station interface was +	 * added. +	 */ +	if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) +		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + +	ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);  	if (ret)  		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); @@ -795,31 +1283,66 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  				IWL_ERR(mvm, "failed to update quotas\n");  				return;  			} -			iwl_mvm_configure_mcast_filter(mvm, vif); + +			if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, +				     &mvm->status)) { +				/* +				 * If we're restarting then the firmware will +				 * obviously have lost synchronisation with +				 * the AP. It will attempt to synchronise by +				 * itself, but we can make it more reliable by +				 * scheduling a session protection time event. +				 * +				 * The firmware needs to receive a beacon to +				 * catch up with synchronisation, use 110% of +				 * the beacon interval. +				 * +				 * Set a large maximum delay to allow for more +				 * than a single interface. +				 */ +				u32 dur = (11 * vif->bss_conf.beacon_int) / 10; +				iwl_mvm_protect_session(mvm, vif, dur, dur, +							5 * dur); +			} + +			iwl_mvm_sf_update(mvm, vif, false); +			iwl_mvm_power_vif_assoc(mvm, vif); +			if (vif->p2p) +				iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);  		} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { +			/* +			 * If update fails - SF might be running in associated +			 * mode while disassociated - which is forbidden. +			 */ +			WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), +				  "Failed to update SF upon disassociation\n"); +  			/* remove AP station now that the MAC is unassoc */  			ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);  			if (ret)  				IWL_ERR(mvm, "failed to remove AP station\n"); + +			if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) +				mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;  			mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;  			/* remove quota for this interface */  			ret = iwl_mvm_update_quotas(mvm, NULL);  			if (ret)  				IWL_ERR(mvm, "failed to update quotas\n"); + +			if (vif->p2p) +				iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);  		} +		iwl_mvm_recalc_multicast(mvm); +		iwl_mvm_configure_bcast_filter(mvm, vif); +  		/* reset rssi values */  		mvmvif->bf_data.ave_beacon_signal = 0; -		if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { -			/* Workaround for FW bug, otherwise FW disables device -			 * power save upon disassociation -			 */ -			ret = iwl_mvm_power_update_mode(mvm, vif); -			if (ret) -				IWL_ERR(mvm, "failed to update power mode\n"); -		} -		iwl_mvm_bt_coex_vif_assoc(mvm, vif); +		iwl_mvm_bt_coex_vif_change(mvm); +		iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, +				    IEEE80211_SMPS_AUTOMATIC);  	} else if (changes & BSS_CHANGED_BEACON_INFO) {  		/*  		 * We received a beacon _after_ association so @@ -827,8 +1350,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  		 */  		iwl_mvm_remove_time_event(mvm, mvmvif,  					  &mvmvif->time_event_data); -	} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) { -		ret = iwl_mvm_power_update_mode(mvm, vif); +		iwl_mvm_sf_update(mvm, vif, false); +		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); +	} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | +			      BSS_CHANGED_QOS)) { +		ret = iwl_mvm_power_update_mac(mvm);  		if (ret)  			IWL_ERR(mvm, "failed to update power mode\n");  	} @@ -839,16 +1365,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  	}  	if (changes & BSS_CHANGED_CQM) { -		IWL_DEBUG_MAC80211(mvm, "cqm info_changed"); +		IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");  		/* reset cqm events tracking */  		mvmvif->bf_data.last_cqm_event = 0; -		ret = iwl_mvm_update_beacon_filter(mvm, vif); -		if (ret) -			IWL_ERR(mvm, "failed to update CQM thresholds\n"); +		if (mvmvif->bf_data.bf_enabled) { +			ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); +			if (ret) +				IWL_ERR(mvm, +					"failed to update CQM thresholds\n"); +		} +	} + +	if (changes & BSS_CHANGED_ARP_FILTER) { +		IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); +		iwl_mvm_configure_bcast_filter(mvm, vif);  	}  } -static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -861,6 +1396,13 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)  	if (ret)  		goto out_unlock; +	/* +	 * Re-calculate the tsf id, as the master-slave relations depend on the +	 * beacon interval, which was not known when the AP interface was added. +	 */ +	if (vif->type == NL80211_IFTYPE_AP) +		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); +  	/* Add the mac context */  	ret = iwl_mvm_mac_ctxt_add(mvm, vif);  	if (ret) @@ -871,26 +1413,36 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)  	if (ret)  		goto out_remove; -	mvmvif->ap_active = true; -  	/* Send the bcast station. At this stage the TBTT and DTIM time events  	 * are added and applied to the scheduler */  	ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);  	if (ret)  		goto out_unbind; +	/* must be set before quota calculations */ +	mvmvif->ap_ibss_active = true; + +	/* power updated needs to be done before quotas */ +	iwl_mvm_power_update_mac(mvm); +  	ret = iwl_mvm_update_quotas(mvm, vif);  	if (ret) -		goto out_rm_bcast; +		goto out_quota_failed; -	/* Need to update the P2P Device MAC */ +	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */  	if (vif->p2p && mvm->p2p_device_vif) -		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); +		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + +	iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); + +	iwl_mvm_bt_coex_vif_change(mvm);  	mutex_unlock(&mvm->mutex);  	return 0; -out_rm_bcast: +out_quota_failed: +	iwl_mvm_power_update_mac(mvm); +	mvmvif->ap_ibss_active = false;  	iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);  out_unbind:  	iwl_mvm_binding_remove_vif(mvm, vif); @@ -901,7 +1453,8 @@ out_unlock:  	return ret;  } -static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -910,30 +1463,48 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)  	mutex_lock(&mvm->mutex); -	mvmvif->ap_active = false; +	mvmvif->ap_ibss_active = false; -	/* Need to update the P2P Device MAC */ +	iwl_mvm_bt_coex_vif_change(mvm); + +	iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS); + +	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */  	if (vif->p2p && mvm->p2p_device_vif) -		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); +		iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);  	iwl_mvm_update_quotas(mvm, NULL);  	iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);  	iwl_mvm_binding_remove_vif(mvm, vif); + +	iwl_mvm_power_update_mac(mvm); +  	iwl_mvm_mac_ctxt_remove(mvm, vif);  	mutex_unlock(&mvm->mutex);  } -static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm, -					struct ieee80211_vif *vif, -					struct ieee80211_bss_conf *bss_conf, -					u32 changes) +static void +iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif, +				 struct ieee80211_bss_conf *bss_conf, +				 u32 changes)  { +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + +	/* Changes will be applied when the AP/IBSS is started */ +	if (!mvmvif->ap_ibss_active) +		return; + +	if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | +		       BSS_CHANGED_BANDWIDTH) && +	    iwl_mvm_mac_ctxt_changed(mvm, vif, false)) +		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); +  	/* Need to send a new beacon template to the FW */ -	if (changes & BSS_CHANGED_BEACON) { -		if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) -			IWL_WARN(mvm, "Failed updating beacon data\n"); -	} +	if (changes & BSS_CHANGED_BEACON && +	    iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) +		IWL_WARN(mvm, "Failed updating beacon data\n");  }  static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, @@ -945,12 +1516,16 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,  	mutex_lock(&mvm->mutex); +	if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) +		iwl_mvm_sched_scan_stop(mvm, true); +  	switch (vif->type) {  	case NL80211_IFTYPE_STATION:  		iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);  		break;  	case NL80211_IFTYPE_AP: -		iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes); +	case NL80211_IFTYPE_ADHOC: +		iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes);  		break;  	default:  		/* shouldn't happen */ @@ -972,13 +1547,30 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,  	mutex_lock(&mvm->mutex); -	if (mvm->scan_status == IWL_MVM_SCAN_NONE) -		ret = iwl_mvm_scan_request(mvm, vif, req); -	else +	switch (mvm->scan_status) { +	case IWL_MVM_SCAN_SCHED: +		ret = iwl_mvm_sched_scan_stop(mvm, true); +		if (ret) { +			ret = -EBUSY; +			goto out; +		} +		break; +	case IWL_MVM_SCAN_NONE: +		break; +	default:  		ret = -EBUSY; +		goto out; +	} -	mutex_unlock(&mvm->mutex); +	iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); +	ret = iwl_mvm_scan_request(mvm, vif, req); +	if (ret) +		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); +out: +	mutex_unlock(&mvm->mutex); +	/* make sure to flush the Rx handler before the next scan arrives */ +	iwl_mvm_wait_for_async_handlers(mvm);  	return ret;  } @@ -996,20 +1588,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,  static void  iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, -				  struct ieee80211_sta *sta, u16 tid, +				  struct ieee80211_sta *sta, u16 tids,  				  int num_frames,  				  enum ieee80211_frame_release_type reason,  				  bool more_data)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); -	/* TODO: how do we tell the fw to send frames for a specific TID */ +	/* Called when we need to transmit (a) frame(s) from mac80211 */ -	/* -	 * The fw will send EOSP notification when the last frame will be -	 * transmitted. -	 */ -	iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames); +	iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, +					  tids, more_data, false); +} + +static void +iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, +				    struct ieee80211_sta *sta, u16 tids, +				    int num_frames, +				    enum ieee80211_frame_release_type reason, +				    bool more_data) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	/* Called when we need to transmit (a) frame(s) from agg queue */ + +	iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, +					  tids, more_data, true);  }  static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, @@ -1018,12 +1622,26 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,  				   struct ieee80211_sta *sta)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); +	int tid;  	switch (cmd) {  	case STA_NOTIFY_SLEEP:  		if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)  			ieee80211_sta_block_awake(hw, sta, true); +		spin_lock_bh(&mvmsta->lock); +		for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { +			struct iwl_mvm_tid_data *tid_data; + +			tid_data = &mvmsta->tid_data[tid]; +			if (tid_data->state != IWL_AGG_ON && +			    tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) +				continue; +			if (iwl_mvm_tid_queued(tid_data) == 0) +				continue; +			ieee80211_sta_set_buffered(sta, tid, true); +		} +		spin_unlock_bh(&mvmsta->lock);  		/*  		 * The fw updates the STA to be asleep. Tx packets on the Tx  		 * queues to this station will not be transmitted. The fw will @@ -1040,6 +1658,28 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,  	}  } +static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, +				       struct ieee80211_vif *vif, +				       struct ieee80211_sta *sta) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + +	/* +	 * This is called before mac80211 does RCU synchronisation, +	 * so here we already invalidate our internal RCU-protected +	 * station pointer. The rest of the code will thus no longer +	 * be able to find the station this way, and we don't rely +	 * on further RCU synchronisation after the sta_state() +	 * callback deleted the station. +	 */ +	mutex_lock(&mvm->mutex); +	if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) +		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], +				   ERR_PTR(-ENOENT)); +	mutex_unlock(&mvm->mutex); +} +  static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  				 struct ieee80211_vif *vif,  				 struct ieee80211_sta *sta, @@ -1081,22 +1721,28 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  		ret = iwl_mvm_add_sta(mvm, vif, sta);  	} else if (old_state == IEEE80211_STA_NONE &&  		   new_state == IEEE80211_STA_AUTH) { +		/* +		 * EBS may be disabled due to previous failures reported by FW. +		 * Reset EBS status here assuming environment has been changed. +		 */ +		mvm->last_ebs_successful = true;  		ret = 0;  	} else if (old_state == IEEE80211_STA_AUTH &&  		   new_state == IEEE80211_STA_ASSOC) {  		ret = iwl_mvm_update_sta(mvm, vif, sta);  		if (ret == 0)  			iwl_mvm_rs_rate_init(mvm, sta, -					     mvmvif->phy_ctxt->channel->band); +					     mvmvif->phy_ctxt->channel->band, +					     true);  	} else if (old_state == IEEE80211_STA_ASSOC &&  		   new_state == IEEE80211_STA_AUTHORIZED) {  		/* enable beacon filtering */ -		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); +		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));  		ret = 0;  	} else if (old_state == IEEE80211_STA_AUTHORIZED &&  		   new_state == IEEE80211_STA_ASSOC) {  		/* disable beacon filtering */ -		WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif)); +		WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));  		ret = 0;  	} else if (old_state == IEEE80211_STA_ASSOC &&  		   new_state == IEEE80211_STA_AUTH) { @@ -1125,6 +1771,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)  	return 0;  } +static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, +				  struct ieee80211_sta *sta, u32 changed) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	if (vif->type == NL80211_IFTYPE_STATION && +	    changed & IEEE80211_RC_NSS_CHANGED) +		iwl_mvm_sf_update(mvm, vif, false); +} +  static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,  			       struct ieee80211_vif *vif, u16 ac,  			       const struct ieee80211_tx_queue_params *params) @@ -1142,7 +1799,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,  		int ret;  		mutex_lock(&mvm->mutex); -		ret = iwl_mvm_mac_ctxt_changed(mvm, vif); +		ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);  		mutex_unlock(&mvm->mutex);  		return ret;  	} @@ -1163,8 +1820,83 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,  	mutex_lock(&mvm->mutex);  	/* Try really hard to protect the session and hear a beacon */ -	iwl_mvm_protect_session(mvm, vif, duration, min_duration); +	iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); +	mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, +					struct ieee80211_vif *vif, +					struct cfg80211_sched_scan_request *req, +					struct ieee80211_sched_scan_ies *ies) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	int ret; + +	mutex_lock(&mvm->mutex); + +	if (!iwl_mvm_is_idle(mvm)) { +		ret = -EBUSY; +		goto out; +	} + +	switch (mvm->scan_status) { +	case IWL_MVM_SCAN_OS: +		IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n"); +		ret = iwl_mvm_cancel_scan(mvm); +		if (ret) { +			ret = -EBUSY; +			goto out; +		} + +		/* +		 * iwl_mvm_rx_scan_complete() will be called soon but will +		 * not reset the scan status as it won't be IWL_MVM_SCAN_OS +		 * any more since we queue the next scan immediately (below). +		 * We make sure it is called before the next scan starts by +		 * flushing the async-handlers work. +		 */ +		break; +	case IWL_MVM_SCAN_NONE: +		break; +	default: +		ret = -EBUSY; +		goto out; +	} + +	mvm->scan_status = IWL_MVM_SCAN_SCHED; + +	ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); +	if (ret) +		goto err; + +	ret = iwl_mvm_config_sched_scan_profiles(mvm, req); +	if (ret) +		goto err; + +	ret = iwl_mvm_sched_scan_start(mvm, req); +	if (!ret) +		goto out; +err: +	mvm->scan_status = IWL_MVM_SCAN_NONE; +out: +	mutex_unlock(&mvm->mutex); +	/* make sure to flush the Rx handler before the next scan arrives */ +	iwl_mvm_wait_for_async_handlers(mvm); +	return ret; +} + +static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, +				       struct ieee80211_vif *vif) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	int ret; + +	mutex_lock(&mvm->mutex); +	ret = iwl_mvm_sched_scan_stop(mvm, false);  	mutex_unlock(&mvm->mutex); +	iwl_mvm_wait_for_async_handlers(mvm); + +	return ret;  }  static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, @@ -1200,15 +1932,25 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  		 */  		return 0;  	default: -		return -EOPNOTSUPP; +		/* currently FW supports only one optional cipher scheme */ +		if (hw->n_cipher_schemes && +		    hw->cipher_schemes->cipher == key->cipher) +			key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; +		else +			return -EOPNOTSUPP;  	}  	mutex_lock(&mvm->mutex);  	switch (cmd) {  	case SET_KEY: -		if (vif->type == NL80211_IFTYPE_AP && !sta) { -			/* GTK on AP interface is a TX-only key, return 0 */ +		if ((vif->type == NL80211_IFTYPE_ADHOC || +		     vif->type == NL80211_IFTYPE_AP) && !sta) { +			/* +			 * GTK on AP interface is a TX-only key, return 0; +			 * on IBSS they're per-station and because we're lazy +			 * we don't support them for RX, so do the same. +			 */  			ret = 0;  			key->hw_key_idx = STA_KEY_IDX_INVALID;  			break; @@ -1252,6 +1994,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) +		return; +  	iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);  } @@ -1398,7 +2143,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,  		goto out;  	} -	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, +	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,  				       ctx->rx_chains_static,  				       ctx->rx_chains_dynamic);  	if (ret) { @@ -1436,13 +2181,15 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,  	if (WARN_ONCE((phy_ctxt->ref > 1) &&  		      (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |  				   IEEE80211_CHANCTX_CHANGE_RX_CHAINS | -				   IEEE80211_CHANCTX_CHANGE_RADAR)), +				   IEEE80211_CHANCTX_CHANGE_RADAR | +				   IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),  		      "Cannot change PHY. Ref=%d, changed=0x%X\n",  		      phy_ctxt->ref, changed))  		return;  	mutex_lock(&mvm->mutex); -	iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, +	iwl_mvm_bt_coex_vif_change(mvm); +	iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,  				 ctx->rx_chains_static,  				 ctx->rx_chains_dynamic);  	mutex_unlock(&mvm->mutex); @@ -1464,14 +2211,19 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,  	switch (vif->type) {  	case NL80211_IFTYPE_AP: +		/* Unless it's a CSA flow we have nothing to do here */ +		if (vif->csa_active) { +			mvmvif->ap_ibss_active = true; +			break; +		} +	case NL80211_IFTYPE_ADHOC:  		/*  		 * The AP binding flow is handled as part of the start_ap flow -		 * (in bss_info_changed). +		 * (in bss_info_changed), similarly for IBSS.  		 */  		ret = 0;  		goto out_unlock;  	case NL80211_IFTYPE_STATION: -	case NL80211_IFTYPE_ADHOC:  	case NL80211_IFTYPE_MONITOR:  		break;  	default: @@ -1484,7 +2236,12 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,  		goto out_unlock;  	/* -	 * Setting the quota at this stage is only required for monitor +	 * Power state must be updated before quotas, +	 * otherwise fw will complain. +	 */ +	iwl_mvm_power_update_mac(mvm); + +	/* Setting the quota at this stage is only required for monitor  	 * interfaces. For the other types, the bss_info changed flow  	 * will handle quota settings.  	 */ @@ -1495,10 +2252,17 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,  			goto out_remove_binding;  	} +	/* Handle binding during CSA */ +	if (vif->type == NL80211_IFTYPE_AP) { +		iwl_mvm_update_quotas(mvm, vif); +		iwl_mvm_mac_ctxt_changed(mvm, vif, false); +	} +  	goto out_unlock;   out_remove_binding:  	iwl_mvm_binding_remove_vif(mvm, vif); +	iwl_mvm_power_update_mac(mvm);   out_unlock:  	mutex_unlock(&mvm->mutex);  	if (ret) @@ -1517,21 +2281,30 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,  	iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); -	if (vif->type == NL80211_IFTYPE_AP) -		goto out_unlock; -  	switch (vif->type) { +	case NL80211_IFTYPE_ADHOC: +		goto out_unlock;  	case NL80211_IFTYPE_MONITOR:  		mvmvif->monitor_active = false;  		iwl_mvm_update_quotas(mvm, NULL);  		break; +	case NL80211_IFTYPE_AP: +		/* This part is triggered only during CSA */ +		if (!vif->csa_active || !mvmvif->ap_ibss_active) +			goto out_unlock; + +		mvmvif->ap_ibss_active = false; +		iwl_mvm_update_quotas(mvm, NULL); +		/*TODO: bt_coex notification here? */  	default:  		break;  	}  	iwl_mvm_binding_remove_vif(mvm, vif); +  out_unlock:  	mvmvif->phy_ctxt = NULL; +	iwl_mvm_power_update_mac(mvm);  	mutex_unlock(&mvm->mutex);  } @@ -1550,16 +2323,121 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,  	return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);  } -static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw, +#ifdef CONFIG_NL80211_TESTMODE +static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { +	[IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, +	[IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, +	[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, +}; + +static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,  				      struct ieee80211_vif *vif, -				      enum ieee80211_rssi_event rssi_event) +				      void *data, int len) +{ +	struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; +	int err; +	u32 noa_duration; + +	err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); +	if (err) +		return err; + +	if (!tb[IWL_MVM_TM_ATTR_CMD]) +		return -EINVAL; + +	switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { +	case IWL_MVM_TM_CMD_SET_NOA: +		if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || +		    !vif->bss_conf.enable_beacon || +		    !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) +			return -EINVAL; + +		noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); +		if (noa_duration >= vif->bss_conf.beacon_int) +			return -EINVAL; + +		mvm->noa_duration = noa_duration; +		mvm->noa_vif = vif; + +		return iwl_mvm_update_quotas(mvm, NULL); +	case IWL_MVM_TM_CMD_SET_BEACON_FILTER: +		/* must be associated client vif - ignore authorized */ +		if (!vif || vif->type != NL80211_IFTYPE_STATION || +		    !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || +		    !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) +			return -EINVAL; + +		if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) +			return iwl_mvm_enable_beacon_filter(mvm, vif, 0); +		return iwl_mvm_disable_beacon_filter(mvm, vif, 0); +	} + +	return -EOPNOTSUPP; +} + +static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, +				    struct ieee80211_vif *vif, +				    void *data, int len) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	int err; + +	mutex_lock(&mvm->mutex); +	err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); +	mutex_unlock(&mvm->mutex); + +	return err; +} +#endif + +static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw, +					  struct ieee80211_vif *vif, +					  struct cfg80211_chan_def *chandef) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); +	if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active, +		 "Another CSA is already in progress")) +		goto out_unlock; + +	IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n", +			   chandef->center_freq1); +	mvm->csa_vif = vif; + +out_unlock: +	mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, u32 queues, bool drop)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif; +	struct iwl_mvm_sta *mvmsta; + +	if (!vif || vif->type != NL80211_IFTYPE_STATION) +		return; + +	mutex_lock(&mvm->mutex); +	mvmvif = iwl_mvm_vif_from_mac80211(vif); +	mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); -	iwl_mvm_bt_rssi_event(mvm, vif, rssi_event); +	if (WARN_ON_ONCE(!mvmsta)) +		goto done; + +	if (drop) { +		if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true)) +			IWL_ERR(mvm, "flush request fail\n"); +	} else { +		iwl_trans_wait_tx_queue_empty(mvm->trans, +					      mvmsta->tfd_queue_msk); +	} +done: +	mutex_unlock(&mvm->mutex);  } -struct ieee80211_ops iwl_mvm_hw_ops = { +const struct ieee80211_ops iwl_mvm_hw_ops = {  	.tx = iwl_mvm_mac_tx,  	.ampdu_action = iwl_mvm_mac_ampdu_action,  	.start = iwl_mvm_mac_start, @@ -1568,33 +2446,44 @@ struct ieee80211_ops iwl_mvm_hw_ops = {  	.add_interface = iwl_mvm_mac_add_interface,  	.remove_interface = iwl_mvm_mac_remove_interface,  	.config = iwl_mvm_mac_config, +	.prepare_multicast = iwl_mvm_prepare_multicast,  	.configure_filter = iwl_mvm_configure_filter,  	.bss_info_changed = iwl_mvm_bss_info_changed,  	.hw_scan = iwl_mvm_mac_hw_scan,  	.cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, +	.sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,  	.sta_state = iwl_mvm_mac_sta_state,  	.sta_notify = iwl_mvm_mac_sta_notify,  	.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, +	.release_buffered_frames = iwl_mvm_mac_release_buffered_frames,  	.set_rts_threshold = iwl_mvm_mac_set_rts_threshold, +	.sta_rc_update = iwl_mvm_sta_rc_update,  	.conf_tx = iwl_mvm_mac_conf_tx,  	.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, +	.flush = iwl_mvm_mac_flush, +	.sched_scan_start = iwl_mvm_mac_sched_scan_start, +	.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,  	.set_key = iwl_mvm_mac_set_key,  	.update_tkip_key = iwl_mvm_mac_update_tkip_key,  	.remain_on_channel = iwl_mvm_roc,  	.cancel_remain_on_channel = iwl_mvm_cancel_roc, -	.rssi_callback = iwl_mvm_mac_rssi_callback, -  	.add_chanctx = iwl_mvm_add_chanctx,  	.remove_chanctx = iwl_mvm_remove_chanctx,  	.change_chanctx = iwl_mvm_change_chanctx,  	.assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,  	.unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, -	.start_ap = iwl_mvm_start_ap, -	.stop_ap = iwl_mvm_stop_ap, +	.start_ap = iwl_mvm_start_ap_ibss, +	.stop_ap = iwl_mvm_stop_ap_ibss, +	.join_ibss = iwl_mvm_start_ap_ibss, +	.leave_ibss = iwl_mvm_stop_ap_ibss,  	.set_tim = iwl_mvm_set_tim, +	.channel_switch_beacon = iwl_mvm_channel_switch_beacon, + +	CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) +  #ifdef CONFIG_PM_SLEEP  	/* look at d3.c */  	.suspend = iwl_mvm_suspend,  | 
