]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/iwlwifi/mvm/mac80211.c
iwlwifi: mvm: declare TDLS support
[karo-tx-linux.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index e16c29dc9d495d403e54c88c6d9f7176bda1e141..801abcfbaa7fd2072ef8ff04e7b78ebde387b873 100644 (file)
@@ -69,6 +69,7 @@
 #include <linux/etherdevice.h>
 #include <linux/ip.h>
 #include <linux/if_arp.h>
+#include <linux/devcoredump.h>
 #include <net/mac80211.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/tcp.h>
@@ -253,6 +254,26 @@ static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
        spin_unlock_bh(&mvm->refs_lock);
 }
 
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+       int i;
+       bool taken = false;
+
+       if (!iwl_mvm_is_d0i3_supported(mvm))
+               return true;
+
+       spin_lock_bh(&mvm->refs_lock);
+       for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+               if (mvm->refs[i]) {
+                       taken = true;
+                       break;
+               }
+       }
+       spin_unlock_bh(&mvm->refs_lock);
+
+       return taken;
+}
+
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 {
        iwl_mvm_ref(mvm, ref_type);
@@ -322,8 +343,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
        }
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+           mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+               hw->wiphy->features |=
+                       NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+                       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+       }
 
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -402,7 +428,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                               NL80211_FEATURE_LOW_PRIORITY_SCAN |
                               NL80211_FEATURE_P2P_GO_OPPPS |
                               NL80211_FEATURE_DYNAMIC_SMPS |
-                              NL80211_FEATURE_STATIC_SMPS;
+                              NL80211_FEATURE_STATIC_SMPS |
+                              NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
 
        if (mvm->fw->ucode_capa.capa[0] &
            IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)
@@ -463,6 +490,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (ret)
                return ret;
 
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_TDLS_SUPPORT) {
+               IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
+               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+       }
+
        ret = ieee80211_register_hw(mvm->hw);
        if (ret)
                iwl_mvm_leds_exit(mvm);
@@ -526,7 +558,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
        }
 
        if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
-           !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
+           !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
+           !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
                goto drop;
 
        /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
@@ -678,10 +711,51 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
+static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
+                                    const void *data, size_t datalen)
+{
+       const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
+       ssize_t bytes_read;
+       ssize_t bytes_read_trans;
+
+       if (offset < dump_ptrs->op_mode_len) {
+               bytes_read = min_t(ssize_t, count,
+                                  dump_ptrs->op_mode_len - offset);
+               memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
+                      bytes_read);
+               offset += bytes_read;
+               count -= bytes_read;
+
+               if (count == 0)
+                       return bytes_read;
+       } else {
+               bytes_read = 0;
+       }
+
+       if (!dump_ptrs->trans_ptr)
+               return bytes_read;
+
+       offset -= dump_ptrs->op_mode_len;
+       bytes_read_trans = min_t(ssize_t, count,
+                                dump_ptrs->trans_ptr->len - offset);
+       memcpy(buffer + bytes_read,
+              (u8 *)dump_ptrs->trans_ptr->data + offset,
+              bytes_read_trans);
+
+       return bytes_read + bytes_read_trans;
+}
+
+static void iwl_mvm_free_coredump(const void *data)
+{
+       const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
+
+       vfree(fw_error_dump->op_mode_ptr);
+       vfree(fw_error_dump->trans_ptr);
+       kfree(fw_error_dump);
+}
+
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 {
-       static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL };
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
@@ -694,10 +768,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->fw_error_dump)
-               return;
-
-       fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL);
+       fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
 
@@ -772,21 +843,25 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        if (fw_error_dump->trans_ptr)
                file_len += fw_error_dump->trans_ptr->len;
        dump_file->file_len = cpu_to_le32(file_len);
-       mvm->fw_error_dump = fw_error_dump;
 
-       /* notify the userspace about the error we had */
-       kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env);
+       dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
+                     GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
 }
-#endif
 
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
-       iwl_mvm_fw_error_dump(mvm);
+       /* clear the D3 reconfig, we only need it to avoid dumping a
+        * firmware coredump on reconfiguration, we shouldn't do that
+        * on D3->D0 transition
+        */
+       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+               iwl_mvm_fw_error_dump(mvm);
 
        iwl_trans_stop_device(mvm->trans);
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        mvm->ps_disabled = false;
+       mvm->calibrating = false;
 
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
@@ -876,6 +951,25 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
        mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
+{
+       bool exit_now;
+
+       if (!iwl_mvm_is_d0i3_supported(mvm))
+               return;
+
+       mutex_lock(&mvm->d0i3_suspend_mutex);
+       __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+       exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+                                       &mvm->d0i3_suspend_flags);
+       mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+       if (exit_now) {
+               IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
+               _iwl_mvm_exit_d0i3(mvm);
+       }
+}
+
 static void
 iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
                              enum ieee80211_reconfig_type reconfig_type)
@@ -887,6 +981,7 @@ iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
                iwl_mvm_restart_complete(mvm);
                break;
        case IEEE80211_RECONFIG_TYPE_SUSPEND:
+               iwl_mvm_resume_complete(mvm);
                break;
        }
 }
@@ -1099,7 +1194,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 = iwl_mvm_mac_get_queues_mask(mvm, vif);
+       u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
 
        if (tfd_msk) {
                mutex_lock(&mvm->mutex);
@@ -1395,6 +1490,9 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
                .cmd = cmd,
        };
 
+       if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
+               return false;
+
        memset(cmd, 0, sizeof(*cmd));
        cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
        cmd->max_macs = ARRAY_SIZE(cmd->macs);
@@ -1748,6 +1846,13 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_BEACON &&
            iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
                IWL_WARN(mvm, "Failed updating beacon data\n");
+
+       if (changes & BSS_CHANGED_TXPOWER) {
+               IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+                               bss_conf->txpower);
+               iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+       }
+
 }
 
 static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -1840,9 +1945,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
            req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
                return -EINVAL;
 
-       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
+               if (ret)
+                       return ret;
+       }
 
        mutex_lock(&mvm->mutex);
 
@@ -1853,7 +1960,9 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
+       else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
                ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
        else
                ret = iwl_mvm_scan_request(mvm, vif, req);
@@ -2152,9 +2261,11 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
-       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
+               if (ret)
+                       return ret;
+       }
 
        mutex_lock(&mvm->mutex);
 
@@ -2174,27 +2285,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                goto out;
        }
 
-       mvm->scan_status = IWL_MVM_SCAN_SCHED;
-
-       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
-               ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
-               if (ret)
-                       goto err;
-       }
-
-       ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+       ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
        if (ret)
-               goto err;
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
 
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-               ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
-       else
-               ret = iwl_mvm_sched_scan_start(mvm, req);
-
-       if (!ret)
-               goto out;
-err:
-       mvm->scan_status = IWL_MVM_SCAN_NONE;
 out:
        mutex_unlock(&mvm->mutex);
        return ret;
@@ -2212,6 +2306,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        iwl_mvm_wait_for_async_handlers(mvm);
 
        return ret;
+
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2240,12 +2335,16 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                break;
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
-               /*
-                * Support for TX only, at least for now, so accept
-                * the key and do nothing else. Then mac80211 will
-                * pass it for TX but we don't have to use it for RX.
+               /* For non-client mode, only use WEP keys for TX as we probably
+                * don't have a station yet anyway and would then have to keep
+                * track of the keys, linking them to each of the clients/peers
+                * as they appear. For now, don't do that, for performance WEP
+                * offload doesn't really matter much, but we need it for some
+                * other offload features in client mode.
                 */
-               return 0;
+               if (vif->type != NL80211_IFTYPE_STATION)
+                       return 0;
+               break;
        default:
                /* currently FW supports only one optional cipher scheme */
                if (hw->n_cipher_schemes &&
@@ -2381,14 +2480,19 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
        /* Set the node address */
        memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
 
+       lockdep_assert_held(&mvm->mutex);
+
+       spin_lock_bh(&mvm->time_event_lock);
+
+       if (WARN_ON(te_data->id == HOT_SPOT_CMD)) {
+               spin_unlock_bh(&mvm->time_event_lock);
+               return -EIO;
+       }
+
        te_data->vif = vif;
        te_data->duration = duration;
        te_data->id = HOT_SPOT_CMD;
 
-       lockdep_assert_held(&mvm->mutex);
-
-       spin_lock_bh(&mvm->time_event_lock);
-       list_add_tail(&te_data->list, &mvm->time_event_list);
        spin_unlock_bh(&mvm->time_event_lock);
 
        /*
@@ -2444,22 +2548,23 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
                           duration, type);
 
+       mutex_lock(&mvm->mutex);
+
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                /* Use aux roc framework (HS20) */
                ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
                                               vif, duration);
-               return ret;
+               goto out_unlock;
        case NL80211_IFTYPE_P2P_DEVICE:
                /* handle below */
                break;
        default:
                IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out_unlock;
        }
 
-       mutex_lock(&mvm->mutex);
-
        for (i = 0; i < NUM_PHY_CTX; i++) {
                phy_ctxt = &mvm->phy_ctxts[i];
                if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
@@ -2556,7 +2661,7 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
        IWL_DEBUG_MAC80211(mvm, "enter\n");
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_stop_p2p_roc(mvm);
+       iwl_mvm_stop_roc(mvm);
        mutex_unlock(&mvm->mutex);
 
        IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -3010,18 +3115,24 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
 
-       if (WARN_ON_ONCE(!mvmsta))
-               goto done;
+       if (WARN_ON_ONCE(!mvmsta)) {
+               mutex_unlock(&mvm->mutex);
+               return;
+       }
 
        if (drop) {
                if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
                        IWL_ERR(mvm, "flush request fail\n");
+               mutex_unlock(&mvm->mutex);
        } else {
-               iwl_trans_wait_tx_queue_empty(mvm->trans,
-                                             mvmsta->tfd_queue_msk);
+               u32 tfd_queue_msk = mvmsta->tfd_queue_msk;
+               mutex_unlock(&mvm->mutex);
+
+               /* this can take a while, and we may need/want other operations
+                * to succeed while doing this, so do it without the mutex held
+                */
+               iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_queue_msk);
        }
-done:
-       mutex_unlock(&mvm->mutex);
 }
 
 const struct ieee80211_ops iwl_mvm_hw_ops = {