Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f0a7c67 authored by Andrei Otcheretianski's avatar Andrei Otcheretianski Committed by Emmanuel Grumbach
Browse files

iwlwifi: mvm: Reflect GO channel switch in NoA



According to the spec, GO/AP should perform the channel switch just
before "beacon 0". However, since the exact timing isn't defined,
it may result in a sudden GO disappearance from the channel.
Prevent potential packet loss when performing the CS by scheduling
NoA time event and executing the channel switch flow when a notification
from fw is received.

Signed-off-by: default avatarAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 664322fa
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -538,6 +538,9 @@ enum iwl_time_event_type {
	/* WiDi Sync Events */
	/* WiDi Sync Events */
	TE_WIDI_TX_SYNC,
	TE_WIDI_TX_SYNC,


	/* Channel Switch NoA */
	TE_P2P_GO_CSA_NOA,

	TE_MAX
	TE_MAX
}; /* MAC_EVENT_TYPE_API_E_VER_1 */
}; /* MAC_EVENT_TYPE_API_E_VER_1 */


+32 −9
Original line number Original line Diff line number Diff line
@@ -67,6 +67,7 @@
#include "iwl-prph.h"
#include "iwl-prph.h"
#include "fw-api.h"
#include "fw-api.h"
#include "mvm.h"
#include "mvm.h"
#include "time-event.h"


const u8 iwl_mvm_ac_to_tx_fifo[] = {
const u8 iwl_mvm_ac_to_tx_fifo[] = {
	IWL_MVM_TX_FIFO_VO,
	IWL_MVM_TX_FIFO_VO,
@@ -1200,6 +1201,35 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
	return 0;
	return 0;
}
}


static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
				   struct ieee80211_vif *csa_vif, u32 gp2)
{
	struct iwl_mvm_vif *mvmvif =
			iwl_mvm_vif_from_mac80211(csa_vif);

	if (!ieee80211_csa_is_complete(csa_vif)) {
		int c = ieee80211_csa_update_counter(csa_vif);

		iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
		if (csa_vif->p2p &&
		    !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
			u32 rel_time = (c + 1) *
				       csa_vif->bss_conf.beacon_int -
				       IWL_MVM_CHANNEL_SWITCH_TIME;
			u32 apply_time = gp2 + rel_time * 1024;

			iwl_mvm_schedule_csa_noa(mvm, csa_vif,
						 IWL_MVM_CHANNEL_SWITCH_TIME -
						 IWL_MVM_CHANNEL_SWITCH_MARGIN,
						 apply_time);
		}
	} else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
		/* we don't have CSA NoA scheduled yet, switch now */
		ieee80211_csa_finish(csa_vif);
		RCU_INIT_POINTER(mvm->csa_vif, NULL);
	}
}

int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
			    struct iwl_rx_cmd_buffer *rxb,
			    struct iwl_rx_cmd_buffer *rxb,
			    struct iwl_device_cmd *cmd)
			    struct iwl_device_cmd *cmd)
@@ -1234,15 +1264,8 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,


	csa_vif = rcu_dereference_protected(mvm->csa_vif,
	csa_vif = rcu_dereference_protected(mvm->csa_vif,
					    lockdep_is_held(&mvm->mutex));
					    lockdep_is_held(&mvm->mutex));
	if (unlikely(csa_vif && csa_vif->csa_active)) {
	if (unlikely(csa_vif && csa_vif->csa_active))
		if (!ieee80211_csa_is_complete(csa_vif)) {
		iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
			ieee80211_csa_update_counter(csa_vif);
			iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
		} else {
			ieee80211_csa_finish(csa_vif);
			RCU_INIT_POINTER(mvm->csa_vif, NULL);
		}
	}


	return 0;
	return 0;
}
}
+4 −1
Original line number Original line Diff line number Diff line
@@ -1608,8 +1608,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
	mutex_lock(&mvm->mutex);
	mutex_lock(&mvm->mutex);


	/* Handle AP stop while in CSA */
	/* Handle AP stop while in CSA */
	if (rcu_access_pointer(mvm->csa_vif) == vif)
	if (rcu_access_pointer(mvm->csa_vif) == vif) {
		iwl_mvm_remove_time_event(mvm, mvmvif,
					  &mvmvif->time_event_data);
		RCU_INIT_POINTER(mvm->csa_vif, NULL);
		RCU_INIT_POINTER(mvm->csa_vif, NULL);
	}


	mvmvif->ap_ibss_active = false;
	mvmvif->ap_ibss_active = false;
	mvm->ap_last_beacon_gp2 = 0;
	mvm->ap_last_beacon_gp2 = 0;
+12 −0
Original line number Original line Diff line number Diff line
@@ -83,6 +83,18 @@
#define IWL_RSSI_OFFSET 50
#define IWL_RSSI_OFFSET 50
#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8


/*
 * The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
 * TBTT. This value should be big enough to ensure that we switch in time.
 */
#define IWL_MVM_CHANNEL_SWITCH_TIME 40

/*
 * This value (in TUs) is used to fine tune the CSA NoA end time which should
 * be just before "beacon 0" TBTT.
 */
#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4

enum iwl_mvm_tx_fifo {
enum iwl_mvm_tx_fifo {
	IWL_MVM_TX_FIFO_BK = 0,
	IWL_MVM_TX_FIFO_BK = 0,
	IWL_MVM_TX_FIFO_BE,
	IWL_MVM_TX_FIFO_BE,
+73 −0
Original line number Original line Diff line number Diff line
@@ -138,6 +138,41 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
	schedule_work(&mvm->roc_done_wk);
	schedule_work(&mvm->roc_done_wk);
}
}


static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
{
	struct ieee80211_vif *csa_vif;

	rcu_read_lock();

	csa_vif = rcu_dereference(mvm->csa_vif);
	if (!csa_vif || !csa_vif->csa_active)
		goto out_unlock;

	IWL_DEBUG_TE(mvm, "CSA NOA started\n");

	/*
	 * CSA NoA is started but we still have beacons to
	 * transmit on the current channel.
	 * So we just do nothing here and the switch
	 * will be performed on the last TBTT.
	 */
	if (!ieee80211_csa_is_complete(csa_vif)) {
		IWL_WARN(mvm, "CSA NOA started too early\n");
		goto out_unlock;
	}

	ieee80211_csa_finish(csa_vif);

	rcu_read_unlock();

	RCU_INIT_POINTER(mvm->csa_vif, NULL);

	return;

out_unlock:
	rcu_read_unlock();
}

static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
					struct ieee80211_vif *vif,
					struct ieee80211_vif *vif,
					const char *errmsg)
					const char *errmsg)
@@ -213,6 +248,14 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
			set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
			set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
			iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
			iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
			ieee80211_ready_on_channel(mvm->hw);
			ieee80211_ready_on_channel(mvm->hw);
		} else if (te_data->vif->type == NL80211_IFTYPE_AP) {
			if (le32_to_cpu(notif->status))
				iwl_mvm_csa_noa_start(mvm);
			else
				IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");

			/* we don't need it anymore */
			iwl_mvm_te_clear_data(mvm, te_data);
		}
		}
	} else {
	} else {
		IWL_WARN(mvm, "Got TE with unknown action\n");
		IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -538,3 +581,33 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)


	iwl_mvm_roc_finished(mvm);
	iwl_mvm_roc_finished(mvm);
}
}

int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
			      struct ieee80211_vif *vif,
			      u32 duration, u32 apply_time)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
	struct iwl_time_event_cmd time_cmd = {};

	lockdep_assert_held(&mvm->mutex);

	if (te_data->running) {
		IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n");
		return -EBUSY;
	}

	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
	time_cmd.id_and_color =
		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
	time_cmd.id = cpu_to_le32(TE_P2P_GO_CSA_NOA);
	time_cmd.apply_time = cpu_to_le32(apply_time);
	time_cmd.max_frags = TE_V2_FRAG_NONE;
	time_cmd.duration = cpu_to_le32(duration);
	time_cmd.repeat = 1;
	time_cmd.interval = cpu_to_le32(1);
	time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
				      TE_V2_ABSENCE);

	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
Loading