Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index 7e169b085afe05f1c4c5a17804e864d8bc15e9f2..3d193f8c33b618cadc50bad99626f973b275085a 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);
@@ -1051,6 +1087,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) {
@@ -1059,11 +1102,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;
@@ -1090,7 +1139,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;
@@ -1101,8 +1151,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);
 
@@ -1111,7 +1161,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");
@@ -1215,6 +1265,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;
@@ -1245,15 +1296,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);