1
0
Fork 0

iwlwifi: mvm: add CSA absent time event for clients

Add an absent time event when pre_channel_switch is called and use the
time event started indication to set the disable_tx bit instead of
doing it in unassign_vif().  This is done so that the firmware queues
are stopped before the actual switch takes place to avoid losing
packets while the AP/GO is performing its actual switch.

Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
hifive-unleashed-5.1
Luciano Coelho 2014-11-10 11:10:14 +02:00 committed by Emmanuel Grumbach
parent 4741dd049a
commit dc88b4baa9
5 changed files with 81 additions and 18 deletions

View File

@ -2891,7 +2891,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_vif *disabled_vif = NULL;
struct iwl_mvm_sta *mvmsta;
lockdep_assert_held(&mvm->mutex);
@ -2925,12 +2924,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
disabled_vif = vif;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
mvmvif->ap_sta_id);
if (!WARN_ON(!mvmsta))
iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
break;
default:
@ -3167,6 +3160,7 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u32 apply_time;
int ret;
mutex_lock(&mvm->mutex);
@ -3194,6 +3188,17 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
}
break;
case NL80211_IFTYPE_STATION:
apply_time = chsw->timestamp +
(vif->bss_conf.beacon_int * chsw->count * 1024);
if (chsw->block_tx)
iwl_mvm_csa_client_absent(mvm, vif);
iwl_mvm_schedule_csa_period(mvm, vif,
IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT,
apply_time);
break;
default:
break;
}

View File

@ -87,11 +87,12 @@
/* A TimeUnit is 1024 microsecond */
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
/* This value represents the number of TUs before CSA "beacon 0" TBTT
* when the CSA time-event needs to be scheduled to start. It must be
* big enough to ensure that we switch in time.
/* These values represent the number of TUs before CSA "beacon 0" TBTT
* when the CSA time-event needs to be scheduled to start. They must
* be big enough to ensure that we switch in time.
*/
#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 110
/*
* This value (in TUs) is used to fine tune the CSA NoA end time which should
@ -797,6 +798,26 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
}
/* Must be called with rcu_read_lock() held and it can only be
* released when mvmsta is not needed anymore.
*/
static inline struct iwl_mvm_sta *
iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
{
struct ieee80211_sta *sta;
if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
return NULL;
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
/* This can happen if the station has been removed right now */
if (IS_ERR_OR_NULL(sta))
return NULL;
return iwl_mvm_sta_from_mac80211(sta);
}
static inline struct iwl_mvm_sta *
iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
{

View File

@ -1732,3 +1732,18 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
}
}
void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvmsta;
rcu_read_lock();
mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
if (!WARN_ON(!mvmsta))
iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
rcu_read_unlock();
}

View File

@ -422,5 +422,6 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
bool disable);
void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
#endif /* __sta_h__ */

View File

@ -191,6 +191,33 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
return true;
}
static void
iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data,
struct iwl_time_event_notif *notif)
{
if (!le32_to_cpu(notif->status)) {
IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
return;
}
switch (te_data->vif->type) {
case NL80211_IFTYPE_AP:
iwl_mvm_csa_noa_start(mvm);
break;
case NL80211_IFTYPE_STATION:
iwl_mvm_csa_client_absent(mvm, te_data->vif);
break;
default:
/* should never happen */
WARN_ON_ONCE(1);
break;
}
/* we don't need it anymore */
iwl_mvm_te_clear_data(mvm, te_data);
}
/*
* Handles a FW notification for an event that is known to the driver.
*
@ -252,14 +279,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
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 if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
}
} else {
IWL_WARN(mvm, "Got TE with unknown action\n");