]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/net/wireless/iwlwifi/mvm/mac80211.c
iwlwifi: mvm: clean up invalid station handling
[mirror_ubuntu-artful-kernel.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index e8264e11b12da0d2ab33108ff2cf8676ecd61825..fe031608fd912ce67201358a187031a09c8cb549 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -65,7 +65,9 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ip.h>
 #include <net/mac80211.h>
+#include <net/tcp.h>
 
 #include "iwl-op-mode.h"
 #include "iwl-io.h"
@@ -102,10 +104,33 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
        },
 };
 
+#ifdef CONFIG_PM_SLEEP
+static const struct nl80211_wowlan_tcp_data_token_feature
+iwl_mvm_wowlan_tcp_token_feature = {
+       .min_len = 0,
+       .max_len = 255,
+       .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
+};
+
+static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
+       .tok = &iwl_mvm_wowlan_tcp_token_feature,
+       .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
+                           sizeof(struct ethhdr) -
+                           sizeof(struct iphdr) -
+                           sizeof(struct tcphdr),
+       .data_interval_max = 65535, /* __le16 in API */
+       .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
+                           sizeof(struct ethhdr) -
+                           sizeof(struct iphdr) -
+                           sizeof(struct tcphdr),
+       .seq = true,
+};
+#endif
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
-       int num_mac, ret;
+       int num_mac, ret, i;
 
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
@@ -118,8 +143,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY;
 
-       hw->queues = IWL_FIRST_AMPDU_QUEUE;
-       hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE;
+       hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
+       hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 
        /*
@@ -149,18 +174,22 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
 
-       hw->wiphy->max_remain_on_channel_duration = 500;
+       hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
 
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
        hw->wiphy->addresses = mvm->addresses;
        hw->wiphy->n_addresses = 1;
-       num_mac = mvm->nvm_data->n_hw_addrs;
-       if (num_mac > 1) {
-               memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr,
+
+       /* Extract additional MAC addresses if available */
+       num_mac = (mvm->nvm_data->n_hw_addrs > 1) ?
+               min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1;
+
+       for (i = 1; i < num_mac; i++) {
+               memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr,
                       ETH_ALEN);
-               mvm->addresses[1].addr[5]++;
+               mvm->addresses[i].addr[5]++;
                hw->wiphy->n_addresses++;
        }
 
@@ -206,6 +235,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
                hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
                hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
        }
 #endif
 
@@ -227,7 +257,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
                goto drop;
        }
 
-       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_OFFCHANNEL_QUEUE &&
+       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
            !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
                goto drop;
 
@@ -273,12 +303,18 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
                break;
        case IEEE80211_AMPDU_TX_START:
+               if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) {
+                       ret = -EINVAL;
+                       break;
+               }
                ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
                break;
        case IEEE80211_AMPDU_TX_STOP_CONT:
+               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               break;
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
-               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
                ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
@@ -466,11 +502,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        /*
         * TODO: remove this temporary code.
         * Currently MVM FW supports power management only on single MAC.
-        * Iterate and disable PM on all active interfaces.
+        * If new interface added, disable PM on existing interface.
+        * P2P device is a special case, since it is handled by FW similary to
+        * scan. If P2P deviced is added, PM remains enabled on existing
+        * interface.
         * Note: the method below does not count the new interface being added
         * at this moment.
         */
-       mvm->vif_count++;
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+               mvm->vif_count++;
        if (mvm->vif_count > 1) {
                IWL_DEBUG_MAC80211(mvm,
                                   "Disable power on existing interfaces\n");
@@ -526,6 +566,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                mvm->p2p_device_vif = vif;
        }
 
+       iwl_mvm_vif_dbgfs_register(mvm, vif);
        goto out_unlock;
 
  out_unbind:
@@ -539,10 +580,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        /*
         * TODO: remove this temporary code.
         * Currently MVM FW supports power management only on single MAC.
-        * Check if only one additional interface remains after rereasing
+        * Check if only one additional interface remains after releasing
         * current one. Update power mode on the remaining interface.
         */
-       mvm->vif_count--;
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+               mvm->vif_count--;
        IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
                           mvm->vif_count);
        if (mvm->vif_count == 1) {
@@ -557,11 +599,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        return ret;
 }
 
-static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif)
+static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        u32 tfd_msk = 0, ac;
 
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@ -594,12 +634,23 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                 */
                flush_work(&mvm->sta_drained_wk);
        }
+}
+
+static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       iwl_mvm_prepare_mac_removal(mvm, vif);
 
        mutex_lock(&mvm->mutex);
 
+       iwl_mvm_vif_dbgfs_clean(mvm, vif);
+
        /*
         * For AP/GO interface, the tear down of the resources allocated to the
-        * interface should be handled as part of the bss_info_changed flow.
+        * interface is be handled as part of the stop_ap flow.
         */
        if (vif->type == NL80211_IFTYPE_AP) {
                iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
@@ -620,7 +671,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
         * Check if only one additional interface remains after removing
         * current one. Update power mode on the remaining interface.
         */
-       if (mvm->vif_count)
+       if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
        IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
                           mvm->vif_count);
@@ -670,6 +721,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
+                       iwl_mvm_bt_coex_vif_assoc(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
@@ -763,6 +815,8 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
+       iwl_mvm_prepare_mac_removal(mvm, vif);
+
        mutex_lock(&mvm->mutex);
 
        mvmvif->ap_active = false;
@@ -886,7 +940,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
                 */
                break;
        case STA_NOTIFY_AWAKE:
-               if (WARN_ON(mvmsta->sta_id == IWL_INVALID_STATION))
+               if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
                        break;
                iwl_mvm_sta_modify_ps_wake(mvm, sta);
                break;
@@ -1042,6 +1096,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
+               if (vif->type == NL80211_IFTYPE_AP && !sta) {
+                       /* GTK on AP interface is a TX-only key, return 0 */
+                       ret = 0;
+                       key->hw_key_idx = STA_KEY_IDX_INVALID;
+                       break;
+               }
+
                IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
                ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
                if (ret) {
@@ -1050,11 +1111,17 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                         * can't add key for RX, but we don't need it
                         * in the device for TX so still return 0
                         */
+                       key->hw_key_idx = STA_KEY_IDX_INVALID;
                        ret = 0;
                }
 
                break;
        case DISABLE_KEY:
+               if (key->hw_key_idx == STA_KEY_IDX_INVALID) {
+                       ret = 0;
+                       break;
+               }
+
                IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
                ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
                break;
@@ -1081,7 +1148,8 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 static int iwl_mvm_roc(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_channel *channel,
-                      int duration)
+                      int duration,
+                      enum ieee80211_roc_type type)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct cfg80211_chan_def chandef;
@@ -1092,8 +1160,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                return -EINVAL;
        }
 
-       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
-                          duration);
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+                          duration, type);
 
        mutex_lock(&mvm->mutex);
 
@@ -1102,7 +1170,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                                       &chandef, 1, 1);
 
        /* Schedule the time events */
-       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration);
+       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
 
        mutex_unlock(&mvm->mutex);
        IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -1206,6 +1274,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         * will handle quota settings.
         */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
+               mvmvif->monitor_active = true;
                ret = iwl_mvm_update_quotas(mvm, vif);
                if (ret)
                        goto out_remove_binding;
@@ -1236,15 +1305,16 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        if (vif->type == NL80211_IFTYPE_AP)
                goto out_unlock;
 
-       iwl_mvm_binding_remove_vif(mvm, vif);
        switch (vif->type) {
        case NL80211_IFTYPE_MONITOR:
-               iwl_mvm_update_quotas(mvm, vif);
+               mvmvif->monitor_active = false;
+               iwl_mvm_update_quotas(mvm, NULL);
                break;
        default:
                break;
        }
 
+       iwl_mvm_binding_remove_vif(mvm, vif);
 out_unlock:
        mvmvif->phy_ctxt = NULL;
        mutex_unlock(&mvm->mutex);
@@ -1265,6 +1335,15 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
        return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
 }
 
+static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     enum ieee80211_rssi_event rssi_event)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       iwl_mvm_bt_rssi_event(mvm, vif, rssi_event);
+}
+
 struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -1288,6 +1367,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .update_tkip_key = iwl_mvm_mac_update_tkip_key,
        .remain_on_channel = iwl_mvm_roc,
        .cancel_remain_on_channel = iwl_mvm_cancel_roc,
+       .rssi_callback = iwl_mvm_mac_rssi_callback,
 
        .add_chanctx = iwl_mvm_add_chanctx,
        .remove_chanctx = iwl_mvm_remove_chanctx,