Merge remote-tracking branch 'iwlwifi-fixes/master' into NEXT
[cascardo/linux.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index cdc272d..069bb8e 100644 (file)
@@ -6,6 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -31,6 +32,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -778,6 +780,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
        iwl_trans_stop_device(mvm->trans);
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
+       mvm->ps_disabled = false;
 
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
@@ -805,6 +808,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
         * ucode_down ref until reconfig is complete */
        iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 
+       /* clear any stale d0i3 state */
+       clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+
        mvm->vif_count = 0;
        mvm->rx_ba_sessions = 0;
 }
@@ -882,7 +888,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        /* async_handlers_list is empty and will stay empty: HW is stopped */
 
        /* the fw is stopped, the aux sta is dead: clean up driver state */
-       iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+       iwl_mvm_del_aux_sta(mvm);
 
        mutex_unlock(&mvm->mutex);
 
@@ -967,10 +973,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
         */
        if (vif->type == NL80211_IFTYPE_AP ||
            vif->type == NL80211_IFTYPE_ADHOC) {
-               u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
-               ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
-                                              qmask,
-                                              ieee80211_vif_type_p2p(vif));
+               ret = iwl_mvm_alloc_bcast_sta(mvm, vif);
                if (ret) {
                        IWL_ERR(mvm, "Failed to allocate bcast sta\n");
                        goto out_release;
@@ -1018,7 +1021,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                if (ret)
                        goto out_unref_phy;
 
-               ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+               ret = iwl_mvm_add_bcast_sta(mvm, vif);
                if (ret)
                        goto out_unbind;
 
@@ -1059,14 +1062,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif)
 {
-       u32 tfd_msk = 0, ac;
-
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
-                       tfd_msk |= BIT(vif->hw_queue[ac]);
-
-       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
-               tfd_msk |= BIT(vif->cab_queue);
+       u32 tfd_msk = iwl_mvm_mac_get_queues_mask(mvm, vif);
 
        if (tfd_msk) {
                mutex_lock(&mvm->mutex);
@@ -1122,13 +1118,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                        mvm->noa_duration = 0;
                }
 #endif
-               iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
+               iwl_mvm_dealloc_bcast_sta(mvm, vif);
                goto out_release;
        }
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
                mvm->p2p_device_vif = NULL;
-               iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+               iwl_mvm_rm_bcast_sta(mvm, vif);
                iwl_mvm_binding_remove_vif(mvm, vif);
                iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
                mvmvif->phy_ctxt = NULL;
@@ -1447,10 +1443,23 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
 
-       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
+       /*
+        * If we're not associated yet, take the (new) BSSID before associating
+        * so the firmware knows. If we're already associated, then use the old
+        * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC
+        * branch for disassociation below.
+        */
+       if (changes & BSS_CHANGED_BSSID && !mvmvif->associated)
+               memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid);
        if (ret)
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
+       /* after sending it once, adopt mac80211 data */
+       memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN);
+       mvmvif->associated = bss_conf->assoc;
+
        if (changes & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        /* add quota for this interface */
@@ -1478,13 +1487,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                 */
                                u32 dur = (11 * vif->bss_conf.beacon_int) / 10;
                                iwl_mvm_protect_session(mvm, vif, dur, dur,
-                                                       5 * dur);
+                                                       5 * dur, false);
                        }
 
                        iwl_mvm_sf_update(mvm, vif, false);
                        iwl_mvm_power_vif_assoc(mvm, vif);
-                       if (vif->p2p)
+                       if (vif->p2p) {
                                iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
+                               iwl_mvm_update_smps(mvm, vif,
+                                                   IWL_MVM_SMPS_REQ_PROT,
+                                                   IEEE80211_SMPS_DYNAMIC);
+                       }
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /*
                         * If update fails - SF might be running in associated
@@ -1508,6 +1521,13 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
 
                        if (vif->p2p)
                                iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
+
+                       /* this will take the cleared BSSID from bss_conf */
+                       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+                       if (ret)
+                               IWL_ERR(mvm,
+                                       "failed to update MAC %pM (clear after unassoc)\n",
+                                       vif->addr);
                }
 
                iwl_mvm_recalc_multicast(mvm);
@@ -1604,7 +1624,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
-       ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+       ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
        if (ret)
                goto out_unbind;
 
@@ -1620,7 +1640,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
-               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
 
@@ -1636,7 +1656,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 out_quota_failed:
        iwl_mvm_power_update_mac(mvm);
        mvmvif->ap_ibss_active = false;
-       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+       iwl_mvm_send_rm_bcast_sta(mvm, vif);
 out_unbind:
        iwl_mvm_binding_remove_vif(mvm, vif);
 out_remove:
@@ -1678,10 +1698,10 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
-               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
 
        iwl_mvm_update_quotas(mvm, NULL);
-       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+       iwl_mvm_send_rm_bcast_sta(mvm, vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
        iwl_mvm_power_update_mac(mvm);
@@ -1705,7 +1725,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
 
        if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
                       BSS_CHANGED_BANDWIDTH) &&
-           iwl_mvm_mac_ctxt_changed(mvm, vif, false))
+           iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL))
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
        /* Need to send a new beacon template to the FW */
@@ -2116,7 +2136,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
                int ret;
 
                mutex_lock(&mvm->mutex);
-               ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
+               ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
                mutex_unlock(&mvm->mutex);
                return ret;
        }
@@ -2144,7 +2164,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
        /* Try really hard to protect the session and hear a beacon */
-       iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500);
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false);
        mutex_unlock(&mvm->mutex);
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX);
@@ -2165,7 +2185,7 @@ static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
        /* Protect the session to hear the TDLS setup response on the channel */
-       iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
+       iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true);
        mutex_unlock(&mvm->mutex);
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
@@ -2703,7 +2723,10 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
                ret = 0;
                goto out;
        case NL80211_IFTYPE_STATION:
+               break;
        case NL80211_IFTYPE_MONITOR:
+               /* always disable PS when a monitor interface is active */
+               mvmvif->ps_disabled = true;
                break;
        default:
                ret = -EINVAL;
@@ -2735,7 +2758,20 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
        if ((vif->type == NL80211_IFTYPE_AP) ||
            (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
                iwl_mvm_update_quotas(mvm, NULL);
-               iwl_mvm_mac_ctxt_changed(mvm, vif, false);
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+       }
+
+       if (vif->csa_active && 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);
        }
 
        goto out;
@@ -2769,6 +2805,7 @@ 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);
 
@@ -2779,6 +2816,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
                goto out;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
+               mvmvif->ps_disabled = false;
                break;
        case NL80211_IFTYPE_AP:
                /* This part is triggered only during CSA */
@@ -2799,7 +2837,13 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
 
                disabled_vif = vif;
 
-               iwl_mvm_mac_ctxt_changed(mvm, vif, true);
+               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:
                break;