]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/net/wireless/iwlwifi/mvm/tdls.c
iwlwifi: deprecate -9.ucode for 3160 / 7260 / 7265
[mirror_ubuntu-bionic-kernel.git] / drivers / net / wireless / iwlwifi / mvm / tdls.c
index c0e00bae5bd0622090de58a17f2f9b4fcc35e22c..a87b506c8c7272393304824409ae8473d6022277 100644 (file)
@@ -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