2014-11-10 09:10:34

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 00/16] iwlwifi: mvm: more CSA patches

From: Luciano Coelho <[email protected]>

Hi,

These patches finalize the CSA implementation for iwlmvm. These are
the remaining patches that were still pending in our internal tree.
After this, I believe we are aligned.

This series depends on the mac80211/cfg80211 series I sent to Johannes
last Friday (2014-11-07), we probably need to wait those to get
applied to sync things up.

There is one more mac80211 patch that I need to send. This series
removes the usage of the channel_switch_beacon operation in iwlmvm.
It's the last user of it in the mainline, so we can remove it from
mac80211 after the trees are in sync.

--
Luca.

Andrei Otcheretianski (2):
iwlwifi: mvm: Handle failed beacon transmissions during CSA
iwlwifi: mvm: Insert DS Parameter Set placeholder in probes

Luciano Coelho (14):
iwlwifi: mvm: use new pre_channel_switch op instead of
channel_switch_beacon
iwlwifi: mvm: only save csa_vif in AP/GO mode
iwlwifi: mvm: refactor iwl_mvm_switch_vif_chanctx to support other
modes
iwlwifi: mvm: add support for CHANCTX_SWMODE_REASSIGN_VIF
iwlwifi: mvm: return the actual error code when switch_vif_chanctx
fails
iwlwifi: mvm: disable PS during channel switch
iwlwifi: mvm: use switching_chanctx argument instead of csa_active
iwlwifi: mvm: add CSA absent time event for clients
iwlwifi: mvm: schedule CSA time event a bit before beacon 1
iwlwifi: mvm: finalize on post_switch instead of unassign
iwlwifi: mvm: add a channel_switch op to bypass mac80211 timer
iwlwifi: mvm: disable beacon filtering during CSA
iwlwifi: mvm: clear TE data if CSA time event fails to start
iwlwifi: mvm: protect session during CSA

drivers/net/wireless/iwlwifi/iwl-fw.h | 4 +-
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 21 ++-
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 254 +++++++++++++++++++++-----
drivers/net/wireless/iwlwifi/mvm/mvm.h | 36 +++-
drivers/net/wireless/iwlwifi/mvm/scan.c | 53 +++++-
drivers/net/wireless/iwlwifi/mvm/sta.c | 15 ++
drivers/net/wireless/iwlwifi/mvm/sta.h | 1 +
drivers/net/wireless/iwlwifi/mvm/time-event.c | 39 +++-
8 files changed, 356 insertions(+), 67 deletions(-)

--
2.1.1



2014-11-10 09:23:51

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 11/16] iwlwifi: mvm: finalize on post_switch instead of unassign

From: Luciano Coelho <[email protected]>

Now that mac80211 waits for the first beacon on the new channel before
calling post_channel_switch, the reconfiguration of the firmware
should be done in the post_channel_switch operation instead of when
assigning the vif to the new context.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 31 ++++++++++++++++++-----------
1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 3fb2b49..de6a64d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2714,23 +2714,13 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
}

/* Handle binding during CSA */
- if ((vif->type == NL80211_IFTYPE_AP) ||
- (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+ if (vif->type == NL80211_IFTYPE_AP) {
iwl_mvm_update_quotas(mvm, NULL);
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}

if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
- struct iwl_mvm_sta *mvmsta;
-
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
- mvmvif->ap_sta_id);
-
- if (WARN_ON(!mvmsta))
- goto out;
-
- /* TODO: only re-enable after the first beacon */
- iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+ iwl_mvm_update_quotas(mvm, NULL);
}

goto out;
@@ -3101,10 +3091,27 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,

mutex_lock(&mvm->mutex);

+ if (vif->type == NL80211_IFTYPE_STATION) {
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
+ mvmvif->ap_sta_id);
+
+ if (WARN_ON(!mvmsta)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
+
+ iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
+
mvmvif->ps_disabled = false;

ret = iwl_mvm_power_update_ps(mvm);

+out_unlock:
mutex_unlock(&mvm->mutex);

return ret;
--
2.1.1


2014-11-10 13:21:07

by Emmanuel Grumbach

[permalink] [raw]
Subject: Re: [PATCH 00/16] iwlwifi: mvm: more CSA patches

>
> Hi,
>
> These patches finalize the CSA implementation for iwlmvm. These are
> the remaining patches that were still pending in our internal tree.
> After this, I believe we are aligned.

Thanks Luca!

>
> This series depends on the mac80211/cfg80211 series I sent to Johannes
> last Friday (2014-11-07), we probably need to wait those to get
> applied to sync things up.
>

I will - no problem.

> There is one more mac80211 patch that I need to send. This series
> removes the usage of the channel_switch_beacon operation in iwlmvm.
> It's the last user of it in the mainline, so we can remove it from
> mac80211 after the trees are in sync.
>
> --
> Luca.
>
> Andrei Otcheretianski (2):
> iwlwifi: mvm: Handle failed beacon transmissions during CSA
> iwlwifi: mvm: Insert DS Parameter Set placeholder in probes
>
> Luciano Coelho (14):
> iwlwifi: mvm: use new pre_channel_switch op instead of
> channel_switch_beacon
> iwlwifi: mvm: only save csa_vif in AP/GO mode
> iwlwifi: mvm: refactor iwl_mvm_switch_vif_chanctx to support other
> modes
> iwlwifi: mvm: add support for CHANCTX_SWMODE_REASSIGN_VIF
> iwlwifi: mvm: return the actual error code when switch_vif_chanctx
> fails
> iwlwifi: mvm: disable PS during channel switch
> iwlwifi: mvm: use switching_chanctx argument instead of csa_active
> iwlwifi: mvm: add CSA absent time event for clients
> iwlwifi: mvm: schedule CSA time event a bit before beacon 1
> iwlwifi: mvm: finalize on post_switch instead of unassign
> iwlwifi: mvm: add a channel_switch op to bypass mac80211 timer
> iwlwifi: mvm: disable beacon filtering during CSA
> iwlwifi: mvm: clear TE data if CSA time event fails to start
> iwlwifi: mvm: protect session during CSA
>
> drivers/net/wireless/iwlwifi/iwl-fw.h | 4 +-
> drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 21 ++-
> drivers/net/wireless/iwlwifi/mvm/mac80211.c | 254 +++++++++++++++++++++-----
> drivers/net/wireless/iwlwifi/mvm/mvm.h | 36 +++-
> drivers/net/wireless/iwlwifi/mvm/scan.c | 53 +++++-
> drivers/net/wireless/iwlwifi/mvm/sta.c | 15 ++
> drivers/net/wireless/iwlwifi/mvm/sta.h | 1 +
> drivers/net/wireless/iwlwifi/mvm/time-event.c | 39 +++-
> 8 files changed, 356 insertions(+), 67 deletions(-)
>
> --
> 2.1.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-11-10 09:23:54

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 15/16] iwlwifi: mvm: clear TE data if CSA time event fails to start

From: Luciano Coelho <[email protected]>

If setting the CSA time event fails, we must clear the TE data,
otherwise we'll try to remove it when, for instance, a disconnection
occurs, causing a SYSASSERT.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/time-event.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index 2feff15..6120ff2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -198,6 +198,7 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
{
if (!le32_to_cpu(notif->status)) {
IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
+ iwl_mvm_te_clear_data(mvm, te_data);
return;
}

--
2.1.1


2014-11-10 09:10:41

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 09/16] iwlwifi: mvm: add CSA absent time event for clients

From: Luciano Coelho <[email protected]>

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 <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 19 +++++++++-----
drivers/net/wireless/iwlwifi/mvm/mvm.h | 27 ++++++++++++++++---
drivers/net/wireless/iwlwifi/mvm/sta.c | 15 +++++++++++
drivers/net/wireless/iwlwifi/mvm/sta.h | 1 +
drivers/net/wireless/iwlwifi/mvm/time-event.c | 37 +++++++++++++++++++++------
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 ddbdb78..0ca6185 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2764,7 +2764,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);

@@ -2798,12 +2797,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:
@@ -3040,6 +3033,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);
@@ -3067,6 +3061,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;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index d7b956a..6c4c255 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -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
@@ -758,6 +759,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)
{
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 1731c20..96dc3ea 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -1580,3 +1580,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();
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index d9c0d7b..3aa228c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -418,5 +418,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__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index b7f9e61..aa05a71 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,
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");
--
2.1.1


2014-11-10 09:24:18

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 14/16] iwlwifi: mvm: Insert DS Parameter Set placeholder in probes

From: Andrei Otcheretianski <[email protected]>

Since it's too complicated for the fw to insert a DS Parameter Set
element in the middle of the frame, it was decided that it should be
done by the host. The fw will only parse the frame and update the
current channel field.

Signed-off-by: Andrei Otcheretianski <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-fw.h | 4 +--
drivers/net/wireless/iwlwifi/mvm/scan.c | 53 +++++++++++++++++++++++++++++----
2 files changed, 50 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 4f6e668..e6e69ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -148,8 +148,8 @@ enum iwl_ucode_tlv_api {
* @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
* tx power value into TPC Report action frame and Link Measurement Report
* action frame
- * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports adding DS params
- * element in probe requests.
+ * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
+ * channel in DS parameter set element in probe requests.
* @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
* probe requests.
* @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index cb85e63..094d3b0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -365,6 +365,10 @@ static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm,
!is_sched_scan)
max_probe_len -= 32;

+ /* DS parameter set element is added on 2.4GHZ band if required */
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ max_probe_len -= 3;
+
return max_probe_len;
}

@@ -1133,6 +1137,42 @@ iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
}
}

+static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
+ size_t len, u8 *const pos)
+{
+ static const u8 before_ds_params[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_REQUEST,
+ WLAN_EID_EXT_SUPP_RATES,
+ };
+ size_t offs;
+ u8 *newpos = pos;
+
+ if (!iwl_mvm_rrm_scan_needed(mvm)) {
+ memcpy(newpos, ies, len);
+ return newpos + len;
+ }
+
+ offs = ieee80211_ie_split(ies, len,
+ before_ds_params,
+ ARRAY_SIZE(before_ds_params),
+ 0);
+
+ memcpy(newpos, ies, offs);
+ newpos += offs;
+
+ /* Add a placeholder for DS Parameter Set element */
+ *newpos++ = WLAN_EID_DS_PARAMS;
+ *newpos++ = 1;
+ *newpos++ = 0;
+
+ memcpy(newpos, ies + offs, len - offs);
+ newpos += len - offs;
+
+ return newpos;
+}
+
static void
iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_scan_ies *ies,
@@ -1142,7 +1182,7 @@ iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
sizeof(struct iwl_scan_channel_cfg_lmac) *
mvm->fw->ucode_capa.n_scan_channels);
struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
- u8 *pos;
+ u8 *pos, *newpos;

frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
eth_broadcast_addr(frame->da);
@@ -1157,11 +1197,14 @@ iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
preq->mac_header.offset = 0;
preq->mac_header.len = cpu_to_le16(24 + 2);

- memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
- ies->len[IEEE80211_BAND_2GHZ]);
+ /* Insert ds parameter set element on 2.4 GHz band */
+ newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
+ ies->ies[IEEE80211_BAND_2GHZ],
+ ies->len[IEEE80211_BAND_2GHZ],
+ pos);
preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
- preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
- pos += ies->len[IEEE80211_BAND_2GHZ];
+ preq->band_data[0].len = cpu_to_le16(newpos - pos);
+ pos = newpos;

memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
ies->len[IEEE80211_BAND_5GHZ]);
--
2.1.1


2014-11-10 09:10:38

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 05/16] iwlwifi: mvm: return the actual error code when switch_vif_chanctx fails

From: Luciano Coelho <[email protected]>

We have code to recover and go back to the original channel context if
something fails in the middle of switch_vif_chanctx, but we return the
error code of the recover calls instead of the original code, so if
the recovery succeeds, we will return 0 (success). Fix this by not
assigning the return value of the recovery calls to ret.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index b42124d..10347d1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2861,15 +2861,13 @@ out_remove:
__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);

out_reassign:
- ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
- if (ret) {
+ if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
goto out_restart;
}

- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true);
- if (ret) {
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
goto out_restart;
}
--
2.1.1


2014-11-10 09:23:48

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 12/16] iwlwifi: mvm: add a channel_switch op to bypass mac80211 timer

From: Luciano Coelho <[email protected]>

We need to call ieee80211_chswitch_done() ourselves just when the
absence TE started, so we perform the actual context switch early
enough. To do so, add a dummy channel_switch op, which will cause
mac80211 to skip the countdown timer and allow us to call
ieee80211_chswitch_done() to complete the operation.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 15 +++++++++++++++
drivers/net/wireless/iwlwifi/mvm/time-event.c | 1 +
2 files changed, 16 insertions(+)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index de6a64d..55ab81e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3016,6 +3016,20 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
}
#endif

+static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ /* By implementing this operation, we prevent mac80211 from
+ * starting its own channel switch timer, so that we can call
+ * ieee80211_chswitch_done() ourselves at the right time
+ * (which is when the absence time event starts).
+ */
+
+ IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
+ "dummy channel switch op\n");
+}
+
static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
@@ -3190,6 +3204,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {

.set_tim = iwl_mvm_set_tim,

+ .channel_switch = iwl_mvm_channel_switch,
.pre_channel_switch = iwl_mvm_pre_channel_switch,
.post_channel_switch = iwl_mvm_post_channel_switch,

diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index aa05a71..2feff15 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -207,6 +207,7 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
break;
case NL80211_IFTYPE_STATION:
iwl_mvm_csa_client_absent(mvm, te_data->vif);
+ ieee80211_chswitch_done(te_data->vif, true);
break;
default:
/* should never happen */
--
2.1.1


2014-11-10 09:23:53

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 16/16] iwlwifi: mvm: protect session during CSA

From: Luciano Coelho <[email protected]>

When another vif is also running during a channel switch, we need to
use a session protection when we move to the new channel, so that we
don't miss the beacons. Without this, sometimes the other vif
repeatedly gets time exactly when we should be hearing the beacons,
preventing channel switch from completing. Adding a session
protection that lasts from the moment the channel changes until 2
TBTTs later, ensures that we will hear the beacons on the destination
channel.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 21 +++++++++++++++++++++
drivers/net/wireless/iwlwifi/mvm/mvm.h | 1 +
2 files changed, 22 insertions(+)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 7c031ee..bdfb4b4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2720,6 +2720,25 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
}

if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+ u32 duration = 2 * vif->bss_conf.beacon_int;
+
+ /* iwl_mvm_protect_session() reads directly from the
+ * device (the system time), so make sure it is
+ * available.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+ if (ret)
+ goto out_remove_binding;
+
+ /* Protect the session to make sure we hear the first
+ * beacon on the new channel.
+ */
+ iwl_mvm_protect_session(mvm, vif, duration, duration,
+ vif->bss_conf.beacon_int / 2,
+ true);
+
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
iwl_mvm_update_quotas(mvm, NULL);
}

@@ -3129,6 +3148,8 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
if (ret)
goto out_unlock;
+
+ iwl_mvm_stop_session_protection(mvm, vif);
}

mvmvif->ps_disabled = false;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 3280698..5efa8c3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -275,6 +275,7 @@ enum iwl_mvm_ref_type {
IWL_MVM_REF_NMI,
IWL_MVM_REF_TM_CMD,
IWL_MVM_REF_EXIT_WORK,
+ IWL_MVM_REF_PROTECT_CSA,

/* update debugfs.c when changing this */

--
2.1.1


2014-11-10 09:23:50

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 10/16] iwlwifi: mvm: schedule CSA time event a bit before beacon 1

From: Luciano Coelho <[email protected]>

Instead of using a hardcoded number of TUs before beacon 0 as the time
to start the absence and actual channel switch, calculate it in
relation to the beacon interval. We use 10 TUs + beacon interval
before beacon 0 to target a bit before beacon 1. This gives us enough
time to switch to the new channel before the AP/GO switches.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 12 ++++++++----
drivers/net/wireless/iwlwifi/mvm/mvm.h | 13 +++++++++----
2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 0ca6185..3fb2b49 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3062,14 +3062,18 @@ 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);
+ /* Schedule the time event to a bit before beacon 1,
+ * to make sure we're in the new channel when the
+ * GO/AP arrives.
+ */
+ apply_time = chsw->device_timestamp +
+ ((vif->bss_conf.beacon_int * (chsw->count - 1) -
+ IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 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,
+ iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
apply_time);
break;
default:
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 6c4c255..3280698 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -87,12 +87,17 @@
/* A TimeUnit is 1024 microsecond */
#define MSEC_TO_TU(_msec) (_msec*1000/1024)

-/* 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.
+/* For GO, 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.
*/
#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
-#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 110
+
+/* For client, this value represents the number of TUs before CSA
+ * "beacon 1" TBTT, instead. This is because we don't know when the
+ * GO/AP will be in the new channel, so we switch early enough.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10

/*
* This value (in TUs) is used to fine tune the CSA NoA end time which should
--
2.1.1


2014-11-10 09:10:39

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 06/16] iwlwifi: mvm: Handle failed beacon transmissions during CSA

From: Andrei Otcheretianski <[email protected]>

The spec requires to decrement the CSA counters based on TBTT,
regardless if the beacon was actually transmitted. Previously, the fw
would send beacon notifications only for successfully transmitted
beacons. This behavior resulted in inaccurate CSA countdown. In order
to address this issue, the fw was changed to send beacon
notifications also for not transmitted beacons. Such notifications
have TX_STATUS_INTERNAL_ABORT (0x92).
Don't start the CSA countdown before first successfully transmitted
beacon, in order to guarantee that the CSA is announced for a
required period.

Signed-off-by: Andrei Otcheretianski <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 21 +++++++++++++++------
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 11 +++++++++++
drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 +++
3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index 0c5c0b0..b259beb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -1219,17 +1219,25 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
}

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

+ /* Don't start to countdown from a failed beacon */
+ if (!tx_success && !mvmvif->csa_countdown)
+ return;
+
+ mvmvif->csa_countdown = true;
+
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) {
+ !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
+ tx_success) {
u32 rel_time = (c + 1) *
csa_vif->bss_conf.beacon_int -
IWL_MVM_CHANNEL_SWITCH_TIME_GO;
@@ -1256,6 +1264,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
struct ieee80211_vif *csa_vif;
struct ieee80211_vif *tx_blocked_vif;
u64 tsf;
+ u16 status;

lockdep_assert_held(&mvm->mutex);

@@ -1272,18 +1281,18 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
tsf = le64_to_cpu(beacon->tsf);
}

+ status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
IWL_DEBUG_RX(mvm,
"beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
- le16_to_cpu(beacon_notify_hdr->status.status) &
- TX_STATUS_MSK,
- beacon_notify_hdr->failure_frame, tsf,
+ status, beacon_notify_hdr->failure_frame, tsf,
mvm->ap_last_beacon_gp2,
le32_to_cpu(beacon_notify_hdr->initial_rate));

csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(csa_vif && csa_vif->csa_active))
- iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+ iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
+ (status == TX_STATUS_SUCCESS));

tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
lockdep_is_held(&mvm->mutex));
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 10347d1..9cf90af 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2782,6 +2782,8 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
if (!vif->csa_active || !mvmvif->ap_ibss_active)
goto out;

+ mvmvif->csa_countdown = false;
+
/* Set CS bit on all the stations */
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);

@@ -3037,6 +3039,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;
int ret;

mutex_lock(&mvm->mutex);
@@ -3056,6 +3059,14 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
}

rcu_assign_pointer(mvm->csa_vif, vif);
+
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ if (WARN_ONCE(mvmvif->csa_countdown,
+ "Previous CSA countdown didn't complete")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
break;
default:
break;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index b153ced..d7b956a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -399,6 +399,9 @@ struct iwl_mvm_vif {

/* FW identified misbehaving AP */
u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+ /* Indicates that CSA countdown may be started */
+ bool csa_countdown;
};

static inline struct iwl_mvm_vif *
--
2.1.1


2014-11-10 09:10:35

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 01/16] iwlwifi: mvm: use new pre_channel_switch op instead of channel_switch_beacon

From: Luciano Coelho <[email protected]>

A new callback has been added to prepare the device for a channel
switch. Use the new callback instead of the old channel_switch_beacon
operation.

This makes it possible to remove the channel_switch_beacon operation
from mac80211.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index e16c29d..e45b9b9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2973,27 +2973,34 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
}
#endif

-static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_chan_def *chandef)
+static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
+ int ret;

mutex_lock(&mvm->mutex);

csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (WARN(csa_vif && csa_vif->csa_active,
- "Another CSA is already in progress"))
+ "Another CSA is already in progress")) {
+ ret = -EBUSY;
goto out_unlock;
+ }

IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
- chandef->center_freq1);
+ chsw->chandef.center_freq1);
rcu_assign_pointer(mvm->csa_vif, vif);

+ ret = 0;
+
out_unlock:
mutex_unlock(&mvm->mutex);
+
+ return ret;
}

static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
@@ -3069,7 +3076,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {

.set_tim = iwl_mvm_set_tim,

- .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+ .pre_channel_switch = iwl_mvm_pre_channel_switch,

CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)

--
2.1.1


2014-11-10 09:10:39

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 07/16] iwlwifi: mvm: disable PS during channel switch

From: Luciano Coelho <[email protected]>

Disable PS when pre_channel_switch is called and add the
post_channel_switch operation to re-enable PS when the channel switch
is completed.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 28 +++++++++++++++++++++++++---
1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 9cf90af..221f823 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3039,7 +3039,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;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;

mutex_lock(&mvm->mutex);
@@ -3060,7 +3060,6 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,

rcu_assign_pointer(mvm->csa_vif, vif);

- mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (WARN_ONCE(mvmvif->csa_countdown,
"Previous CSA countdown didn't complete")) {
ret = -EBUSY;
@@ -3072,7 +3071,11 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
break;
}

- ret = 0;
+ mvmvif->ps_disabled = true;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+ if (ret)
+ goto out_unlock;

out_unlock:
mutex_unlock(&mvm->mutex);
@@ -3080,6 +3083,24 @@ out_unlock:
return ret;
}

+static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ mvmvif->ps_disabled = false;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u32 queues, bool drop)
{
@@ -3154,6 +3175,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.set_tim = iwl_mvm_set_tim,

.pre_channel_switch = iwl_mvm_pre_channel_switch,
+ .post_channel_switch = iwl_mvm_post_channel_switch,

CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)

--
2.1.1


2014-11-10 09:10:37

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 04/16] iwlwifi: mvm: add support for CHANCTX_SWMODE_REASSIGN_VIF

From: Luciano Coelho <[email protected]>

Add support to reassign vif in switch_vif_chanctx. This is similar to
the existing CHANCTX_SWMODE_SWAP_CONTEXTS mode, but doesn't delete the
old context nor creates a new one, doing to switch between two
existing contexts.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 40 ++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 98c9e1b..b42124d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2886,6 +2886,44 @@ out:
return ret;
}

+static int
+iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ goto out;
+
+out_reassign:
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
+ IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+ goto out_restart;
+ }
+
+ goto out;
+
+out_restart:
+ /* things keep failing, better restart the hw */
+ iwl_mvm_nic_restart(mvm, false);
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs,
@@ -2903,7 +2941,7 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
break;
case CHANCTX_SWMODE_REASSIGN_VIF:
- ret = -EOPNOTSUPP;
+ ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
break;
default:
ret = -EOPNOTSUPP;
--
2.1.1


2014-11-10 09:10:40

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 08/16] iwlwifi: mvm: use switching_chanctx argument instead of csa_active

From: Luciano Coelho <[email protected]>

Now that all CSA flows are using the switch_vif_chanctx op, we can
rely on the switching_chanctx boolean that is passed to the
__iwl_mvm_assign_vif_chanctx() and __iwl_mvm_unassign_vif_chanctx()
functions to decide whether the context switch flows need to be
executed. In this way we make the chanctx switch flow more generic,
without having to rely on the csa_active flag being set.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 221f823..ddbdb78 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2669,8 +2669,8 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,

switch (vif->type) {
case NL80211_IFTYPE_AP:
- /* Unless it's a CSA flow we have nothing to do here */
- if (vif->csa_active) {
+ /* only needed if we're switching chanctx (i.e. during CSA) */
+ if (switching_chanctx) {
mvmvif->ap_ibss_active = true;
break;
}
@@ -2720,7 +2720,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}

- if (vif->csa_active && vif->type == NL80211_IFTYPE_STATION) {
+ if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;

mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
@@ -2779,7 +2779,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
- if (!vif->csa_active || !mvmvif->ap_ibss_active)
+ if (!switching_chanctx || !mvmvif->ap_ibss_active)
goto out;

mvmvif->csa_countdown = false;
--
2.1.1


2014-11-10 09:10:36

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 03/16] iwlwifi: mvm: refactor iwl_mvm_switch_vif_chanctx to support other modes

From: Luciano Coelho <[email protected]>

Currently we only support the CHANCTX_SWMODE_SWAP_CONTEXTS mode, but
we need to support other modes as well. Spin a new function off in
order to make it easier to support other modes.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 40 ++++++++++++++++++++++-------
1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 8bb6f18..98c9e1b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2827,18 +2827,12 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}

-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif_chanctx_switch *vifs,
- int n_vifs,
- enum ieee80211_chanctx_switch_mode mode)
+static int
+iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;

- /* we only support SWAP_CONTEXTS and with a single-vif right now */
- if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
- return -EOPNOTSUPP;
-
mutex_lock(&mvm->mutex);
__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
@@ -2888,6 +2882,34 @@ out_restart:

out:
mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ /* we only support a single-vif right now */
+ if (n_vifs > 1)
+ return -EOPNOTSUPP;
+
+ switch (mode) {
+ case CHANCTX_SWMODE_SWAP_CONTEXTS:
+ ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+ break;
+ case CHANCTX_SWMODE_REASSIGN_VIF:
+ ret = -EOPNOTSUPP;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
return ret;
}

--
2.1.1


2014-11-10 09:10:36

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 02/16] iwlwifi: mvm: only save csa_vif in AP/GO mode

From: Luciano Coelho <[email protected]>

We only need the csa_vif in AP/GO modes, and assigning for other
interfaces may cause problems, because csa_vif is never cleared. To
prevent this, only assign the value if the iftype is
NL80211_IFTYPE_AP. Use a switch to do this, even though, for now,
only the AP interface type is handled, because soon other interface
types will be added as well.

Additionally, convert the WARN() in the error case when a
channel-switch is already running to WARN_ONCE().

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index e45b9b9..8bb6f18 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -2983,17 +2983,25 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,

mutex_lock(&mvm->mutex);

- csa_vif = rcu_dereference_protected(mvm->csa_vif,
- lockdep_is_held(&mvm->mutex));
- if (WARN(csa_vif && csa_vif->csa_active,
- "Another CSA is already in progress")) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
+ IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
chsw->chandef.center_freq1);
- rcu_assign_pointer(mvm->csa_vif, vif);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ csa_vif =
+ rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ONCE(csa_vif && csa_vif->csa_active,
+ "Another CSA is already in progress")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ rcu_assign_pointer(mvm->csa_vif, vif);
+ break;
+ default:
+ break;
+ }

ret = 0;

--
2.1.1


2014-11-10 09:24:16

by Luca Coelho

[permalink] [raw]
Subject: [PATCH 13/16] iwlwifi: mvm: disable beacon filtering during CSA

From: Luciano Coelho <[email protected]>

After a channel switch, transmission on the new channel is only
started once we see a beacon on it. Thus, beacon filtering needs to
be disabled during channel switch so that mac80211 receives this
beacon and finishes the process.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/iwlwifi/mvm/mac80211.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 55ab81e..7c031ee 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3079,6 +3079,12 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,

iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
apply_time);
+ if (mvmvif->bf_data.bf_enabled) {
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
+ }
+
break;
default:
break;
@@ -3119,6 +3125,10 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);

iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
}

mvmvif->ps_disabled = false;
--
2.1.1