iwlwifi: remove all occurrences of the FSF address paragraph
[linux-2.6-microblaze.git] / drivers / net / wireless / intel / iwlwifi / mvm / tx.c
index ff193dc..c99b79d 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  *
  * 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
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
  * The full GNU General Public License is included in this distribution
  * in the file called COPYING.
  *
@@ -35,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -245,14 +242,18 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec,
                                          ssn);
        } else {
-               tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+               if (ieee80211_is_data(fc))
+                       tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+               else
+                       tx_cmd->tid_tspec = IWL_MAX_TID_COUNT;
+
                if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
                        tx_flags |= TX_CMD_FLG_SEQ_CTL;
                else
                        tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
        }
 
-       /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */
+       /* Default to 0 (BE) when tid_spec is set to IWL_MAX_TID_COUNT */
        if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT)
                ac = tid_to_mac80211_ac[tx_cmd->tid_tspec];
        else
@@ -620,6 +621,66 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
        }
 }
 
+static void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_mvm_vif *mvmvif =
+               iwl_mvm_vif_from_mac80211(info->control.vif);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       int base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt;
+       struct iwl_probe_resp_data *resp_data;
+       u8 *ie, *pos;
+       u8 match[] = {
+               (WLAN_OUI_WFA >> 16) & 0xff,
+               (WLAN_OUI_WFA >> 8) & 0xff,
+               WLAN_OUI_WFA & 0xff,
+               WLAN_OUI_TYPE_WFA_P2P,
+       };
+
+       rcu_read_lock();
+
+       resp_data = rcu_dereference(mvmvif->probe_resp_data);
+       if (!resp_data)
+               goto out;
+
+       if (!resp_data->notif.noa_active)
+               goto out;
+
+       ie = (u8 *)cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC,
+                                         mgmt->u.probe_resp.variable,
+                                         skb->len - base_len,
+                                         match, 4, 2);
+       if (!ie) {
+               IWL_DEBUG_TX(mvm, "probe resp doesn't have P2P IE\n");
+               goto out;
+       }
+
+       if (skb_tailroom(skb) < resp_data->noa_len) {
+               if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) {
+                       IWL_ERR(mvm,
+                               "Failed to reallocate probe resp\n");
+                       goto out;
+               }
+       }
+
+       pos = skb_put(skb, resp_data->noa_len);
+
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       /* Set length of IE body (not including ID and length itself) */
+       *pos++ = resp_data->noa_len - 2;
+       *pos++ = (WLAN_OUI_WFA >> 16) & 0xff;
+       *pos++ = (WLAN_OUI_WFA >> 8) & 0xff;
+       *pos++ = WLAN_OUI_WFA & 0xff;
+       *pos++ = WLAN_OUI_TYPE_WFA_P2P;
+
+       memcpy(pos, &resp_data->notif.noa_attr,
+              resp_data->noa_len - sizeof(struct ieee80211_vendor_ie));
+
+out:
+       rcu_read_unlock();
+}
+
 int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -628,6 +689,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
        struct iwl_device_cmd *dev_cmd;
        u8 sta_id;
        int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       __le16 fc = hdr->frame_control;
        int queue;
 
        /* IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
@@ -689,6 +751,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                }
        }
 
+       if (unlikely(ieee80211_is_probe_resp(fc)))
+               iwl_mvm_probe_resp_set_noa(mvm, skb);
+
        IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, queue);
 
        dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id);
@@ -1010,6 +1075,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
        if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA))
                return -1;
 
+       if (unlikely(ieee80211_is_probe_resp(fc)))
+               iwl_mvm_probe_resp_set_noa(mvm, skb);
+
        dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen,
                                        sta, mvmsta->sta_id);
        if (!dev_cmd)
@@ -1049,6 +1117,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
                        /* update the tx_cmd hdr as it was already copied */
                        tx_cmd->hdr->seq_ctrl = hdr->seq_ctrl;
                }
+       } else if (ieee80211_is_data(fc) && !ieee80211_is_data_qos(fc)) {
+               tid = IWL_TID_NON_QOS;
        }
 
        txq_id = mvmsta->tid_data[tid].txq_id;
@@ -1405,6 +1475,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
        while (!skb_queue_empty(&skbs)) {
                struct sk_buff *skb = __skb_dequeue(&skbs);
                struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               struct ieee80211_hdr *hdr = (void *)skb->data;
                bool flushed = false;
 
                skb_freed++;
@@ -1434,6 +1505,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        break;
                }
 
+               /*
+                * If we are freeing multiple frames, mark all the frames
+                * but the first one as acked, since they were acknowledged
+                * before
+                * */
+               if (skb_freed > 1)
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+
                iwl_mvm_tx_status_check_trigger(mvm, status);
 
                info->status.rates[0].count = tx_resp->failure_frame + 1;
@@ -1449,11 +1528,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
                info->flags &= ~IEEE80211_TX_CTL_AMPDU;
 
-               /* W/A FW bug: seq_ctl is wrong when the status isn't success */
-               if (status != TX_STATUS_SUCCESS) {
-                       struct ieee80211_hdr *hdr = (void *)skb->data;
+               /* W/A FW bug: seq_ctl is wrong upon failure / BAR frame */
+               if (ieee80211_is_back_req(hdr->frame_control))
+                       seq_ctl = 0;
+               else if (status != TX_STATUS_SUCCESS)
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
-               }
 
                if (unlikely(!seq_ctl)) {
                        struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -1525,7 +1604,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                iwl_mvm_tx_airtime(mvm, mvmsta,
                                   le16_to_cpu(tx_resp->wireless_media_time));
 
-               if (tid != IWL_TID_NON_QOS && tid != IWL_MGMT_TID) {
+               if (sta->wme && tid != IWL_MGMT_TID) {
                        struct iwl_mvm_tid_data *tid_data =
                                &mvmsta->tid_data[tid];
                        bool send_eosp_ndp = false;
@@ -1645,20 +1724,24 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
        u16 sequence = le16_to_cpu(pkt->hdr.sequence);
        struct iwl_mvm_sta *mvmsta;
        int queue = SEQ_TO_QUEUE(sequence);
+       struct ieee80211_sta *sta;
 
        if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE &&
                         (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE)))
                return;
 
-       if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
-               return;
-
        iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
 
        rcu_read_lock();
 
        mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id);
 
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+       if (WARN_ON_ONCE(!sta || !sta->wme)) {
+               rcu_read_unlock();
+               return;
+       }
+
        if (!WARN_ON_ONCE(!mvmsta)) {
                mvmsta->tid_data[tid].rate_n_flags =
                        le32_to_cpu(tx_resp->initial_rate);