aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-11-10 04:10:14 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-11-24 01:30:31 -0500
commitdc88b4baa9d16fdf15f76b6277461e6d9e4eb8e6 (patch)
tree8b5f98f7c7bbeb80d5fff39f2d048bf4a991c51b /drivers/net
parent4741dd049a27fa6490e502d42ff51417c58ac7cd (diff)
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>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c19
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h27
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c15
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c37
5 files changed, 81 insertions, 18 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index fdeaab3457a3..ac1cef6a6b03 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2891,7 +2891,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
2891{ 2891{
2892 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 2892 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2893 struct ieee80211_vif *disabled_vif = NULL; 2893 struct ieee80211_vif *disabled_vif = NULL;
2894 struct iwl_mvm_sta *mvmsta;
2895 2894
2896 lockdep_assert_held(&mvm->mutex); 2895 lockdep_assert_held(&mvm->mutex);
2897 2896
@@ -2925,12 +2924,6 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
2925 2924
2926 disabled_vif = vif; 2925 disabled_vif = vif;
2927 2926
2928 mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
2929 mvmvif->ap_sta_id);
2930
2931 if (!WARN_ON(!mvmsta))
2932 iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
2933
2934 iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); 2927 iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
2935 break; 2928 break;
2936 default: 2929 default:
@@ -3167,6 +3160,7 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
3167 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 3160 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
3168 struct ieee80211_vif *csa_vif; 3161 struct ieee80211_vif *csa_vif;
3169 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 3162 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3163 u32 apply_time;
3170 int ret; 3164 int ret;
3171 3165
3172 mutex_lock(&mvm->mutex); 3166 mutex_lock(&mvm->mutex);
@@ -3194,6 +3188,17 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
3194 } 3188 }
3195 3189
3196 break; 3190 break;
3191 case NL80211_IFTYPE_STATION:
3192 apply_time = chsw->timestamp +
3193 (vif->bss_conf.beacon_int * chsw->count * 1024);
3194
3195 if (chsw->block_tx)
3196 iwl_mvm_csa_client_absent(mvm, vif);
3197
3198 iwl_mvm_schedule_csa_period(mvm, vif,
3199 IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT,
3200 apply_time);
3201 break;
3197 default: 3202 default:
3198 break; 3203 break;
3199 } 3204 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 2c54c522b8c6..2816c6b9222b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -87,11 +87,12 @@
87/* A TimeUnit is 1024 microsecond */ 87/* A TimeUnit is 1024 microsecond */
88#define MSEC_TO_TU(_msec) (_msec*1000/1024) 88#define MSEC_TO_TU(_msec) (_msec*1000/1024)
89 89
90/* This value represents the number of TUs before CSA "beacon 0" TBTT 90/* These values represent the number of TUs before CSA "beacon 0" TBTT
91 * when the CSA time-event needs to be scheduled to start. It must be 91 * when the CSA time-event needs to be scheduled to start. They must
92 * big enough to ensure that we switch in time. 92 * be big enough to ensure that we switch in time.
93 */ 93 */
94#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40 94#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
95#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 110
95 96
96/* 97/*
97 * This value (in TUs) is used to fine tune the CSA NoA end time which should 98 * 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)
797 test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); 798 test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
798} 799}
799 800
801/* Must be called with rcu_read_lock() held and it can only be
802 * released when mvmsta is not needed anymore.
803 */
804static inline struct iwl_mvm_sta *
805iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
806{
807 struct ieee80211_sta *sta;
808
809 if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
810 return NULL;
811
812 sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
813
814 /* This can happen if the station has been removed right now */
815 if (IS_ERR_OR_NULL(sta))
816 return NULL;
817
818 return iwl_mvm_sta_from_mac80211(sta);
819}
820
800static inline struct iwl_mvm_sta * 821static inline struct iwl_mvm_sta *
801iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) 822iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
802{ 823{
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index a38f1f151ff5..d86fe432e51f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -1732,3 +1732,18 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
1732 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); 1732 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
1733 } 1733 }
1734} 1734}
1735
1736void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
1737{
1738 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1739 struct iwl_mvm_sta *mvmsta;
1740
1741 rcu_read_lock();
1742
1743 mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
1744
1745 if (!WARN_ON(!mvmsta))
1746 iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
1747
1748 rcu_read_unlock();
1749}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index 2c869bcb9490..d8f48975ad08 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -422,5 +422,6 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
422void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, 422void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
423 struct iwl_mvm_vif *mvmvif, 423 struct iwl_mvm_vif *mvmvif,
424 bool disable); 424 bool disable);
425void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
425 426
426#endif /* __sta_h__ */ 427#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index 8cf246b695c4..1a537be216ce 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -191,6 +191,33 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
191 return true; 191 return true;
192} 192}
193 193
194static void
195iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
196 struct iwl_mvm_time_event_data *te_data,
197 struct iwl_time_event_notif *notif)
198{
199 if (!le32_to_cpu(notif->status)) {
200 IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
201 return;
202 }
203
204 switch (te_data->vif->type) {
205 case NL80211_IFTYPE_AP:
206 iwl_mvm_csa_noa_start(mvm);
207 break;
208 case NL80211_IFTYPE_STATION:
209 iwl_mvm_csa_client_absent(mvm, te_data->vif);
210 break;
211 default:
212 /* should never happen */
213 WARN_ON_ONCE(1);
214 break;
215 }
216
217 /* we don't need it anymore */
218 iwl_mvm_te_clear_data(mvm, te_data);
219}
220
194/* 221/*
195 * Handles a FW notification for an event that is known to the driver. 222 * Handles a FW notification for an event that is known to the driver.
196 * 223 *
@@ -252,14 +279,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
252 set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); 279 set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
253 iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); 280 iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
254 ieee80211_ready_on_channel(mvm->hw); 281 ieee80211_ready_on_channel(mvm->hw);
255 } else if (te_data->vif->type == NL80211_IFTYPE_AP) { 282 } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
256 if (le32_to_cpu(notif->status)) 283 iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
257 iwl_mvm_csa_noa_start(mvm);
258 else
259 IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
260
261 /* we don't need it anymore */
262 iwl_mvm_te_clear_data(mvm, te_data);
263 } 284 }
264 } else { 285 } else {
265 IWL_WARN(mvm, "Got TE with unknown action\n"); 286 IWL_WARN(mvm, "Got TE with unknown action\n");