Merge git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers.git
[cascardo/linux.git] / drivers / net / wireless / intel / iwlwifi / mvm / rxmq.c
index 2c61516..df6c32c 100644 (file)
 #include "fw-api.h"
 #include "fw-dbg.h"
 
-void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
-{
-       mvm->ampdu_ref++;
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
-               spin_lock(&mvm->drv_stats_lock);
-               mvm->drv_rx_stats.ampdu_count++;
-               spin_unlock(&mvm->drv_stats_lock);
-       }
-#endif
-}
-
 static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
                                   int queue, struct ieee80211_sta *sta)
 {
@@ -489,6 +476,9 @@ void iwl_mvm_reorder_timer_expired(unsigned long data)
                rcu_read_lock();
                sta = rcu_dereference(buf->mvm->fw_id_to_mac_id[buf->sta_id]);
                /* SN is set to the last expired frame + 1 */
+               IWL_DEBUG_HT(buf->mvm,
+                            "Releasing expired frames for sta %u, sn %d\n",
+                            buf->sta_id, sn);
                iwl_mvm_release_frames(buf->mvm, sta, NULL, buf, sn);
                rcu_read_unlock();
        } else if (buf->num_stored) {
@@ -587,6 +577,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
        struct sk_buff *tail;
        u32 reorder = le32_to_cpu(desc->reorder_data);
        bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU;
+       bool last_subframe =
+               desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME;
        u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
        u8 sub_frame_idx = desc->amsdu_info &
                           IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
@@ -653,7 +645,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
        /* release immediately if allowed by nssn and no stored frames */
        if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {
                if (iwl_mvm_is_sn_less(buffer->head_sn, nssn,
-                                      buffer->buf_size))
+                                      buffer->buf_size) &&
+                  (!amsdu || last_subframe))
                        buffer->head_sn = nssn;
                /* No need to update AMSDU last SN - we are moving the head */
                spin_unlock_bh(&buffer->lock);
@@ -687,7 +680,20 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
                buffer->last_sub_index = sub_frame_idx;
        }
 
-       iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn);
+       /*
+        * We cannot trust NSSN for AMSDU sub-frames that are not the last.
+        * The reason is that NSSN advances on the first sub-frame, and may
+        * cause the reorder buffer to advance before all the sub-frames arrive.
+        * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with
+        * SN 1. NSSN for first sub frame will be 3 with the result of driver
+        * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is
+        * already ahead and it will be dropped.
+        * If the last sub-frame is not on this queue - we will get frame
+        * release notification with up to date NSSN.
+        */
+       if (!amsdu || last_subframe)
+               iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn);
+
        spin_unlock_bh(&buffer->lock);
        return true;
 
@@ -736,6 +742,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
        struct ieee80211_hdr *hdr = (void *)(pkt->data + sizeof(*desc));
        u32 len = le16_to_cpu(desc->mpdu_len);
        u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags);
+       u16 phy_info = le16_to_cpu(desc->phy_info);
        struct ieee80211_sta *sta = NULL;
        struct sk_buff *skb;
        u8 crypt_len = 0;
@@ -766,16 +773,34 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                             le16_to_cpu(desc->status));
                rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
        }
-
-       rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise);
+       /* set the preamble flag if appropriate */
+       if (phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
+               rx_status->flag |= RX_FLAG_SHORTPRE;
+
+       if (likely(!(phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) {
+               rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise);
+               /* TSF as indicated by the firmware is at INA time */
+               rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
+       }
        rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise);
        rx_status->band = desc->channel > 14 ? NL80211_BAND_5GHZ :
                                               NL80211_BAND_2GHZ;
        rx_status->freq = ieee80211_channel_to_frequency(desc->channel,
                                                         rx_status->band);
        iwl_mvm_get_signal_strength(mvm, desc, rx_status);
-       /* TSF as indicated by the firmware is at INA time */
-       rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
+
+       /* update aggregation data for monitor sake on default queue */
+       if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+               bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
+
+               rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status->ampdu_reference = mvm->ampdu_ref;
+               /* toggle is switched whenever new aggregation starts */
+               if (toggle_bit != mvm->ampdu_toggle) {
+                       mvm->ampdu_ref++;
+                       mvm->ampdu_toggle = toggle_bit;
+               }
+       }
 
        rcu_read_lock();
 
@@ -797,6 +822,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
 
        if (sta) {
                struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               struct ieee80211_vif *tx_blocked_vif =
+                       rcu_dereference(mvm->csa_tx_blocked_vif);
                u8 baid = (u8)((le32_to_cpu(desc->reorder_data) &
                               IWL_RX_MPDU_REORDER_BAID_MASK) >>
                               IWL_RX_MPDU_REORDER_BAID_SHIFT);
@@ -806,8 +833,15 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                 * frames from a blocked station on a new channel we can
                 * TX to it again.
                 */
-               if (unlikely(mvm->csa_tx_block_bcn_timeout))
-                       iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+               if (unlikely(tx_blocked_vif) &&
+                   tx_blocked_vif == mvmsta->vif) {
+                       struct iwl_mvm_vif *mvmvif =
+                               iwl_mvm_vif_from_mac80211(tx_blocked_vif);
+
+                       if (mvmvif->csa_target_freq == rx_status->freq)
+                               iwl_mvm_sta_modify_disable_tx_ap(mvm, sta,
+                                                                false);
+               }
 
                rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
 
@@ -830,8 +864,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                                iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
                }
 
-               /* TODO: multi queue TCM */
-
                if (ieee80211_is_data(hdr->frame_control))
                        iwl_mvm_rx_csum(sta, skb, desc);
 
@@ -856,14 +888,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                        iwl_mvm_agg_rx_received(mvm, baid);
        }
 
-       /*
-        * TODO: PHY info.
-        * Verify we don't have the information in the MPDU descriptor and
-        * that it is not needed.
-        * Make sure for monitor mode that we are on default queue, update
-        * ampdu_ref and the rest of phy info then
-        */
-
        /* Set up the HT phy flags */
        switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
        case RATE_MCS_CHAN_WIDTH_20:
@@ -907,8 +931,18 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                                                            rx_status->band);
        }
 
-       /* TODO: PHY info - update ampdu queue statistics (for debugfs) */
-       /* TODO: PHY info - gscan */
+       /* management stuff on default queue */
+       if (!queue) {
+               if (unlikely((ieee80211_is_beacon(hdr->frame_control) ||
+                             ieee80211_is_probe_resp(hdr->frame_control)) &&
+                            mvm->sched_scan_pass_all ==
+                            SCHED_SCAN_PASS_ALL_ENABLED))
+                       mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND;
+
+               if (unlikely(ieee80211_is_beacon(hdr->frame_control) ||
+                            ieee80211_is_probe_resp(hdr->frame_control)))
+                       rx_status->boottime_ns = ktime_get_boot_ns();
+       }
 
        iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
        if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc))
@@ -927,6 +961,9 @@ void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
 
        int baid = release->baid;
 
+       IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n",
+                    release->baid, le16_to_cpu(release->nssn));
+
        if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID))
                return;