Merge remote-tracking branch 'iwlwifi-fixes/master' into iwlwifi-next
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index ac1cef6a6b038827b8bc638fba261bf10f35751e..f876d866b8abbef411232533ea6ec75abdd81727 100644 (file)
@@ -323,7 +323,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
                                    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
-       hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
+       hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+               IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
        /*
@@ -467,7 +468,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
                                    WIPHY_WOWLAN_DISCONNECT |
                                    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                   WIPHY_WOWLAN_RFKILL_RELEASE;
+                                   WIPHY_WOWLAN_RFKILL_RELEASE |
+                                   WIPHY_WOWLAN_NET_DETECT;
                if (!iwlwifi_mod_params.sw_crypto)
                        mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                             WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -476,6 +478,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
                mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
                mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
                mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
                hw->wiphy->wowlan = &mvm->wowlan;
        }
@@ -2574,9 +2577,15 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               /* Use aux roc framework (HS20) */
-               ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
-                                              vif, duration);
+               if (mvm->fw->ucode_capa.capa[0] &
+                   IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+                       /* Use aux roc framework (HS20) */
+                       ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+                                                      vif, duration);
+                       goto out_unlock;
+               }
+               IWL_ERR(mvm, "hotspot not supported\n");
+               ret = -EINVAL;
                goto out_unlock;
        case NL80211_IFTYPE_P2P_DEVICE:
                /* handle below */
@@ -2841,23 +2850,32 @@ 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;
+               u32 duration = 2 * vif->bss_conf.beacon_int;
 
-               mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
-                                                         mvmvif->ap_sta_id);
+               /* 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;
 
-               if (WARN_ON(!mvmsta))
-                       goto out;
+               /* 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);
 
-               /* TODO: only re-enable after the first beacon */
-               iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
+               iwl_mvm_update_quotas(mvm, NULL);
        }
 
        goto out;
@@ -3153,6 +3171,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)
@@ -3189,15 +3221,25 @@ 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);
+               if (mvmvif->bf_data.bf_enabled) {
+                       ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+                       if (ret)
+                               goto out_unlock;
+               }
+
                break;
        default:
                break;
@@ -3209,6 +3251,9 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
        if (ret)
                goto out_unlock;
 
+       /* we won't be on this channel any longer */
+       iwl_mvm_teardown_tdls_peers(mvm);
+
 out_unlock:
        mutex_unlock(&mvm->mutex);
 
@@ -3224,10 +3269,33 @@ 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);
+
+               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;
 
        ret = iwl_mvm_power_update_ps(mvm);
 
+out_unlock:
        mutex_unlock(&mvm->mutex);
 
        return ret;
@@ -3325,6 +3393,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,