1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun *
3*4882a593Smuzhiyun * This file is provided under a dual BSD/GPLv2 license. When using or
4*4882a593Smuzhiyun * redistributing this file, you may do so under either license.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * GPL LICENSE SUMMARY
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
9*4882a593Smuzhiyun * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
10*4882a593Smuzhiyun * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
11*4882a593Smuzhiyun * Copyright(c) 2018 - 2020 Intel Corporation
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
14*4882a593Smuzhiyun * it under the terms of version 2 of the GNU General Public License as
15*4882a593Smuzhiyun * published by the Free Software Foundation.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful, but
18*4882a593Smuzhiyun * WITHOUT ANY WARRANTY; without even the implied warranty of
19*4882a593Smuzhiyun * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20*4882a593Smuzhiyun * General Public License for more details.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * The full GNU General Public License is included in this distribution
23*4882a593Smuzhiyun * in the file called COPYING.
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * Contact Information:
26*4882a593Smuzhiyun * Intel Linux Wireless <linuxwifi@intel.com>
27*4882a593Smuzhiyun * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * BSD LICENSE
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
32*4882a593Smuzhiyun * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
33*4882a593Smuzhiyun * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
34*4882a593Smuzhiyun * Copyright(c) 2018 - 2020 Intel Corporation
35*4882a593Smuzhiyun * All rights reserved.
36*4882a593Smuzhiyun *
37*4882a593Smuzhiyun * Redistribution and use in source and binary forms, with or without
38*4882a593Smuzhiyun * modification, are permitted provided that the following conditions
39*4882a593Smuzhiyun * are met:
40*4882a593Smuzhiyun *
41*4882a593Smuzhiyun * * Redistributions of source code must retain the above copyright
42*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer.
43*4882a593Smuzhiyun * * Redistributions in binary form must reproduce the above copyright
44*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer in
45*4882a593Smuzhiyun * the documentation and/or other materials provided with the
46*4882a593Smuzhiyun * distribution.
47*4882a593Smuzhiyun * * Neither the name Intel Corporation nor the names of its
48*4882a593Smuzhiyun * contributors may be used to endorse or promote products derived
49*4882a593Smuzhiyun * from this software without specific prior written permission.
50*4882a593Smuzhiyun *
51*4882a593Smuzhiyun * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
52*4882a593Smuzhiyun * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
53*4882a593Smuzhiyun * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
54*4882a593Smuzhiyun * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
55*4882a593Smuzhiyun * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
56*4882a593Smuzhiyun * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
57*4882a593Smuzhiyun * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58*4882a593Smuzhiyun * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59*4882a593Smuzhiyun * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60*4882a593Smuzhiyun * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
61*4882a593Smuzhiyun * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62*4882a593Smuzhiyun *****************************************************************************/
63*4882a593Smuzhiyun #include <asm/unaligned.h>
64*4882a593Smuzhiyun #include <linux/etherdevice.h>
65*4882a593Smuzhiyun #include <linux/skbuff.h>
66*4882a593Smuzhiyun #include "iwl-trans.h"
67*4882a593Smuzhiyun #include "mvm.h"
68*4882a593Smuzhiyun #include "fw-api.h"
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /*
71*4882a593Smuzhiyun * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
72*4882a593Smuzhiyun *
73*4882a593Smuzhiyun * Copies the phy information in mvm->last_phy_info, it will be used when the
74*4882a593Smuzhiyun * actual data will come from the fw in the next packet.
75*4882a593Smuzhiyun */
iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)76*4882a593Smuzhiyun void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun struct iwl_rx_packet *pkt = rxb_addr(rxb);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
81*4882a593Smuzhiyun mvm->ampdu_ref++;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun #ifdef CONFIG_IWLWIFI_DEBUGFS
84*4882a593Smuzhiyun if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
85*4882a593Smuzhiyun spin_lock(&mvm->drv_stats_lock);
86*4882a593Smuzhiyun mvm->drv_rx_stats.ampdu_count++;
87*4882a593Smuzhiyun spin_unlock(&mvm->drv_stats_lock);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun #endif
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun /*
93*4882a593Smuzhiyun * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
94*4882a593Smuzhiyun *
95*4882a593Smuzhiyun * Adds the rxb to a new skb and give it to mac80211
96*4882a593Smuzhiyun */
iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm * mvm,struct ieee80211_sta * sta,struct napi_struct * napi,struct sk_buff * skb,struct ieee80211_hdr * hdr,u16 len,u8 crypt_len,struct iwl_rx_cmd_buffer * rxb)97*4882a593Smuzhiyun static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
98*4882a593Smuzhiyun struct ieee80211_sta *sta,
99*4882a593Smuzhiyun struct napi_struct *napi,
100*4882a593Smuzhiyun struct sk_buff *skb,
101*4882a593Smuzhiyun struct ieee80211_hdr *hdr, u16 len,
102*4882a593Smuzhiyun u8 crypt_len,
103*4882a593Smuzhiyun struct iwl_rx_cmd_buffer *rxb)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
106*4882a593Smuzhiyun unsigned int fraglen;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /*
109*4882a593Smuzhiyun * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len,
110*4882a593Smuzhiyun * but those are all multiples of 4 long) all goes away, but we
111*4882a593Smuzhiyun * want the *end* of it, which is going to be the start of the IP
112*4882a593Smuzhiyun * header, to be aligned when it gets pulled in.
113*4882a593Smuzhiyun * The beginning of the skb->data is aligned on at least a 4-byte
114*4882a593Smuzhiyun * boundary after allocation. Everything here is aligned at least
115*4882a593Smuzhiyun * on a 2-byte boundary so we can just take hdrlen & 3 and pad by
116*4882a593Smuzhiyun * the result.
117*4882a593Smuzhiyun */
118*4882a593Smuzhiyun skb_reserve(skb, hdrlen & 3);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /* If frame is small enough to fit in skb->head, pull it completely.
121*4882a593Smuzhiyun * If not, only pull ieee80211_hdr (including crypto if present, and
122*4882a593Smuzhiyun * an additional 8 bytes for SNAP/ethertype, see below) so that
123*4882a593Smuzhiyun * splice() or TCP coalesce are more efficient.
124*4882a593Smuzhiyun *
125*4882a593Smuzhiyun * Since, in addition, ieee80211_data_to_8023() always pull in at
126*4882a593Smuzhiyun * least 8 bytes (possibly more for mesh) we can do the same here
127*4882a593Smuzhiyun * to save the cost of doing it later. That still doesn't pull in
128*4882a593Smuzhiyun * the actual IP header since the typical case has a SNAP header.
129*4882a593Smuzhiyun * If the latter changes (there are efforts in the standards group
130*4882a593Smuzhiyun * to do so) we should revisit this and ieee80211_data_to_8023().
131*4882a593Smuzhiyun */
132*4882a593Smuzhiyun hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun skb_put_data(skb, hdr, hdrlen);
135*4882a593Smuzhiyun fraglen = len - hdrlen;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun if (fraglen) {
138*4882a593Smuzhiyun int offset = (void *)hdr + hdrlen -
139*4882a593Smuzhiyun rxb_addr(rxb) + rxb_offset(rxb);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
142*4882a593Smuzhiyun fraglen, rxb->truesize);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun ieee80211_rx_napi(mvm->hw, sta, skb, napi);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun /*
149*4882a593Smuzhiyun * iwl_mvm_get_signal_strength - use new rx PHY INFO API
150*4882a593Smuzhiyun * values are reported by the fw as positive values - need to negate
151*4882a593Smuzhiyun * to obtain their dBM. Account for missing antennas by replacing 0
152*4882a593Smuzhiyun * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
153*4882a593Smuzhiyun */
iwl_mvm_get_signal_strength(struct iwl_mvm * mvm,struct iwl_rx_phy_info * phy_info,struct ieee80211_rx_status * rx_status)154*4882a593Smuzhiyun static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
155*4882a593Smuzhiyun struct iwl_rx_phy_info *phy_info,
156*4882a593Smuzhiyun struct ieee80211_rx_status *rx_status)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun int energy_a, energy_b, energy_c, max_energy;
159*4882a593Smuzhiyun u32 val;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun val =
162*4882a593Smuzhiyun le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
163*4882a593Smuzhiyun energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
164*4882a593Smuzhiyun IWL_RX_INFO_ENERGY_ANT_A_POS;
165*4882a593Smuzhiyun energy_a = energy_a ? -energy_a : S8_MIN;
166*4882a593Smuzhiyun energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
167*4882a593Smuzhiyun IWL_RX_INFO_ENERGY_ANT_B_POS;
168*4882a593Smuzhiyun energy_b = energy_b ? -energy_b : S8_MIN;
169*4882a593Smuzhiyun energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
170*4882a593Smuzhiyun IWL_RX_INFO_ENERGY_ANT_C_POS;
171*4882a593Smuzhiyun energy_c = energy_c ? -energy_c : S8_MIN;
172*4882a593Smuzhiyun max_energy = max(energy_a, energy_b);
173*4882a593Smuzhiyun max_energy = max(max_energy, energy_c);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
176*4882a593Smuzhiyun energy_a, energy_b, energy_c, max_energy);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun rx_status->signal = max_energy;
179*4882a593Smuzhiyun rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
180*4882a593Smuzhiyun RX_RES_PHY_FLAGS_ANTENNA)
181*4882a593Smuzhiyun >> RX_RES_PHY_FLAGS_ANTENNA_POS;
182*4882a593Smuzhiyun rx_status->chain_signal[0] = energy_a;
183*4882a593Smuzhiyun rx_status->chain_signal[1] = energy_b;
184*4882a593Smuzhiyun rx_status->chain_signal[2] = energy_c;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /*
188*4882a593Smuzhiyun * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
189*4882a593Smuzhiyun * @mvm: the mvm object
190*4882a593Smuzhiyun * @hdr: 80211 header
191*4882a593Smuzhiyun * @stats: status in mac80211's format
192*4882a593Smuzhiyun * @rx_pkt_status: status coming from fw
193*4882a593Smuzhiyun *
194*4882a593Smuzhiyun * returns non 0 value if the packet should be dropped
195*4882a593Smuzhiyun */
iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm * mvm,struct ieee80211_hdr * hdr,struct ieee80211_rx_status * stats,u32 rx_pkt_status,u8 * crypt_len)196*4882a593Smuzhiyun static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
197*4882a593Smuzhiyun struct ieee80211_hdr *hdr,
198*4882a593Smuzhiyun struct ieee80211_rx_status *stats,
199*4882a593Smuzhiyun u32 rx_pkt_status,
200*4882a593Smuzhiyun u8 *crypt_len)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun if (!ieee80211_has_protected(hdr->frame_control) ||
203*4882a593Smuzhiyun (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
204*4882a593Smuzhiyun RX_MPDU_RES_STATUS_SEC_NO_ENC)
205*4882a593Smuzhiyun return 0;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /* packet was encrypted with unknown alg */
208*4882a593Smuzhiyun if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
209*4882a593Smuzhiyun RX_MPDU_RES_STATUS_SEC_ENC_ERR)
210*4882a593Smuzhiyun return 0;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
213*4882a593Smuzhiyun case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
214*4882a593Smuzhiyun /* alg is CCM: check MIC only */
215*4882a593Smuzhiyun if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
216*4882a593Smuzhiyun return -1;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun stats->flag |= RX_FLAG_DECRYPTED;
219*4882a593Smuzhiyun *crypt_len = IEEE80211_CCMP_HDR_LEN;
220*4882a593Smuzhiyun return 0;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
223*4882a593Smuzhiyun /* Don't drop the frame and decrypt it in SW */
224*4882a593Smuzhiyun if (!fw_has_api(&mvm->fw->ucode_capa,
225*4882a593Smuzhiyun IWL_UCODE_TLV_API_DEPRECATE_TTAK) &&
226*4882a593Smuzhiyun !(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
227*4882a593Smuzhiyun return 0;
228*4882a593Smuzhiyun *crypt_len = IEEE80211_TKIP_IV_LEN;
229*4882a593Smuzhiyun /* fall through */
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
232*4882a593Smuzhiyun if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
233*4882a593Smuzhiyun return -1;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun stats->flag |= RX_FLAG_DECRYPTED;
236*4882a593Smuzhiyun if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
237*4882a593Smuzhiyun RX_MPDU_RES_STATUS_SEC_WEP_ENC)
238*4882a593Smuzhiyun *crypt_len = IEEE80211_WEP_IV_LEN;
239*4882a593Smuzhiyun return 0;
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
242*4882a593Smuzhiyun if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
243*4882a593Smuzhiyun return -1;
244*4882a593Smuzhiyun stats->flag |= RX_FLAG_DECRYPTED;
245*4882a593Smuzhiyun return 0;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun default:
248*4882a593Smuzhiyun /* Expected in monitor (not having the keys) */
249*4882a593Smuzhiyun if (!mvm->monitor_on)
250*4882a593Smuzhiyun IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun return 0;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
iwl_mvm_rx_handle_tcm(struct iwl_mvm * mvm,struct ieee80211_sta * sta,struct ieee80211_hdr * hdr,u32 len,struct iwl_rx_phy_info * phy_info,u32 rate_n_flags)256*4882a593Smuzhiyun static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,
257*4882a593Smuzhiyun struct ieee80211_sta *sta,
258*4882a593Smuzhiyun struct ieee80211_hdr *hdr, u32 len,
259*4882a593Smuzhiyun struct iwl_rx_phy_info *phy_info,
260*4882a593Smuzhiyun u32 rate_n_flags)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun struct iwl_mvm_sta *mvmsta;
263*4882a593Smuzhiyun struct iwl_mvm_tcm_mac *mdata;
264*4882a593Smuzhiyun int mac;
265*4882a593Smuzhiyun int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */
266*4882a593Smuzhiyun struct iwl_mvm_vif *mvmvif;
267*4882a593Smuzhiyun /* expected throughput in 100Kbps, single stream, 20 MHz */
268*4882a593Smuzhiyun static const u8 thresh_tpt[] = {
269*4882a593Smuzhiyun 9, 18, 30, 42, 60, 78, 90, 96, 120, 135,
270*4882a593Smuzhiyun };
271*4882a593Smuzhiyun u16 thr;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (ieee80211_is_data_qos(hdr->frame_control))
274*4882a593Smuzhiyun ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)];
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun mvmsta = iwl_mvm_sta_from_mac80211(sta);
277*4882a593Smuzhiyun mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD))
280*4882a593Smuzhiyun schedule_delayed_work(&mvm->tcm.work, 0);
281*4882a593Smuzhiyun mdata = &mvm->tcm.data[mac];
282*4882a593Smuzhiyun mdata->rx.pkts[ac]++;
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun /* count the airtime only once for each ampdu */
285*4882a593Smuzhiyun if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) {
286*4882a593Smuzhiyun mdata->rx.last_ampdu_ref = mvm->ampdu_ref;
287*4882a593Smuzhiyun mdata->rx.airtime += le16_to_cpu(phy_info->frame_time);
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK)))
291*4882a593Smuzhiyun return;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun if (mdata->opened_rx_ba_sessions ||
296*4882a593Smuzhiyun mdata->uapsd_nonagg_detect.detected ||
297*4882a593Smuzhiyun (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd &&
298*4882a593Smuzhiyun !mvmvif->queue_params[IEEE80211_AC_VI].uapsd &&
299*4882a593Smuzhiyun !mvmvif->queue_params[IEEE80211_AC_BE].uapsd &&
300*4882a593Smuzhiyun !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) ||
301*4882a593Smuzhiyun mvmsta->sta_id != mvmvif->ap_sta_id)
302*4882a593Smuzhiyun return;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun if (rate_n_flags & RATE_MCS_HT_MSK) {
305*4882a593Smuzhiyun thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK];
306*4882a593Smuzhiyun thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >>
307*4882a593Smuzhiyun RATE_HT_MCS_NSS_POS);
308*4882a593Smuzhiyun } else {
309*4882a593Smuzhiyun if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >=
310*4882a593Smuzhiyun ARRAY_SIZE(thresh_tpt)))
311*4882a593Smuzhiyun return;
312*4882a593Smuzhiyun thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK];
313*4882a593Smuzhiyun thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
314*4882a593Smuzhiyun RATE_VHT_MCS_NSS_POS);
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) >>
318*4882a593Smuzhiyun RATE_MCS_CHAN_WIDTH_POS);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun mdata->uapsd_nonagg_detect.rx_bytes += len;
321*4882a593Smuzhiyun ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr);
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
iwl_mvm_rx_csum(struct ieee80211_sta * sta,struct sk_buff * skb,u32 status)324*4882a593Smuzhiyun static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
325*4882a593Smuzhiyun struct sk_buff *skb,
326*4882a593Smuzhiyun u32 status)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
329*4882a593Smuzhiyun struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun if (mvmvif->features & NETIF_F_RXCSUM &&
332*4882a593Smuzhiyun status & RX_MPDU_RES_STATUS_CSUM_DONE &&
333*4882a593Smuzhiyun status & RX_MPDU_RES_STATUS_CSUM_OK)
334*4882a593Smuzhiyun skb->ip_summed = CHECKSUM_UNNECESSARY;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun /*
338*4882a593Smuzhiyun * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
339*4882a593Smuzhiyun *
340*4882a593Smuzhiyun * Handles the actual data of the Rx packet from the fw
341*4882a593Smuzhiyun */
iwl_mvm_rx_rx_mpdu(struct iwl_mvm * mvm,struct napi_struct * napi,struct iwl_rx_cmd_buffer * rxb)342*4882a593Smuzhiyun void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
343*4882a593Smuzhiyun struct iwl_rx_cmd_buffer *rxb)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun struct ieee80211_hdr *hdr;
346*4882a593Smuzhiyun struct ieee80211_rx_status *rx_status;
347*4882a593Smuzhiyun struct iwl_rx_packet *pkt = rxb_addr(rxb);
348*4882a593Smuzhiyun struct iwl_rx_phy_info *phy_info;
349*4882a593Smuzhiyun struct iwl_rx_mpdu_res_start *rx_res;
350*4882a593Smuzhiyun struct ieee80211_sta *sta = NULL;
351*4882a593Smuzhiyun struct sk_buff *skb;
352*4882a593Smuzhiyun u32 len;
353*4882a593Smuzhiyun u32 rate_n_flags;
354*4882a593Smuzhiyun u32 rx_pkt_status;
355*4882a593Smuzhiyun u8 crypt_len = 0;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun phy_info = &mvm->last_phy_info;
358*4882a593Smuzhiyun rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
359*4882a593Smuzhiyun hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
360*4882a593Smuzhiyun len = le16_to_cpu(rx_res->byte_count);
361*4882a593Smuzhiyun rx_pkt_status = get_unaligned_le32((__le32 *)
362*4882a593Smuzhiyun (pkt->data + sizeof(*rx_res) + len));
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /* Dont use dev_alloc_skb(), we'll have enough headroom once
365*4882a593Smuzhiyun * ieee80211_hdr pulled.
366*4882a593Smuzhiyun */
367*4882a593Smuzhiyun skb = alloc_skb(128, GFP_ATOMIC);
368*4882a593Smuzhiyun if (!skb) {
369*4882a593Smuzhiyun IWL_ERR(mvm, "alloc_skb failed\n");
370*4882a593Smuzhiyun return;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun rx_status = IEEE80211_SKB_RXCB(skb);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun /*
376*4882a593Smuzhiyun * drop the packet if it has failed being decrypted by HW
377*4882a593Smuzhiyun */
378*4882a593Smuzhiyun if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
379*4882a593Smuzhiyun &crypt_len)) {
380*4882a593Smuzhiyun IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
381*4882a593Smuzhiyun rx_pkt_status);
382*4882a593Smuzhiyun kfree_skb(skb);
383*4882a593Smuzhiyun return;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun /*
387*4882a593Smuzhiyun * Keep packets with CRC errors (and with overrun) for monitor mode
388*4882a593Smuzhiyun * (otherwise the firmware discards them) but mark them as bad.
389*4882a593Smuzhiyun */
390*4882a593Smuzhiyun if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
391*4882a593Smuzhiyun !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
392*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
393*4882a593Smuzhiyun rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun /* This will be used in several places later */
397*4882a593Smuzhiyun rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun /* rx_status carries information about the packet to mac80211 */
400*4882a593Smuzhiyun rx_status->mactime = le64_to_cpu(phy_info->timestamp);
401*4882a593Smuzhiyun rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
402*4882a593Smuzhiyun rx_status->band =
403*4882a593Smuzhiyun (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
404*4882a593Smuzhiyun NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
405*4882a593Smuzhiyun rx_status->freq =
406*4882a593Smuzhiyun ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
407*4882a593Smuzhiyun rx_status->band);
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun /* TSF as indicated by the firmware is at INA time */
410*4882a593Smuzhiyun rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
415*4882a593Smuzhiyun (unsigned long long)rx_status->mactime);
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun rcu_read_lock();
418*4882a593Smuzhiyun if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) {
419*4882a593Smuzhiyun u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK;
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT;
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun if (!WARN_ON_ONCE(id >= mvm->fw->ucode_capa.num_stations)) {
424*4882a593Smuzhiyun sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);
425*4882a593Smuzhiyun if (IS_ERR(sta))
426*4882a593Smuzhiyun sta = NULL;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun } else if (!is_multicast_ether_addr(hdr->addr2)) {
429*4882a593Smuzhiyun /* This is fine since we prevent two stations with the same
430*4882a593Smuzhiyun * address from being added.
431*4882a593Smuzhiyun */
432*4882a593Smuzhiyun sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun if (sta) {
436*4882a593Smuzhiyun struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
437*4882a593Smuzhiyun struct ieee80211_vif *tx_blocked_vif =
438*4882a593Smuzhiyun rcu_dereference(mvm->csa_tx_blocked_vif);
439*4882a593Smuzhiyun struct iwl_fw_dbg_trigger_tlv *trig;
440*4882a593Smuzhiyun struct ieee80211_vif *vif = mvmsta->vif;
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun /* We have tx blocked stations (with CS bit). If we heard
443*4882a593Smuzhiyun * frames from a blocked station on a new channel we can
444*4882a593Smuzhiyun * TX to it again.
445*4882a593Smuzhiyun */
446*4882a593Smuzhiyun if (unlikely(tx_blocked_vif) && vif == tx_blocked_vif) {
447*4882a593Smuzhiyun struct iwl_mvm_vif *mvmvif =
448*4882a593Smuzhiyun iwl_mvm_vif_from_mac80211(tx_blocked_vif);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun if (mvmvif->csa_target_freq == rx_status->freq)
451*4882a593Smuzhiyun iwl_mvm_sta_modify_disable_tx_ap(mvm, sta,
452*4882a593Smuzhiyun false);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun rs_update_last_rssi(mvm, mvmsta, rx_status);
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun trig = iwl_fw_dbg_trigger_on(&mvm->fwrt,
458*4882a593Smuzhiyun ieee80211_vif_to_wdev(vif),
459*4882a593Smuzhiyun FW_DBG_TRIGGER_RSSI);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun if (trig && ieee80211_is_beacon(hdr->frame_control)) {
462*4882a593Smuzhiyun struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
463*4882a593Smuzhiyun s32 rssi;
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun rssi_trig = (void *)trig->data;
466*4882a593Smuzhiyun rssi = le32_to_cpu(rssi_trig->rssi);
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun if (rx_status->signal < rssi)
469*4882a593Smuzhiyun iwl_fw_dbg_collect_trig(&mvm->fwrt, trig,
470*4882a593Smuzhiyun NULL);
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun if (!mvm->tcm.paused && len >= sizeof(*hdr) &&
474*4882a593Smuzhiyun !is_multicast_ether_addr(hdr->addr1) &&
475*4882a593Smuzhiyun ieee80211_is_data(hdr->frame_control))
476*4882a593Smuzhiyun iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info,
477*4882a593Smuzhiyun rate_n_flags);
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun if (ieee80211_is_data(hdr->frame_control))
480*4882a593Smuzhiyun iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun rcu_read_unlock();
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun /* set the preamble flag if appropriate */
485*4882a593Smuzhiyun if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
486*4882a593Smuzhiyun rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
489*4882a593Smuzhiyun /*
490*4882a593Smuzhiyun * We know which subframes of an A-MPDU belong
491*4882a593Smuzhiyun * together since we get a single PHY response
492*4882a593Smuzhiyun * from the firmware for all of them
493*4882a593Smuzhiyun */
494*4882a593Smuzhiyun rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
495*4882a593Smuzhiyun rx_status->ampdu_reference = mvm->ampdu_ref;
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun /* Set up the HT phy flags */
499*4882a593Smuzhiyun switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
500*4882a593Smuzhiyun case RATE_MCS_CHAN_WIDTH_20:
501*4882a593Smuzhiyun break;
502*4882a593Smuzhiyun case RATE_MCS_CHAN_WIDTH_40:
503*4882a593Smuzhiyun rx_status->bw = RATE_INFO_BW_40;
504*4882a593Smuzhiyun break;
505*4882a593Smuzhiyun case RATE_MCS_CHAN_WIDTH_80:
506*4882a593Smuzhiyun rx_status->bw = RATE_INFO_BW_80;
507*4882a593Smuzhiyun break;
508*4882a593Smuzhiyun case RATE_MCS_CHAN_WIDTH_160:
509*4882a593Smuzhiyun rx_status->bw = RATE_INFO_BW_160;
510*4882a593Smuzhiyun break;
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun if (!(rate_n_flags & RATE_MCS_CCK_MSK) &&
513*4882a593Smuzhiyun rate_n_flags & RATE_MCS_SGI_MSK)
514*4882a593Smuzhiyun rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
515*4882a593Smuzhiyun if (rate_n_flags & RATE_HT_MCS_GF_MSK)
516*4882a593Smuzhiyun rx_status->enc_flags |= RX_ENC_FLAG_HT_GF;
517*4882a593Smuzhiyun if (rate_n_flags & RATE_MCS_LDPC_MSK)
518*4882a593Smuzhiyun rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
519*4882a593Smuzhiyun if (rate_n_flags & RATE_MCS_HT_MSK) {
520*4882a593Smuzhiyun u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >>
521*4882a593Smuzhiyun RATE_MCS_STBC_POS;
522*4882a593Smuzhiyun rx_status->encoding = RX_ENC_HT;
523*4882a593Smuzhiyun rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
524*4882a593Smuzhiyun rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
525*4882a593Smuzhiyun } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
526*4882a593Smuzhiyun u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >>
527*4882a593Smuzhiyun RATE_MCS_STBC_POS;
528*4882a593Smuzhiyun rx_status->nss =
529*4882a593Smuzhiyun ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
530*4882a593Smuzhiyun RATE_VHT_MCS_NSS_POS) + 1;
531*4882a593Smuzhiyun rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
532*4882a593Smuzhiyun rx_status->encoding = RX_ENC_VHT;
533*4882a593Smuzhiyun rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
534*4882a593Smuzhiyun if (rate_n_flags & RATE_MCS_BF_MSK)
535*4882a593Smuzhiyun rx_status->enc_flags |= RX_ENC_FLAG_BF;
536*4882a593Smuzhiyun } else {
537*4882a593Smuzhiyun int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
538*4882a593Smuzhiyun rx_status->band);
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun if (WARN(rate < 0 || rate > 0xFF,
541*4882a593Smuzhiyun "Invalid rate flags 0x%x, band %d,\n",
542*4882a593Smuzhiyun rate_n_flags, rx_status->band)) {
543*4882a593Smuzhiyun kfree_skb(skb);
544*4882a593Smuzhiyun return;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun rx_status->rate_idx = rate;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun #ifdef CONFIG_IWLWIFI_DEBUGFS
550*4882a593Smuzhiyun iwl_mvm_update_frame_stats(mvm, rate_n_flags,
551*4882a593Smuzhiyun rx_status->flag & RX_FLAG_AMPDU_DETAILS);
552*4882a593Smuzhiyun #endif
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun if (unlikely((ieee80211_is_beacon(hdr->frame_control) ||
555*4882a593Smuzhiyun ieee80211_is_probe_resp(hdr->frame_control)) &&
556*4882a593Smuzhiyun mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED))
557*4882a593Smuzhiyun mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND;
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun if (unlikely(ieee80211_is_beacon(hdr->frame_control) ||
560*4882a593Smuzhiyun ieee80211_is_probe_resp(hdr->frame_control)))
561*4882a593Smuzhiyun rx_status->boottime_ns = ktime_get_boottime_ns();
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun iwl_mvm_pass_packet_to_mac80211(mvm, sta, napi, skb, hdr, len,
564*4882a593Smuzhiyun crypt_len, rxb);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun struct iwl_mvm_stat_data {
568*4882a593Smuzhiyun struct iwl_mvm *mvm;
569*4882a593Smuzhiyun __le32 flags;
570*4882a593Smuzhiyun __le32 mac_id;
571*4882a593Smuzhiyun u8 beacon_filter_average_energy;
572*4882a593Smuzhiyun __le32 *beacon_counter;
573*4882a593Smuzhiyun u8 *beacon_average_energy;
574*4882a593Smuzhiyun };
575*4882a593Smuzhiyun
iwl_mvm_stat_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)576*4882a593Smuzhiyun static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
577*4882a593Smuzhiyun struct ieee80211_vif *vif)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun struct iwl_mvm_stat_data *data = _data;
580*4882a593Smuzhiyun struct iwl_mvm *mvm = data->mvm;
581*4882a593Smuzhiyun int sig = -data->beacon_filter_average_energy;
582*4882a593Smuzhiyun int last_event;
583*4882a593Smuzhiyun int thold = vif->bss_conf.cqm_rssi_thold;
584*4882a593Smuzhiyun int hyst = vif->bss_conf.cqm_rssi_hyst;
585*4882a593Smuzhiyun u16 id = le32_to_cpu(data->mac_id);
586*4882a593Smuzhiyun struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
587*4882a593Smuzhiyun u16 vif_id = mvmvif->id;
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun /* This doesn't need the MAC ID check since it's not taking the
590*4882a593Smuzhiyun * data copied into the "data" struct, but rather the data from
591*4882a593Smuzhiyun * the notification directly.
592*4882a593Smuzhiyun */
593*4882a593Smuzhiyun mvmvif->beacon_stats.num_beacons =
594*4882a593Smuzhiyun le32_to_cpu(data->beacon_counter[vif_id]);
595*4882a593Smuzhiyun mvmvif->beacon_stats.avg_signal =
596*4882a593Smuzhiyun -data->beacon_average_energy[vif_id];
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun /* make sure that beacon statistics don't go backwards with TCM
599*4882a593Smuzhiyun * request to clear statistics
600*4882a593Smuzhiyun */
601*4882a593Smuzhiyun if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
602*4882a593Smuzhiyun mvmvif->beacon_stats.accu_num_beacons +=
603*4882a593Smuzhiyun mvmvif->beacon_stats.num_beacons;
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun if (mvmvif->id != id)
606*4882a593Smuzhiyun return;
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun if (vif->type != NL80211_IFTYPE_STATION)
609*4882a593Smuzhiyun return;
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun if (sig == 0) {
612*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
613*4882a593Smuzhiyun return;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun mvmvif->bf_data.ave_beacon_signal = sig;
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun /* BT Coex */
619*4882a593Smuzhiyun if (mvmvif->bf_data.bt_coex_min_thold !=
620*4882a593Smuzhiyun mvmvif->bf_data.bt_coex_max_thold) {
621*4882a593Smuzhiyun last_event = mvmvif->bf_data.last_bt_coex_event;
622*4882a593Smuzhiyun if (sig > mvmvif->bf_data.bt_coex_max_thold &&
623*4882a593Smuzhiyun (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
624*4882a593Smuzhiyun last_event == 0)) {
625*4882a593Smuzhiyun mvmvif->bf_data.last_bt_coex_event = sig;
626*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
627*4882a593Smuzhiyun sig);
628*4882a593Smuzhiyun iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
629*4882a593Smuzhiyun } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
630*4882a593Smuzhiyun (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
631*4882a593Smuzhiyun last_event == 0)) {
632*4882a593Smuzhiyun mvmvif->bf_data.last_bt_coex_event = sig;
633*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
634*4882a593Smuzhiyun sig);
635*4882a593Smuzhiyun iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun
639*4882a593Smuzhiyun if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
640*4882a593Smuzhiyun return;
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun /* CQM Notification */
643*4882a593Smuzhiyun last_event = mvmvif->bf_data.last_cqm_event;
644*4882a593Smuzhiyun if (thold && sig < thold && (last_event == 0 ||
645*4882a593Smuzhiyun sig < last_event - hyst)) {
646*4882a593Smuzhiyun mvmvif->bf_data.last_cqm_event = sig;
647*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
648*4882a593Smuzhiyun sig);
649*4882a593Smuzhiyun ieee80211_cqm_rssi_notify(
650*4882a593Smuzhiyun vif,
651*4882a593Smuzhiyun NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
652*4882a593Smuzhiyun sig,
653*4882a593Smuzhiyun GFP_KERNEL);
654*4882a593Smuzhiyun } else if (sig > thold &&
655*4882a593Smuzhiyun (last_event == 0 || sig > last_event + hyst)) {
656*4882a593Smuzhiyun mvmvif->bf_data.last_cqm_event = sig;
657*4882a593Smuzhiyun IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
658*4882a593Smuzhiyun sig);
659*4882a593Smuzhiyun ieee80211_cqm_rssi_notify(
660*4882a593Smuzhiyun vif,
661*4882a593Smuzhiyun NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
662*4882a593Smuzhiyun sig,
663*4882a593Smuzhiyun GFP_KERNEL);
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun static inline void
iwl_mvm_rx_stats_check_trigger(struct iwl_mvm * mvm,struct iwl_rx_packet * pkt)668*4882a593Smuzhiyun iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
669*4882a593Smuzhiyun {
670*4882a593Smuzhiyun struct iwl_fw_dbg_trigger_tlv *trig;
671*4882a593Smuzhiyun struct iwl_fw_dbg_trigger_stats *trig_stats;
672*4882a593Smuzhiyun u32 trig_offset, trig_thold;
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, NULL, FW_DBG_TRIGGER_STATS);
675*4882a593Smuzhiyun if (!trig)
676*4882a593Smuzhiyun return;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun trig_stats = (void *)trig->data;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun trig_offset = le32_to_cpu(trig_stats->stop_offset);
681*4882a593Smuzhiyun trig_thold = le32_to_cpu(trig_stats->stop_threshold);
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
684*4882a593Smuzhiyun return;
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
687*4882a593Smuzhiyun return;
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, NULL);
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun
iwl_mvm_update_avg_energy(struct iwl_mvm * mvm,u8 energy[IWL_MVM_STATION_COUNT_MAX])692*4882a593Smuzhiyun static void iwl_mvm_update_avg_energy(struct iwl_mvm *mvm,
693*4882a593Smuzhiyun u8 energy[IWL_MVM_STATION_COUNT_MAX])
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun int i;
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun if (WARN_ONCE(mvm->fw->ucode_capa.num_stations >
698*4882a593Smuzhiyun IWL_MVM_STATION_COUNT_MAX,
699*4882a593Smuzhiyun "Driver and FW station count mismatch %d\n",
700*4882a593Smuzhiyun mvm->fw->ucode_capa.num_stations))
701*4882a593Smuzhiyun return;
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun rcu_read_lock();
704*4882a593Smuzhiyun for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
705*4882a593Smuzhiyun struct iwl_mvm_sta *sta;
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun if (!energy[i])
708*4882a593Smuzhiyun continue;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun sta = iwl_mvm_sta_from_staid_rcu(mvm, i);
711*4882a593Smuzhiyun if (!sta)
712*4882a593Smuzhiyun continue;
713*4882a593Smuzhiyun sta->avg_energy = energy[i];
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun rcu_read_unlock();
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun static void
iwl_mvm_update_tcm_from_stats(struct iwl_mvm * mvm,__le32 * air_time_le,__le32 * rx_bytes_le)719*4882a593Smuzhiyun iwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le,
720*4882a593Smuzhiyun __le32 *rx_bytes_le)
721*4882a593Smuzhiyun {
722*4882a593Smuzhiyun int i;
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun spin_lock(&mvm->tcm.lock);
725*4882a593Smuzhiyun for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {
726*4882a593Smuzhiyun struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i];
727*4882a593Smuzhiyun u32 rx_bytes = le32_to_cpu(rx_bytes_le[i]);
728*4882a593Smuzhiyun u32 airtime = le32_to_cpu(air_time_le[i]);
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun mdata->rx.airtime += airtime;
731*4882a593Smuzhiyun mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes;
732*4882a593Smuzhiyun if (airtime) {
733*4882a593Smuzhiyun /* re-init every time to store rate from FW */
734*4882a593Smuzhiyun ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
735*4882a593Smuzhiyun ewma_rate_add(&mdata->uapsd_nonagg_detect.rate,
736*4882a593Smuzhiyun rx_bytes * 8 / airtime);
737*4882a593Smuzhiyun }
738*4882a593Smuzhiyun }
739*4882a593Smuzhiyun spin_unlock(&mvm->tcm.lock);
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun static void
iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm * mvm,struct iwl_rx_packet * pkt)743*4882a593Smuzhiyun iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm,
744*4882a593Smuzhiyun struct iwl_rx_packet *pkt)
745*4882a593Smuzhiyun {
746*4882a593Smuzhiyun struct iwl_mvm_stat_data data = {
747*4882a593Smuzhiyun .mvm = mvm,
748*4882a593Smuzhiyun };
749*4882a593Smuzhiyun u8 beacon_average_energy[MAC_INDEX_AUX];
750*4882a593Smuzhiyun u8 average_energy[IWL_MVM_STATION_COUNT_MAX];
751*4882a593Smuzhiyun struct iwl_statistics_operational_ntfy *stats;
752*4882a593Smuzhiyun int expected_size;
753*4882a593Smuzhiyun __le32 flags;
754*4882a593Smuzhiyun int i;
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun expected_size = sizeof(*stats);
757*4882a593Smuzhiyun if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) < expected_size,
758*4882a593Smuzhiyun "received invalid statistics size (%d)!, expected_size: %d\n",
759*4882a593Smuzhiyun iwl_rx_packet_payload_len(pkt), expected_size))
760*4882a593Smuzhiyun return;
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun stats = (void *)&pkt->data;
763*4882a593Smuzhiyun
764*4882a593Smuzhiyun if (WARN_ONCE(stats->hdr.type != FW_STATISTICS_OPERATIONAL ||
765*4882a593Smuzhiyun stats->hdr.version != 1,
766*4882a593Smuzhiyun "received unsupported hdr type %d, version %d\n",
767*4882a593Smuzhiyun stats->hdr.type, stats->hdr.version))
768*4882a593Smuzhiyun return;
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun flags = stats->flags;
771*4882a593Smuzhiyun mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time);
772*4882a593Smuzhiyun mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time);
773*4882a593Smuzhiyun mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf);
774*4882a593Smuzhiyun mvm->radio_stats.on_time_scan = le64_to_cpu(stats->on_time_scan);
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun iwl_mvm_rx_stats_check_trigger(mvm, pkt);
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun data.mac_id = stats->mac_id;
779*4882a593Smuzhiyun data.beacon_filter_average_energy =
780*4882a593Smuzhiyun le32_to_cpu(stats->beacon_filter_average_energy);
781*4882a593Smuzhiyun data.flags = flags;
782*4882a593Smuzhiyun data.beacon_counter = stats->beacon_counter;
783*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(beacon_average_energy); i++)
784*4882a593Smuzhiyun beacon_average_energy[i] =
785*4882a593Smuzhiyun le32_to_cpu(stats->beacon_average_energy[i]);
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun data.beacon_average_energy = beacon_average_energy;
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun ieee80211_iterate_active_interfaces(mvm->hw,
790*4882a593Smuzhiyun IEEE80211_IFACE_ITER_NORMAL,
791*4882a593Smuzhiyun iwl_mvm_stat_iterator,
792*4882a593Smuzhiyun &data);
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(average_energy); i++)
795*4882a593Smuzhiyun average_energy[i] = le32_to_cpu(stats->average_energy[i]);
796*4882a593Smuzhiyun iwl_mvm_update_avg_energy(mvm, average_energy);
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun /*
799*4882a593Smuzhiyun * Don't update in case the statistics are not cleared, since
800*4882a593Smuzhiyun * we will end up counting twice the same airtime, once in TCM
801*4882a593Smuzhiyun * request and once in statistics notification.
802*4882a593Smuzhiyun */
803*4882a593Smuzhiyun if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
804*4882a593Smuzhiyun iwl_mvm_update_tcm_from_stats(mvm, stats->air_time,
805*4882a593Smuzhiyun stats->rx_bytes);
806*4882a593Smuzhiyun }
807*4882a593Smuzhiyun
iwl_mvm_handle_rx_statistics(struct iwl_mvm * mvm,struct iwl_rx_packet * pkt)808*4882a593Smuzhiyun void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
809*4882a593Smuzhiyun struct iwl_rx_packet *pkt)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun struct iwl_mvm_stat_data data = {
812*4882a593Smuzhiyun .mvm = mvm,
813*4882a593Smuzhiyun };
814*4882a593Smuzhiyun __le32 *bytes, *air_time, flags;
815*4882a593Smuzhiyun int expected_size;
816*4882a593Smuzhiyun u8 *energy;
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun /* From ver 14 and up we use TLV statistics format */
819*4882a593Smuzhiyun if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP,
820*4882a593Smuzhiyun STATISTICS_CMD, 0) >= 14)
821*4882a593Smuzhiyun return iwl_mvm_handle_rx_statistics_tlv(mvm, pkt);
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
824*4882a593Smuzhiyun if (iwl_mvm_has_new_rx_api(mvm))
825*4882a593Smuzhiyun expected_size = sizeof(struct iwl_notif_statistics_v11);
826*4882a593Smuzhiyun else
827*4882a593Smuzhiyun expected_size = sizeof(struct iwl_notif_statistics_v10);
828*4882a593Smuzhiyun } else {
829*4882a593Smuzhiyun expected_size = sizeof(struct iwl_notif_statistics);
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) != expected_size,
833*4882a593Smuzhiyun "received invalid statistics size (%d)!\n",
834*4882a593Smuzhiyun iwl_rx_packet_payload_len(pkt)))
835*4882a593Smuzhiyun return;
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
838*4882a593Smuzhiyun struct iwl_notif_statistics_v11 *stats = (void *)&pkt->data;
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun data.mac_id = stats->rx.general.mac_id;
841*4882a593Smuzhiyun data.beacon_filter_average_energy =
842*4882a593Smuzhiyun stats->general.common.beacon_filter_average_energy;
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun mvm->rx_stats_v3 = stats->rx;
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun mvm->radio_stats.rx_time =
847*4882a593Smuzhiyun le64_to_cpu(stats->general.common.rx_time);
848*4882a593Smuzhiyun mvm->radio_stats.tx_time =
849*4882a593Smuzhiyun le64_to_cpu(stats->general.common.tx_time);
850*4882a593Smuzhiyun mvm->radio_stats.on_time_rf =
851*4882a593Smuzhiyun le64_to_cpu(stats->general.common.on_time_rf);
852*4882a593Smuzhiyun mvm->radio_stats.on_time_scan =
853*4882a593Smuzhiyun le64_to_cpu(stats->general.common.on_time_scan);
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun data.beacon_counter = stats->general.beacon_counter;
856*4882a593Smuzhiyun data.beacon_average_energy =
857*4882a593Smuzhiyun stats->general.beacon_average_energy;
858*4882a593Smuzhiyun flags = stats->flag;
859*4882a593Smuzhiyun } else {
860*4882a593Smuzhiyun struct iwl_notif_statistics *stats = (void *)&pkt->data;
861*4882a593Smuzhiyun
862*4882a593Smuzhiyun data.mac_id = stats->rx.general.mac_id;
863*4882a593Smuzhiyun data.beacon_filter_average_energy =
864*4882a593Smuzhiyun stats->general.common.beacon_filter_average_energy;
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun mvm->rx_stats = stats->rx;
867*4882a593Smuzhiyun
868*4882a593Smuzhiyun mvm->radio_stats.rx_time =
869*4882a593Smuzhiyun le64_to_cpu(stats->general.common.rx_time);
870*4882a593Smuzhiyun mvm->radio_stats.tx_time =
871*4882a593Smuzhiyun le64_to_cpu(stats->general.common.tx_time);
872*4882a593Smuzhiyun mvm->radio_stats.on_time_rf =
873*4882a593Smuzhiyun le64_to_cpu(stats->general.common.on_time_rf);
874*4882a593Smuzhiyun mvm->radio_stats.on_time_scan =
875*4882a593Smuzhiyun le64_to_cpu(stats->general.common.on_time_scan);
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun data.beacon_counter = stats->general.beacon_counter;
878*4882a593Smuzhiyun data.beacon_average_energy =
879*4882a593Smuzhiyun stats->general.beacon_average_energy;
880*4882a593Smuzhiyun flags = stats->flag;
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun data.flags = flags;
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun iwl_mvm_rx_stats_check_trigger(mvm, pkt);
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun ieee80211_iterate_active_interfaces(mvm->hw,
887*4882a593Smuzhiyun IEEE80211_IFACE_ITER_NORMAL,
888*4882a593Smuzhiyun iwl_mvm_stat_iterator,
889*4882a593Smuzhiyun &data);
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun if (!iwl_mvm_has_new_rx_api(mvm))
892*4882a593Smuzhiyun return;
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
895*4882a593Smuzhiyun struct iwl_notif_statistics_v11 *v11 = (void *)&pkt->data;
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun energy = (void *)&v11->load_stats.avg_energy;
898*4882a593Smuzhiyun bytes = (void *)&v11->load_stats.byte_count;
899*4882a593Smuzhiyun air_time = (void *)&v11->load_stats.air_time;
900*4882a593Smuzhiyun } else {
901*4882a593Smuzhiyun struct iwl_notif_statistics *stats = (void *)&pkt->data;
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun energy = (void *)&stats->load_stats.avg_energy;
904*4882a593Smuzhiyun bytes = (void *)&stats->load_stats.byte_count;
905*4882a593Smuzhiyun air_time = (void *)&stats->load_stats.air_time;
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun iwl_mvm_update_avg_energy(mvm, energy);
909*4882a593Smuzhiyun
910*4882a593Smuzhiyun /*
911*4882a593Smuzhiyun * Don't update in case the statistics are not cleared, since
912*4882a593Smuzhiyun * we will end up counting twice the same airtime, once in TCM
913*4882a593Smuzhiyun * request and once in statistics notification.
914*4882a593Smuzhiyun */
915*4882a593Smuzhiyun if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
916*4882a593Smuzhiyun iwl_mvm_update_tcm_from_stats(mvm, air_time, bytes);
917*4882a593Smuzhiyun
918*4882a593Smuzhiyun }
919*4882a593Smuzhiyun
iwl_mvm_rx_statistics(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)920*4882a593Smuzhiyun void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
921*4882a593Smuzhiyun {
922*4882a593Smuzhiyun iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
923*4882a593Smuzhiyun }
924*4882a593Smuzhiyun
iwl_mvm_window_status_notif(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)925*4882a593Smuzhiyun void iwl_mvm_window_status_notif(struct iwl_mvm *mvm,
926*4882a593Smuzhiyun struct iwl_rx_cmd_buffer *rxb)
927*4882a593Smuzhiyun {
928*4882a593Smuzhiyun struct iwl_rx_packet *pkt = rxb_addr(rxb);
929*4882a593Smuzhiyun struct iwl_ba_window_status_notif *notif = (void *)pkt->data;
930*4882a593Smuzhiyun int i;
931*4882a593Smuzhiyun u32 pkt_len = iwl_rx_packet_payload_len(pkt);
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun if (WARN_ONCE(pkt_len != sizeof(*notif),
934*4882a593Smuzhiyun "Received window status notification of wrong size (%u)\n",
935*4882a593Smuzhiyun pkt_len))
936*4882a593Smuzhiyun return;
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun rcu_read_lock();
939*4882a593Smuzhiyun for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) {
940*4882a593Smuzhiyun struct ieee80211_sta *sta;
941*4882a593Smuzhiyun u8 sta_id, tid;
942*4882a593Smuzhiyun u64 bitmap;
943*4882a593Smuzhiyun u32 ssn;
944*4882a593Smuzhiyun u16 ratid;
945*4882a593Smuzhiyun u16 received_mpdu;
946*4882a593Smuzhiyun
947*4882a593Smuzhiyun ratid = le16_to_cpu(notif->ra_tid[i]);
948*4882a593Smuzhiyun /* check that this TID is valid */
949*4882a593Smuzhiyun if (!(ratid & BA_WINDOW_STATUS_VALID_MSK))
950*4882a593Smuzhiyun continue;
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]);
953*4882a593Smuzhiyun if (received_mpdu == 0)
954*4882a593Smuzhiyun continue;
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun tid = ratid & BA_WINDOW_STATUS_TID_MSK;
957*4882a593Smuzhiyun /* get the station */
958*4882a593Smuzhiyun sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK)
959*4882a593Smuzhiyun >> BA_WINDOW_STATUS_STA_ID_POS;
960*4882a593Smuzhiyun sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
961*4882a593Smuzhiyun if (IS_ERR_OR_NULL(sta))
962*4882a593Smuzhiyun continue;
963*4882a593Smuzhiyun bitmap = le64_to_cpu(notif->bitmap[i]);
964*4882a593Smuzhiyun ssn = le32_to_cpu(notif->start_seq_num[i]);
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun /* update mac80211 with the bitmap for the reordering buffer */
967*4882a593Smuzhiyun ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap,
968*4882a593Smuzhiyun received_mpdu);
969*4882a593Smuzhiyun }
970*4882a593Smuzhiyun rcu_read_unlock();
971*4882a593Smuzhiyun }
972