diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/tdls.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/tdls.c | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c index c0e00bae5bd..a87b506c8c7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c @@ -64,6 +64,8 @@ #include <linux/etherdevice.h> #include "mvm.h" #include "time-event.h" +#include "iwl-io.h" +#include "iwl-prph.h" #define TU_TO_US(x) (x * 1024) #define TU_TO_MS(x) (TU_TO_US(x) / 1000) @@ -228,6 +230,8 @@ iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state) return "IDLE"; case IWL_MVM_TDLS_SW_REQ_SENT: return "REQ SENT"; + case IWL_MVM_TDLS_SW_RESP_RCVD: + return "RESP RECEIVED"; case IWL_MVM_TDLS_SW_REQ_RCVD: return "REQ RECEIVED"; case IWL_MVM_TDLS_SW_ACTIVE: @@ -248,6 +252,11 @@ static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, iwl_mvm_tdls_cs_state_str(state)); mvm->tdls_cs.state = state; + /* we only send requests to our switching peer - update sent time */ + if (state == IWL_MVM_TDLS_SW_REQ_SENT) + mvm->tdls_cs.peer.sent_timestamp = + iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); + if (state == IWL_MVM_TDLS_SW_IDLE) mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; } @@ -300,7 +309,7 @@ out: static int iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator) + const u8 *peer, bool peer_initiator, u32 timestamp) { bool same_peer = false; int ret = 0; @@ -325,17 +334,30 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, ret = -EINVAL; break; case IWL_MVM_TDLS_SW_REQ_SENT: + /* only allow requests from the same peer */ + if (!same_peer) + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH && + !peer_initiator) + /* + * We received a ch-switch request while an outgoing + * one is pending. Allow it if the peer is the link + * initiator. + */ + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_REQ) + /* wait for idle before sending another request */ + ret = -EBUSY; + else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp) + /* we got a stale response - ignore it */ + ret = -EINVAL; + break; + case IWL_MVM_TDLS_SW_RESP_RCVD: /* - * We received a ch-switch request while an outgoing one is - * pending. Allow it to proceed if the other peer is the same - * one we sent to, and we are not the link initiator. + * we are waiting for the FW to give an "active" notification, + * so ignore requests in the meantime */ - if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) { - if (!same_peer) - ret = -EBUSY; - else if (!peer_initiator) /* we are the initiator */ - ret = -EBUSY; - } + ret = -EBUSY; break; case IWL_MVM_TDLS_SW_REQ_RCVD: /* as above, allow the link initiator to proceed */ @@ -349,9 +371,12 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, } break; case IWL_MVM_TDLS_SW_ACTIVE: - /* we don't allow initiations during active channel switch */ - if (type == TDLS_SEND_CHAN_SW_REQ) - ret = -EINVAL; + /* + * the only valid request when active is a request to return + * to the base channel by the current off-channel peer + */ + if (type != TDLS_MOVE_CH || !same_peer) + ret = -EBUSY; break; } @@ -384,7 +409,8 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator); + ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator, + timestamp); if (ret) return ret; @@ -473,6 +499,8 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, type == TDLS_SEND_CHAN_SW_REQ ? IWL_MVM_TDLS_SW_REQ_SENT : IWL_MVM_TDLS_SW_REQ_RCVD); + } else { + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD); } out: @@ -657,12 +685,15 @@ iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); enum iwl_tdls_channel_switch_type type; unsigned int delay; + const char *action_str = + params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ? + "REQ" : "RESP"; mutex_lock(&mvm->mutex); IWL_DEBUG_TDLS(mvm, - "Received TDLS ch switch action %d from %pM status %d\n", - params->action_code, params->sta->addr, params->status); + "Received TDLS ch switch action %s from %pM status %d\n", + action_str, params->sta->addr, params->status); /* * we got a non-zero status from a peer we were switching to - move to |