Commit | Line | Data |
---|---|---|
8e99ea8d JB |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* | |
740dfecc | 3 | * Copyright (C) 2012-2014, 2018-2024 Intel Corporation |
8e99ea8d JB |
4 | * Copyright (C) 2013-2015 Intel Mobile Communications GmbH |
5 | * Copyright (C) 2016-2017 Intel Deutschland GmbH | |
6 | */ | |
c5aaa8be | 7 | #include <asm/unaligned.h> |
a399f980 | 8 | #include <linux/etherdevice.h> |
93190fb0 | 9 | #include <linux/skbuff.h> |
8ca151b5 | 10 | #include "iwl-trans.h" |
8ca151b5 JB |
11 | #include "mvm.h" |
12 | #include "fw-api.h" | |
13 | ||
14 | /* | |
15 | * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler | |
16 | * | |
17 | * Copies the phy information in mvm->last_phy_info, it will be used when the | |
18 | * actual data will come from the fw in the next packet. | |
19 | */ | |
0416841d | 20 | void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) |
8ca151b5 JB |
21 | { |
22 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
1e1a58be JB |
23 | unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); |
24 | ||
25 | if (unlikely(pkt_len < sizeof(mvm->last_phy_info))) | |
26 | return; | |
8ca151b5 JB |
27 | |
28 | memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); | |
29 | mvm->ampdu_ref++; | |
5fc0f76c ES |
30 | |
31 | #ifdef CONFIG_IWLWIFI_DEBUGFS | |
32 | if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { | |
33 | spin_lock(&mvm->drv_stats_lock); | |
34 | mvm->drv_rx_stats.ampdu_count++; | |
35 | spin_unlock(&mvm->drv_stats_lock); | |
36 | } | |
37 | #endif | |
8ca151b5 JB |
38 | } |
39 | ||
40 | /* | |
41 | * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 | |
42 | * | |
43 | * Adds the rxb to a new skb and give it to mac80211 | |
44 | */ | |
45 | static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, | |
43ec72b7 | 46 | struct ieee80211_sta *sta, |
1be5d8cc | 47 | struct napi_struct *napi, |
d0963b5d | 48 | struct sk_buff *skb, |
8ca151b5 | 49 | struct ieee80211_hdr *hdr, u16 len, |
bdbc58ab | 50 | u8 crypt_len, |
d0963b5d | 51 | struct iwl_rx_cmd_buffer *rxb) |
8ca151b5 | 52 | { |
5cddd05c JB |
53 | unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); |
54 | unsigned int fraglen; | |
55 | ||
56 | /* | |
57 | * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len, | |
58 | * but those are all multiples of 4 long) all goes away, but we | |
59 | * want the *end* of it, which is going to be the start of the IP | |
60 | * header, to be aligned when it gets pulled in. | |
61 | * The beginning of the skb->data is aligned on at least a 4-byte | |
62 | * boundary after allocation. Everything here is aligned at least | |
63 | * on a 2-byte boundary so we can just take hdrlen & 3 and pad by | |
64 | * the result. | |
65 | */ | |
66 | skb_reserve(skb, hdrlen & 3); | |
8ca151b5 | 67 | |
8ca151b5 | 68 | /* If frame is small enough to fit in skb->head, pull it completely. |
3771a890 JB |
69 | * If not, only pull ieee80211_hdr (including crypto if present, and |
70 | * an additional 8 bytes for SNAP/ethertype, see below) so that | |
71 | * splice() or TCP coalesce are more efficient. | |
72 | * | |
73 | * Since, in addition, ieee80211_data_to_8023() always pull in at | |
74 | * least 8 bytes (possibly more for mesh) we can do the same here | |
75 | * to save the cost of doing it later. That still doesn't pull in | |
76 | * the actual IP header since the typical case has a SNAP header. | |
77 | * If the latter changes (there are efforts in the standards group | |
78 | * to do so) we should revisit this and ieee80211_data_to_8023(). | |
8ca151b5 | 79 | */ |
5cddd05c | 80 | hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8; |
8ca151b5 | 81 | |
59ae1d12 | 82 | skb_put_data(skb, hdr, hdrlen); |
8ca151b5 JB |
83 | fraglen = len - hdrlen; |
84 | ||
85 | if (fraglen) { | |
3827cb59 JB |
86 | int offset = (u8 *)hdr + hdrlen - |
87 | (u8 *)rxb_addr(rxb) + rxb_offset(rxb); | |
8ca151b5 JB |
88 | |
89 | skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, | |
90 | fraglen, rxb->truesize); | |
91 | } | |
92 | ||
43ec72b7 | 93 | ieee80211_rx_napi(mvm->hw, sta, skb, napi); |
8ca151b5 JB |
94 | } |
95 | ||
a2d7b870 AA |
96 | /* |
97 | * iwl_mvm_get_signal_strength - use new rx PHY INFO API | |
17dbe564 AA |
98 | * values are reported by the fw as positive values - need to negate |
99 | * to obtain their dBM. Account for missing antennas by replacing 0 | |
100 | * values by -256dBm: practically 0 power and a non-feasible 8 bit value. | |
a2d7b870 | 101 | */ |
226eb8cd JB |
102 | static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, |
103 | struct iwl_rx_phy_info *phy_info, | |
104 | struct ieee80211_rx_status *rx_status) | |
a2d7b870 | 105 | { |
57b7b345 | 106 | int energy_a, energy_b, max_energy; |
a2d7b870 AA |
107 | u32 val; |
108 | ||
109 | val = | |
110 | le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); | |
17dbe564 AA |
111 | energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> |
112 | IWL_RX_INFO_ENERGY_ANT_A_POS; | |
2cddddc5 | 113 | energy_a = energy_a ? -energy_a : S8_MIN; |
17dbe564 AA |
114 | energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> |
115 | IWL_RX_INFO_ENERGY_ANT_B_POS; | |
2cddddc5 | 116 | energy_b = energy_b ? -energy_b : S8_MIN; |
a2d7b870 | 117 | max_energy = max(energy_a, energy_b); |
a2d7b870 | 118 | |
57b7b345 MK |
119 | IWL_DEBUG_STATS(mvm, "energy In A %d B %d , and max %d\n", |
120 | energy_a, energy_b, max_energy); | |
a2d7b870 | 121 | |
226eb8cd JB |
122 | rx_status->signal = max_energy; |
123 | rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & | |
124 | RX_RES_PHY_FLAGS_ANTENNA) | |
125 | >> RX_RES_PHY_FLAGS_ANTENNA_POS; | |
126 | rx_status->chain_signal[0] = energy_a; | |
127 | rx_status->chain_signal[1] = energy_b; | |
a2d7b870 AA |
128 | } |
129 | ||
8ca151b5 JB |
130 | /* |
131 | * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format | |
132 | * @mvm: the mvm object | |
133 | * @hdr: 80211 header | |
134 | * @stats: status in mac80211's format | |
135 | * @rx_pkt_status: status coming from fw | |
136 | * | |
137 | * returns non 0 value if the packet should be dropped | |
138 | */ | |
139 | static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, | |
140 | struct ieee80211_hdr *hdr, | |
141 | struct ieee80211_rx_status *stats, | |
eb96ccb1 JB |
142 | u32 rx_pkt_status, |
143 | u8 *crypt_len) | |
8ca151b5 JB |
144 | { |
145 | if (!ieee80211_has_protected(hdr->frame_control) || | |
146 | (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == | |
147 | RX_MPDU_RES_STATUS_SEC_NO_ENC) | |
148 | return 0; | |
149 | ||
150 | /* packet was encrypted with unknown alg */ | |
151 | if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == | |
152 | RX_MPDU_RES_STATUS_SEC_ENC_ERR) | |
153 | return 0; | |
154 | ||
155 | switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { | |
156 | case RX_MPDU_RES_STATUS_SEC_CCM_ENC: | |
157 | /* alg is CCM: check MIC only */ | |
158 | if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) | |
159 | return -1; | |
160 | ||
161 | stats->flag |= RX_FLAG_DECRYPTED; | |
eb96ccb1 | 162 | *crypt_len = IEEE80211_CCMP_HDR_LEN; |
8ca151b5 JB |
163 | return 0; |
164 | ||
165 | case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: | |
166 | /* Don't drop the frame and decrypt it in SW */ | |
57df3839 SS |
167 | if (!fw_has_api(&mvm->fw->ucode_capa, |
168 | IWL_UCODE_TLV_API_DEPRECATE_TTAK) && | |
169 | !(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) | |
8ca151b5 | 170 | return 0; |
eb96ccb1 | 171 | *crypt_len = IEEE80211_TKIP_IV_LEN; |
5a2abdca | 172 | fallthrough; |
8ca151b5 JB |
173 | |
174 | case RX_MPDU_RES_STATUS_SEC_WEP_ENC: | |
175 | if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) | |
176 | return -1; | |
177 | ||
178 | stats->flag |= RX_FLAG_DECRYPTED; | |
eb96ccb1 JB |
179 | if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == |
180 | RX_MPDU_RES_STATUS_SEC_WEP_ENC) | |
181 | *crypt_len = IEEE80211_WEP_IV_LEN; | |
8ca151b5 JB |
182 | return 0; |
183 | ||
e36e5433 MS |
184 | case RX_MPDU_RES_STATUS_SEC_EXT_ENC: |
185 | if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) | |
186 | return -1; | |
187 | stats->flag |= RX_FLAG_DECRYPTED; | |
188 | return 0; | |
189 | ||
8ca151b5 | 190 | default: |
baf41bc3 ST |
191 | /* Expected in monitor (not having the keys) */ |
192 | if (!mvm->monitor_on) | |
876882b5 | 193 | IWL_WARN(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); |
8ca151b5 JB |
194 | } |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
7d9d0d56 LC |
199 | static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm, |
200 | struct ieee80211_sta *sta, | |
201 | struct ieee80211_hdr *hdr, u32 len, | |
202 | struct iwl_rx_phy_info *phy_info, | |
203 | u32 rate_n_flags) | |
204 | { | |
205 | struct iwl_mvm_sta *mvmsta; | |
206 | struct iwl_mvm_tcm_mac *mdata; | |
207 | int mac; | |
208 | int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */ | |
b0ffe455 JB |
209 | struct iwl_mvm_vif *mvmvif; |
210 | /* expected throughput in 100Kbps, single stream, 20 MHz */ | |
211 | static const u8 thresh_tpt[] = { | |
212 | 9, 18, 30, 42, 60, 78, 90, 96, 120, 135, | |
213 | }; | |
214 | u16 thr; | |
7d9d0d56 | 215 | |
aedb2b38 A |
216 | if (ieee80211_is_data_qos(hdr->frame_control)) { |
217 | u8 tid = ieee80211_get_tid(hdr); | |
218 | ||
219 | if (tid < IWL_MAX_TID_COUNT) | |
220 | ac = tid_to_mac80211_ac[tid]; | |
221 | } | |
7d9d0d56 LC |
222 | |
223 | mvmsta = iwl_mvm_sta_from_mac80211(sta); | |
224 | mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; | |
225 | ||
226 | if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) | |
227 | schedule_delayed_work(&mvm->tcm.work, 0); | |
228 | mdata = &mvm->tcm.data[mac]; | |
229 | mdata->rx.pkts[ac]++; | |
230 | ||
231 | /* count the airtime only once for each ampdu */ | |
232 | if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) { | |
233 | mdata->rx.last_ampdu_ref = mvm->ampdu_ref; | |
234 | mdata->rx.airtime += le16_to_cpu(phy_info->frame_time); | |
235 | } | |
236 | ||
48c6ebc1 | 237 | if (!(rate_n_flags & (RATE_MCS_HT_MSK_V1 | RATE_MCS_VHT_MSK_V1))) |
7d9d0d56 LC |
238 | return; |
239 | ||
b0ffe455 JB |
240 | mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); |
241 | ||
242 | if (mdata->opened_rx_ba_sessions || | |
243 | mdata->uapsd_nonagg_detect.detected || | |
650cadb7 GG |
244 | (!mvmvif->deflink.queue_params[IEEE80211_AC_VO].uapsd && |
245 | !mvmvif->deflink.queue_params[IEEE80211_AC_VI].uapsd && | |
246 | !mvmvif->deflink.queue_params[IEEE80211_AC_BE].uapsd && | |
247 | !mvmvif->deflink.queue_params[IEEE80211_AC_BK].uapsd) || | |
c8ee33e1 | 248 | mvmsta->deflink.sta_id != mvmvif->deflink.ap_sta_id) |
b0ffe455 JB |
249 | return; |
250 | ||
48c6ebc1 MK |
251 | if (rate_n_flags & RATE_MCS_HT_MSK_V1) { |
252 | thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK_V1]; | |
253 | thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK_V1) >> | |
254 | RATE_HT_MCS_NSS_POS_V1); | |
b0ffe455 JB |
255 | } else { |
256 | if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >= | |
257 | ARRAY_SIZE(thresh_tpt))) | |
258 | return; | |
259 | thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK]; | |
774302d2 | 260 | thr *= 1 + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags); |
b0ffe455 JB |
261 | } |
262 | ||
48c6ebc1 | 263 | thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK_V1) >> |
b0ffe455 JB |
264 | RATE_MCS_CHAN_WIDTH_POS); |
265 | ||
266 | mdata->uapsd_nonagg_detect.rx_bytes += len; | |
267 | ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr); | |
7d9d0d56 LC |
268 | } |
269 | ||
93190fb0 AA |
270 | static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, |
271 | struct sk_buff *skb, | |
272 | u32 status) | |
273 | { | |
274 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | |
275 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); | |
276 | ||
277 | if (mvmvif->features & NETIF_F_RXCSUM && | |
278 | status & RX_MPDU_RES_STATUS_CSUM_DONE && | |
279 | status & RX_MPDU_RES_STATUS_CSUM_OK) | |
280 | skb->ip_summed = CHECKSUM_UNNECESSARY; | |
281 | } | |
282 | ||
8ca151b5 JB |
283 | /* |
284 | * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler | |
285 | * | |
286 | * Handles the actual data of the Rx packet from the fw | |
287 | */ | |
1be5d8cc JB |
288 | void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, |
289 | struct iwl_rx_cmd_buffer *rxb) | |
8ca151b5 JB |
290 | { |
291 | struct ieee80211_hdr *hdr; | |
d0963b5d | 292 | struct ieee80211_rx_status *rx_status; |
8ca151b5 JB |
293 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
294 | struct iwl_rx_phy_info *phy_info; | |
295 | struct iwl_rx_mpdu_res_start *rx_res; | |
a399f980 | 296 | struct ieee80211_sta *sta = NULL; |
d0963b5d | 297 | struct sk_buff *skb; |
efc0ec5a | 298 | u32 len, pkt_len = iwl_rx_packet_payload_len(pkt); |
8ca151b5 JB |
299 | u32 rate_n_flags; |
300 | u32 rx_pkt_status; | |
eb96ccb1 | 301 | u8 crypt_len = 0; |
8ca151b5 | 302 | |
b8aba27c JB |
303 | if (unlikely(pkt_len < sizeof(*rx_res))) { |
304 | IWL_DEBUG_DROP(mvm, "Bad REPLY_RX_MPDU_CMD size\n"); | |
305 | return; | |
306 | } | |
307 | ||
8ca151b5 JB |
308 | phy_info = &mvm->last_phy_info; |
309 | rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; | |
310 | hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); | |
311 | len = le16_to_cpu(rx_res->byte_count); | |
efc0ec5a JB |
312 | |
313 | if (unlikely(len + sizeof(*rx_res) + sizeof(__le32) > pkt_len)) { | |
314 | IWL_DEBUG_DROP(mvm, "FW lied about packet len\n"); | |
315 | return; | |
316 | } | |
317 | ||
c5aaa8be | 318 | rx_pkt_status = get_unaligned_le32((__le32 *) |
8ca151b5 JB |
319 | (pkt->data + sizeof(*rx_res) + len)); |
320 | ||
d0963b5d JB |
321 | /* Dont use dev_alloc_skb(), we'll have enough headroom once |
322 | * ieee80211_hdr pulled. | |
323 | */ | |
324 | skb = alloc_skb(128, GFP_ATOMIC); | |
325 | if (!skb) { | |
326 | IWL_ERR(mvm, "alloc_skb failed\n"); | |
0416841d | 327 | return; |
d0963b5d JB |
328 | } |
329 | ||
330 | rx_status = IEEE80211_SKB_RXCB(skb); | |
8ca151b5 | 331 | |
fb8b8ee1 JB |
332 | /* |
333 | * Keep packets with CRC errors (and with overrun) for monitor mode | |
334 | * (otherwise the firmware discards them) but mark them as bad. | |
335 | */ | |
8ca151b5 JB |
336 | if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || |
337 | !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { | |
338 | IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); | |
d0963b5d | 339 | rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; |
8ca151b5 JB |
340 | } |
341 | ||
342 | /* This will be used in several places later */ | |
343 | rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); | |
344 | ||
345 | /* rx_status carries information about the packet to mac80211 */ | |
d0963b5d JB |
346 | rx_status->mactime = le64_to_cpu(phy_info->timestamp); |
347 | rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp); | |
348 | rx_status->band = | |
8ca151b5 | 349 | (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? |
57fbcce3 | 350 | NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; |
d0963b5d | 351 | rx_status->freq = |
8ca151b5 | 352 | ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), |
d0963b5d | 353 | rx_status->band); |
77fe7395 SS |
354 | |
355 | /* TSF as indicated by the firmware is at INA time */ | |
356 | rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; | |
8ca151b5 | 357 | |
d0963b5d | 358 | iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); |
8ca151b5 | 359 | |
d0963b5d JB |
360 | IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, |
361 | (unsigned long long)rx_status->mactime); | |
8ca151b5 | 362 | |
361dbec8 | 363 | rcu_read_lock(); |
a399f980 JB |
364 | if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) { |
365 | u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK; | |
366 | ||
367 | id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT; | |
368 | ||
be9ae34e | 369 | if (!WARN_ON_ONCE(id >= mvm->fw->ucode_capa.num_stations)) { |
a399f980 JB |
370 | sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); |
371 | if (IS_ERR(sta)) | |
372 | sta = NULL; | |
373 | } | |
374 | } else if (!is_multicast_ether_addr(hdr->addr2)) { | |
375 | /* This is fine since we prevent two stations with the same | |
376 | * address from being added. | |
377 | */ | |
378 | sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); | |
361dbec8 ES |
379 | } |
380 | ||
147eb05f EG |
381 | if (sta) { |
382 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | |
383 | struct ieee80211_vif *vif = mvmsta->vif; | |
384 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
385 | ||
386 | /* | |
387 | * Don't even try to decrypt a MCAST frame that was received | |
388 | * before the managed vif is authorized, we'd fail anyway. | |
389 | */ | |
8e5a2636 AB |
390 | if (is_multicast_ether_addr(hdr->addr1) && |
391 | vif->type == NL80211_IFTYPE_STATION && | |
147eb05f | 392 | !mvmvif->authorized && |
8e5a2636 | 393 | ieee80211_has_protected(hdr->frame_control)) { |
147eb05f EG |
394 | IWL_DEBUG_DROP(mvm, "MCAST before the vif is authorized\n"); |
395 | kfree_skb(skb); | |
396 | rcu_read_unlock(); | |
397 | return; | |
398 | } | |
399 | } | |
400 | ||
401 | /* | |
402 | * drop the packet if it has failed being decrypted by HW | |
403 | */ | |
404 | if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, | |
405 | &crypt_len)) { | |
406 | IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", | |
407 | rx_pkt_status); | |
408 | kfree_skb(skb); | |
409 | rcu_read_unlock(); | |
410 | return; | |
411 | } | |
412 | ||
361dbec8 | 413 | if (sta) { |
7c0ebd78 | 414 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
d3a108a4 AO |
415 | struct ieee80211_vif *tx_blocked_vif = |
416 | rcu_dereference(mvm->csa_tx_blocked_vif); | |
6c042d75 SS |
417 | struct iwl_fw_dbg_trigger_tlv *trig; |
418 | struct ieee80211_vif *vif = mvmsta->vif; | |
7c0ebd78 | 419 | |
a399f980 JB |
420 | /* We have tx blocked stations (with CS bit). If we heard |
421 | * frames from a blocked station on a new channel we can | |
422 | * TX to it again. | |
423 | */ | |
6c042d75 | 424 | if (unlikely(tx_blocked_vif) && vif == tx_blocked_vif) { |
d3a108a4 AO |
425 | struct iwl_mvm_vif *mvmvif = |
426 | iwl_mvm_vif_from_mac80211(tx_blocked_vif); | |
427 | ||
428 | if (mvmvif->csa_target_freq == rx_status->freq) | |
429 | iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, | |
430 | false); | |
431 | } | |
a399f980 | 432 | |
ecaf71de | 433 | rs_update_last_rssi(mvm, mvmsta, rx_status); |
3ec50b5e | 434 | |
6c042d75 SS |
435 | trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, |
436 | ieee80211_vif_to_wdev(vif), | |
437 | FW_DBG_TRIGGER_RSSI); | |
438 | ||
439 | if (trig && ieee80211_is_beacon(hdr->frame_control)) { | |
3ec50b5e | 440 | struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; |
3ec50b5e EG |
441 | s32 rssi; |
442 | ||
3ec50b5e EG |
443 | rssi_trig = (void *)trig->data; |
444 | rssi = le32_to_cpu(rssi_trig->rssi); | |
445 | ||
6c042d75 | 446 | if (rx_status->signal < rssi) |
7174beb6 JB |
447 | iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, |
448 | NULL); | |
3ec50b5e | 449 | } |
93190fb0 | 450 | |
7d9d0d56 LC |
451 | if (!mvm->tcm.paused && len >= sizeof(*hdr) && |
452 | !is_multicast_ether_addr(hdr->addr1) && | |
453 | ieee80211_is_data(hdr->frame_control)) | |
454 | iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info, | |
455 | rate_n_flags); | |
456 | ||
a399f980 JB |
457 | if (ieee80211_is_data(hdr->frame_control)) |
458 | iwl_mvm_rx_csum(sta, skb, rx_pkt_status); | |
459 | } | |
361dbec8 ES |
460 | rcu_read_unlock(); |
461 | ||
8ca151b5 JB |
462 | /* set the preamble flag if appropriate */ |
463 | if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) | |
7fdd69c5 | 464 | rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; |
8ca151b5 JB |
465 | |
466 | if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { | |
467 | /* | |
468 | * We know which subframes of an A-MPDU belong | |
469 | * together since we get a single PHY response | |
470 | * from the firmware for all of them | |
471 | */ | |
d0963b5d JB |
472 | rx_status->flag |= RX_FLAG_AMPDU_DETAILS; |
473 | rx_status->ampdu_reference = mvm->ampdu_ref; | |
8ca151b5 JB |
474 | } |
475 | ||
476 | /* Set up the HT phy flags */ | |
48c6ebc1 | 477 | switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK_V1) { |
8ca151b5 JB |
478 | case RATE_MCS_CHAN_WIDTH_20: |
479 | break; | |
480 | case RATE_MCS_CHAN_WIDTH_40: | |
da6a4352 | 481 | rx_status->bw = RATE_INFO_BW_40; |
8ca151b5 JB |
482 | break; |
483 | case RATE_MCS_CHAN_WIDTH_80: | |
da6a4352 | 484 | rx_status->bw = RATE_INFO_BW_80; |
8ca151b5 JB |
485 | break; |
486 | case RATE_MCS_CHAN_WIDTH_160: | |
da6a4352 | 487 | rx_status->bw = RATE_INFO_BW_160; |
8ca151b5 JB |
488 | break; |
489 | } | |
48c6ebc1 MK |
490 | if (!(rate_n_flags & RATE_MCS_CCK_MSK_V1) && |
491 | rate_n_flags & RATE_MCS_SGI_MSK_V1) | |
7fdd69c5 | 492 | rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; |
8ca151b5 | 493 | if (rate_n_flags & RATE_HT_MCS_GF_MSK) |
7fdd69c5 | 494 | rx_status->enc_flags |= RX_ENC_FLAG_HT_GF; |
48c6ebc1 | 495 | if (rate_n_flags & RATE_MCS_LDPC_MSK_V1) |
7fdd69c5 | 496 | rx_status->enc_flags |= RX_ENC_FLAG_LDPC; |
48c6ebc1 | 497 | if (rate_n_flags & RATE_MCS_HT_MSK_V1) { |
77e40945 | 498 | u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> |
7b1dd048 | 499 | RATE_MCS_STBC_POS; |
da6a4352 | 500 | rx_status->encoding = RX_ENC_HT; |
48c6ebc1 | 501 | rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK_V1; |
7fdd69c5 | 502 | rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; |
48c6ebc1 | 503 | } else if (rate_n_flags & RATE_MCS_VHT_MSK_V1) { |
77e40945 | 504 | u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> |
7b1dd048 | 505 | RATE_MCS_STBC_POS; |
8613c948 | 506 | rx_status->nss = |
774302d2 | 507 | FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1; |
d0963b5d | 508 | rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; |
da6a4352 | 509 | rx_status->encoding = RX_ENC_VHT; |
7fdd69c5 | 510 | rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; |
95e05ab7 | 511 | if (rate_n_flags & RATE_MCS_BF_MSK) |
7fdd69c5 | 512 | rx_status->enc_flags |= RX_ENC_FLAG_BF; |
8ca151b5 | 513 | } else { |
cb2de6bb SS |
514 | int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, |
515 | rx_status->band); | |
516 | ||
517 | if (WARN(rate < 0 || rate > 0xFF, | |
518 | "Invalid rate flags 0x%x, band %d,\n", | |
519 | rate_n_flags, rx_status->band)) { | |
520 | kfree_skb(skb); | |
521 | return; | |
522 | } | |
523 | rx_status->rate_idx = rate; | |
8ca151b5 JB |
524 | } |
525 | ||
5fc0f76c | 526 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
2f15a829 | 527 | iwl_mvm_update_frame_stats(mvm, rate_n_flags, |
d0963b5d | 528 | rx_status->flag & RX_FLAG_AMPDU_DETAILS); |
5fc0f76c | 529 | #endif |
a339e918 LC |
530 | |
531 | if (unlikely((ieee80211_is_beacon(hdr->frame_control) || | |
532 | ieee80211_is_probe_resp(hdr->frame_control)) && | |
533 | mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)) | |
534 | mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND; | |
535 | ||
43ec72b7 JB |
536 | if (unlikely(ieee80211_is_beacon(hdr->frame_control) || |
537 | ieee80211_is_probe_resp(hdr->frame_control))) | |
9285ec4c | 538 | rx_status->boottime_ns = ktime_get_boottime_ns(); |
43ec72b7 JB |
539 | |
540 | iwl_mvm_pass_packet_to_mac80211(mvm, sta, napi, skb, hdr, len, | |
bdbc58ab | 541 | crypt_len, rxb); |
8ca151b5 | 542 | } |
9ee718aa | 543 | |
a20fd398 | 544 | struct iwl_mvm_stat_data { |
a20fd398 | 545 | struct iwl_mvm *mvm; |
290d5e49 | 546 | __le32 flags; |
777c9b6b | 547 | __le32 mac_id; |
62e004fe | 548 | u8 beacon_filter_average_energy; |
853f4954 MG |
549 | __le32 *beacon_counter; |
550 | u8 *beacon_average_energy; | |
a20fd398 AO |
551 | }; |
552 | ||
6324c173 MG |
553 | struct iwl_mvm_stat_data_all_macs { |
554 | struct iwl_mvm *mvm; | |
555 | __le32 flags; | |
b6e3d1ba | 556 | struct iwl_stats_ntfy_per_mac *per_mac; |
6324c173 MG |
557 | }; |
558 | ||
30ce0390 | 559 | static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig, |
28e02bc9 JB |
560 | struct iwl_mvm_vif_link_info *link_info, |
561 | struct ieee80211_bss_conf *bss_conf) | |
a20fd398 | 562 | { |
c1e458b9 | 563 | struct iwl_mvm *mvm = iwl_mvm_vif_from_mac80211(vif)->mvm; |
c1e458b9 IP |
564 | int thold = bss_conf->cqm_rssi_thold; |
565 | int hyst = bss_conf->cqm_rssi_hyst; | |
6324c173 | 566 | int last_event; |
30ce0390 | 567 | s8 exit_esr_thresh; |
a20fd398 | 568 | |
8047cc0c AB |
569 | if (sig == 0) { |
570 | IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n"); | |
571 | return; | |
572 | } | |
573 | ||
c1e458b9 | 574 | link_info->bf_data.ave_beacon_signal = sig; |
a20fd398 | 575 | |
911222b5 | 576 | /* BT Coex */ |
c1e458b9 IP |
577 | if (link_info->bf_data.bt_coex_min_thold != |
578 | link_info->bf_data.bt_coex_max_thold) { | |
579 | last_event = link_info->bf_data.last_bt_coex_event; | |
580 | if (sig > link_info->bf_data.bt_coex_max_thold && | |
581 | (last_event <= link_info->bf_data.bt_coex_min_thold || | |
911222b5 | 582 | last_event == 0)) { |
c1e458b9 | 583 | link_info->bf_data.last_bt_coex_event = sig; |
911222b5 AO |
584 | IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", |
585 | sig); | |
586 | iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); | |
c1e458b9 IP |
587 | } else if (sig < link_info->bf_data.bt_coex_min_thold && |
588 | (last_event >= link_info->bf_data.bt_coex_max_thold || | |
911222b5 | 589 | last_event == 0)) { |
c1e458b9 | 590 | link_info->bf_data.last_bt_coex_event = sig; |
911222b5 AO |
591 | IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", |
592 | sig); | |
593 | iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); | |
594 | } | |
595 | } | |
596 | ||
a20fd398 AO |
597 | if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) |
598 | return; | |
599 | ||
600 | /* CQM Notification */ | |
c1e458b9 | 601 | last_event = link_info->bf_data.last_cqm_event; |
a20fd398 AO |
602 | if (thold && sig < thold && (last_event == 0 || |
603 | sig < last_event - hyst)) { | |
c1e458b9 | 604 | link_info->bf_data.last_cqm_event = sig; |
a20fd398 AO |
605 | IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", |
606 | sig); | |
607 | ieee80211_cqm_rssi_notify( | |
608 | vif, | |
609 | NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, | |
769f07d8 | 610 | sig, |
a20fd398 AO |
611 | GFP_KERNEL); |
612 | } else if (sig > thold && | |
613 | (last_event == 0 || sig > last_event + hyst)) { | |
c1e458b9 | 614 | link_info->bf_data.last_cqm_event = sig; |
a20fd398 AO |
615 | IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", |
616 | sig); | |
617 | ieee80211_cqm_rssi_notify( | |
618 | vif, | |
619 | NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, | |
769f07d8 | 620 | sig, |
a20fd398 AO |
621 | GFP_KERNEL); |
622 | } | |
30ce0390 MK |
623 | |
624 | /* ESR recalculation */ | |
625 | if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) | |
626 | return; | |
627 | ||
628 | exit_esr_thresh = | |
629 | iwl_mvm_get_esr_rssi_thresh(mvm, | |
630 | &bss_conf->chanreq.oper, | |
631 | true); | |
632 | ||
633 | if (sig < exit_esr_thresh) | |
634 | iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_LOW_RSSI, | |
635 | iwl_mvm_get_other_link(vif, | |
636 | bss_conf->link_id)); | |
a20fd398 AO |
637 | } |
638 | ||
6324c173 MG |
639 | static void iwl_mvm_stat_iterator(void *_data, u8 *mac, |
640 | struct ieee80211_vif *vif) | |
641 | { | |
642 | struct iwl_mvm_stat_data *data = _data; | |
643 | int sig = -data->beacon_filter_average_energy; | |
644 | u16 id = le32_to_cpu(data->mac_id); | |
645 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
646 | u16 vif_id = mvmvif->id; | |
647 | ||
648 | /* This doesn't need the MAC ID check since it's not taking the | |
649 | * data copied into the "data" struct, but rather the data from | |
650 | * the notification directly. | |
651 | */ | |
650cadb7 | 652 | mvmvif->deflink.beacon_stats.num_beacons = |
6324c173 | 653 | le32_to_cpu(data->beacon_counter[vif_id]); |
650cadb7 | 654 | mvmvif->deflink.beacon_stats.avg_signal = |
6324c173 MG |
655 | -data->beacon_average_energy[vif_id]; |
656 | ||
657 | if (mvmvif->id != id) | |
658 | return; | |
659 | ||
660 | if (vif->type != NL80211_IFTYPE_STATION) | |
661 | return; | |
662 | ||
663 | /* make sure that beacon statistics don't go backwards with TCM | |
664 | * request to clear statistics | |
665 | */ | |
666 | if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) | |
650cadb7 GG |
667 | mvmvif->deflink.beacon_stats.accu_num_beacons += |
668 | mvmvif->deflink.beacon_stats.num_beacons; | |
6324c173 | 669 | |
c1e458b9 | 670 | /* This is used in pre-MLO API so use deflink */ |
28e02bc9 | 671 | iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink, &vif->bss_conf); |
6324c173 MG |
672 | } |
673 | ||
674 | static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac, | |
675 | struct ieee80211_vif *vif) | |
676 | { | |
677 | struct iwl_mvm_stat_data_all_macs *data = _data; | |
b6e3d1ba | 678 | struct iwl_stats_ntfy_per_mac *mac_stats; |
6324c173 MG |
679 | int sig; |
680 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
681 | u16 vif_id = mvmvif->id; | |
682 | ||
f1cbb0a8 | 683 | if (WARN_ONCE(vif_id >= MAC_INDEX_AUX, "invalid vif id: %d", vif_id)) |
6324c173 MG |
684 | return; |
685 | ||
686 | if (vif->type != NL80211_IFTYPE_STATION) | |
687 | return; | |
688 | ||
b6e3d1ba | 689 | mac_stats = &data->per_mac[vif_id]; |
6324c173 | 690 | |
650cadb7 | 691 | mvmvif->deflink.beacon_stats.num_beacons = |
6324c173 | 692 | le32_to_cpu(mac_stats->beacon_counter); |
650cadb7 | 693 | mvmvif->deflink.beacon_stats.avg_signal = |
6324c173 MG |
694 | -le32_to_cpu(mac_stats->beacon_average_energy); |
695 | ||
696 | /* make sure that beacon statistics don't go backwards with TCM | |
697 | * request to clear statistics | |
698 | */ | |
699 | if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) | |
650cadb7 GG |
700 | mvmvif->deflink.beacon_stats.accu_num_beacons += |
701 | mvmvif->deflink.beacon_stats.num_beacons; | |
6324c173 MG |
702 | |
703 | sig = -le32_to_cpu(mac_stats->beacon_filter_average_energy); | |
c1e458b9 IP |
704 | |
705 | /* This is used in pre-MLO API so use deflink */ | |
28e02bc9 | 706 | iwl_mvm_update_link_sig(vif, sig, &mvmvif->deflink, &vif->bss_conf); |
6324c173 MG |
707 | } |
708 | ||
5a756c20 EG |
709 | static inline void |
710 | iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) | |
711 | { | |
712 | struct iwl_fw_dbg_trigger_tlv *trig; | |
713 | struct iwl_fw_dbg_trigger_stats *trig_stats; | |
714 | u32 trig_offset, trig_thold; | |
715 | ||
6c042d75 SS |
716 | trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, NULL, FW_DBG_TRIGGER_STATS); |
717 | if (!trig) | |
5a756c20 EG |
718 | return; |
719 | ||
5a756c20 EG |
720 | trig_stats = (void *)trig->data; |
721 | ||
5a756c20 EG |
722 | trig_offset = le32_to_cpu(trig_stats->stop_offset); |
723 | trig_thold = le32_to_cpu(trig_stats->stop_threshold); | |
724 | ||
725 | if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt))) | |
726 | return; | |
727 | ||
728 | if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) | |
729 | return; | |
730 | ||
7174beb6 | 731 | iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, NULL); |
5a756c20 EG |
732 | } |
733 | ||
c6bae216 MG |
734 | static void iwl_mvm_stats_energy_iter(void *_data, |
735 | struct ieee80211_sta *sta) | |
853f4954 | 736 | { |
c6bae216 MG |
737 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); |
738 | u8 *energy = _data; | |
c8ee33e1 | 739 | u32 sta_id = mvmsta->deflink.sta_id; |
853f4954 | 740 | |
c6bae216 MG |
741 | if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT_MAX, "sta_id %d >= %d", |
742 | sta_id, IWL_MVM_STATION_COUNT_MAX)) | |
853f4954 MG |
743 | return; |
744 | ||
c6bae216 | 745 | if (energy[sta_id]) |
c8ee33e1 | 746 | mvmsta->deflink.avg_energy = energy[sta_id]; |
853f4954 | 747 | |
853f4954 MG |
748 | } |
749 | ||
750 | static void | |
751 | iwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le, | |
752 | __le32 *rx_bytes_le) | |
753 | { | |
754 | int i; | |
755 | ||
756 | spin_lock(&mvm->tcm.lock); | |
757 | for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) { | |
758 | struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i]; | |
759 | u32 rx_bytes = le32_to_cpu(rx_bytes_le[i]); | |
760 | u32 airtime = le32_to_cpu(air_time_le[i]); | |
761 | ||
762 | mdata->rx.airtime += airtime; | |
763 | mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes; | |
764 | if (airtime) { | |
765 | /* re-init every time to store rate from FW */ | |
766 | ewma_rate_init(&mdata->uapsd_nonagg_detect.rate); | |
767 | ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, | |
768 | rx_bytes * 8 / airtime); | |
769 | } | |
770 | } | |
771 | spin_unlock(&mvm->tcm.lock); | |
772 | } | |
773 | ||
740dfecc MK |
774 | static void iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm, |
775 | struct iwl_stats_ntfy_per_phy *per_phy) | |
776 | { | |
777 | int i; | |
778 | ||
779 | for (i = 0; i < NUM_PHY_CTX; i++) { | |
780 | if (!mvm->phy_ctxts[i].ref) | |
781 | continue; | |
782 | mvm->phy_ctxts[i].channel_load_by_us = | |
783 | le32_to_cpu(per_phy[i].channel_load_by_us); | |
784 | } | |
785 | } | |
786 | ||
853f4954 | 787 | static void |
6324c173 MG |
788 | iwl_mvm_stats_ver_15(struct iwl_mvm *mvm, |
789 | struct iwl_statistics_operational_ntfy *stats) | |
790 | { | |
791 | struct iwl_mvm_stat_data_all_macs data = { | |
792 | .mvm = mvm, | |
793 | .flags = stats->flags, | |
b6e3d1ba | 794 | .per_mac = stats->per_mac, |
6324c173 MG |
795 | }; |
796 | ||
797 | ieee80211_iterate_active_interfaces(mvm->hw, | |
798 | IEEE80211_IFACE_ITER_NORMAL, | |
799 | iwl_mvm_stat_iterator_all_macs, | |
800 | &data); | |
740dfecc | 801 | iwl_mvm_handle_per_phy_stats(mvm, stats->per_phy); |
6324c173 MG |
802 | } |
803 | ||
804 | static void | |
805 | iwl_mvm_stats_ver_14(struct iwl_mvm *mvm, | |
806 | struct iwl_statistics_operational_ntfy_ver_14 *stats) | |
853f4954 MG |
807 | { |
808 | struct iwl_mvm_stat_data data = { | |
809 | .mvm = mvm, | |
810 | }; | |
6324c173 | 811 | |
853f4954 | 812 | u8 beacon_average_energy[MAC_INDEX_AUX]; |
853f4954 MG |
813 | __le32 flags; |
814 | int i; | |
815 | ||
853f4954 | 816 | flags = stats->flags; |
853f4954 MG |
817 | |
818 | data.mac_id = stats->mac_id; | |
819 | data.beacon_filter_average_energy = | |
820 | le32_to_cpu(stats->beacon_filter_average_energy); | |
821 | data.flags = flags; | |
822 | data.beacon_counter = stats->beacon_counter; | |
6324c173 | 823 | |
853f4954 MG |
824 | for (i = 0; i < ARRAY_SIZE(beacon_average_energy); i++) |
825 | beacon_average_energy[i] = | |
826 | le32_to_cpu(stats->beacon_average_energy[i]); | |
827 | ||
828 | data.beacon_average_energy = beacon_average_energy; | |
829 | ||
830 | ieee80211_iterate_active_interfaces(mvm->hw, | |
831 | IEEE80211_IFACE_ITER_NORMAL, | |
832 | iwl_mvm_stat_iterator, | |
833 | &data); | |
6324c173 MG |
834 | } |
835 | ||
836 | static bool iwl_mvm_verify_stats_len(struct iwl_mvm *mvm, | |
837 | struct iwl_rx_packet *pkt, | |
838 | u32 expected_size) | |
839 | { | |
840 | struct iwl_statistics_ntfy_hdr *hdr; | |
841 | ||
842 | if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) < expected_size, | |
843 | "received invalid statistics size (%d)!, expected_size: %d\n", | |
844 | iwl_rx_packet_payload_len(pkt), expected_size)) | |
845 | return false; | |
846 | ||
847 | hdr = (void *)&pkt->data; | |
848 | ||
849 | if (WARN_ONCE((hdr->type & IWL_STATISTICS_TYPE_MSK) != FW_STATISTICS_OPERATIONAL || | |
850 | hdr->version != | |
851 | iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, STATISTICS_NOTIFICATION, 0), | |
852 | "received unsupported hdr type %d, version %d\n", | |
853 | hdr->type, hdr->version)) | |
854 | return false; | |
855 | ||
856 | if (WARN_ONCE(le16_to_cpu(hdr->size) != expected_size, | |
857 | "received invalid statistics size in header (%d)!, expected_size: %d\n", | |
858 | le16_to_cpu(hdr->size), expected_size)) | |
859 | return false; | |
860 | ||
861 | return true; | |
862 | } | |
863 | ||
b6e3d1ba A |
864 | static void |
865 | iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm, | |
866 | struct iwl_stats_ntfy_per_link *per_link) | |
867 | { | |
868 | u32 air_time[MAC_INDEX_AUX] = {}; | |
869 | u32 rx_bytes[MAC_INDEX_AUX] = {}; | |
870 | int fw_link_id; | |
871 | ||
872 | for (fw_link_id = 0; fw_link_id < ARRAY_SIZE(mvm->link_id_to_link_conf); | |
873 | fw_link_id++) { | |
874 | struct iwl_stats_ntfy_per_link *link_stats; | |
875 | struct ieee80211_bss_conf *bss_conf; | |
876 | struct iwl_mvm_vif *mvmvif; | |
10159a45 | 877 | struct iwl_mvm_vif_link_info *link_info; |
b6e3d1ba A |
878 | int link_id; |
879 | int sig; | |
880 | ||
881 | bss_conf = iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, fw_link_id, | |
882 | false); | |
883 | if (!bss_conf) | |
884 | continue; | |
885 | ||
886 | if (bss_conf->vif->type != NL80211_IFTYPE_STATION) | |
887 | continue; | |
888 | ||
889 | link_id = bss_conf->link_id; | |
890 | if (link_id >= ARRAY_SIZE(mvmvif->link)) | |
891 | continue; | |
892 | ||
893 | mvmvif = iwl_mvm_vif_from_mac80211(bss_conf->vif); | |
10159a45 MK |
894 | link_info = mvmvif->link[link_id]; |
895 | if (!link_info) | |
b6e3d1ba A |
896 | continue; |
897 | ||
898 | link_stats = &per_link[fw_link_id]; | |
899 | ||
10159a45 | 900 | link_info->beacon_stats.num_beacons = |
b6e3d1ba A |
901 | le32_to_cpu(link_stats->beacon_counter); |
902 | ||
903 | /* we basically just use the u8 to store 8 bits and then treat | |
904 | * it as a s8 whenever we take it out to a different type. | |
905 | */ | |
10159a45 | 906 | link_info->beacon_stats.avg_signal = |
b6e3d1ba A |
907 | -le32_to_cpu(link_stats->beacon_average_energy); |
908 | ||
10159a45 MK |
909 | if (link_info->phy_ctxt && |
910 | link_info->phy_ctxt->channel->band == NL80211_BAND_2GHZ) | |
9c692112 MK |
911 | iwl_mvm_bt_coex_update_link_esr(mvm, bss_conf->vif, |
912 | link_id); | |
10159a45 | 913 | |
b6e3d1ba A |
914 | /* make sure that beacon statistics don't go backwards with TCM |
915 | * request to clear statistics | |
916 | */ | |
917 | if (mvm->statistics_clear) | |
918 | mvmvif->link[link_id]->beacon_stats.accu_num_beacons += | |
919 | mvmvif->link[link_id]->beacon_stats.num_beacons; | |
920 | ||
921 | sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); | |
28e02bc9 JB |
922 | iwl_mvm_update_link_sig(bss_conf->vif, sig, link_info, |
923 | bss_conf); | |
b6e3d1ba A |
924 | |
925 | if (WARN_ONCE(mvmvif->id >= MAC_INDEX_AUX, | |
926 | "invalid mvmvif id: %d", mvmvif->id)) | |
927 | continue; | |
928 | ||
929 | air_time[mvmvif->id] += | |
930 | le32_to_cpu(per_link[fw_link_id].air_time); | |
931 | rx_bytes[mvmvif->id] += | |
932 | le32_to_cpu(per_link[fw_link_id].rx_bytes); | |
933 | } | |
934 | ||
935 | /* Don't update in case the statistics are not cleared, since | |
936 | * we will end up counting twice the same airtime, once in TCM | |
937 | * request and once in statistics notification. | |
938 | */ | |
939 | if (mvm->statistics_clear) { | |
940 | __le32 air_time_le[MAC_INDEX_AUX]; | |
941 | __le32 rx_bytes_le[MAC_INDEX_AUX]; | |
942 | int vif_id; | |
943 | ||
944 | for (vif_id = 0; vif_id < ARRAY_SIZE(air_time_le); vif_id++) { | |
945 | air_time_le[vif_id] = cpu_to_le32(air_time[vif_id]); | |
946 | rx_bytes_le[vif_id] = cpu_to_le32(rx_bytes[vif_id]); | |
947 | } | |
948 | ||
949 | iwl_mvm_update_tcm_from_stats(mvm, air_time_le, rx_bytes_le); | |
950 | } | |
951 | } | |
952 | ||
df966c93 MK |
953 | #define SEC_LINK_MIN_PERC 10 |
954 | #define SEC_LINK_MIN_TX 3000 | |
955 | #define SEC_LINK_MIN_RX 400 | |
956 | ||
ec0d43d2 MK |
957 | static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) |
958 | { | |
959 | struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm); | |
960 | struct iwl_mvm_vif *mvmvif; | |
961 | struct iwl_mvm_sta *mvmsta; | |
962 | unsigned long total_tx = 0, total_rx = 0; | |
df966c93 MK |
963 | unsigned long sec_link_tx = 0, sec_link_rx = 0; |
964 | u8 sec_link_tx_perc, sec_link_rx_perc; | |
965 | u8 sec_link; | |
ec0d43d2 MK |
966 | |
967 | lockdep_assert_held(&mvm->mutex); | |
968 | ||
4130c67c | 969 | if (IS_ERR_OR_NULL(bss_vif)) |
ec0d43d2 MK |
970 | return; |
971 | ||
972 | mvmvif = iwl_mvm_vif_from_mac80211(bss_vif); | |
973 | ||
974 | if (!mvmvif->esr_active || !mvmvif->ap_sta) | |
975 | return; | |
976 | ||
977 | mvmsta = iwl_mvm_sta_from_mac80211(mvmvif->ap_sta); | |
978 | /* We only count for the AP sta in a MLO connection */ | |
979 | if (!mvmsta->mpdu_counters) | |
980 | return; | |
981 | ||
df966c93 MK |
982 | /* Get the FW ID of the secondary link */ |
983 | sec_link = iwl_mvm_get_other_link(bss_vif, | |
984 | iwl_mvm_get_primary_link(bss_vif)); | |
985 | if (WARN_ON(!mvmvif->link[sec_link])) | |
986 | return; | |
987 | sec_link = mvmvif->link[sec_link]->fw_link_id; | |
988 | ||
ec0d43d2 MK |
989 | /* Sum up RX and TX MPDUs from the different queues/links */ |
990 | for (int q = 0; q < mvm->trans->num_rx_queues; q++) { | |
991 | spin_lock_bh(&mvmsta->mpdu_counters[q].lock); | |
992 | ||
993 | /* The link IDs that doesn't exist will contain 0 */ | |
994 | for (int link = 0; link < IWL_MVM_FW_MAX_LINK_ID; link++) { | |
995 | total_tx += mvmsta->mpdu_counters[q].per_link[link].tx; | |
996 | total_rx += mvmsta->mpdu_counters[q].per_link[link].rx; | |
997 | } | |
df966c93 MK |
998 | |
999 | sec_link_tx += mvmsta->mpdu_counters[q].per_link[sec_link].tx; | |
1000 | sec_link_rx += mvmsta->mpdu_counters[q].per_link[sec_link].rx; | |
1001 | ||
ec0d43d2 MK |
1002 | /* |
1003 | * In EMLSR we have statistics every 5 seconds, so we can reset | |
1004 | * the counters upon every statistics notification. | |
1005 | */ | |
1006 | memset(mvmsta->mpdu_counters[q].per_link, 0, | |
1007 | sizeof(mvmsta->mpdu_counters[q].per_link)); | |
1008 | ||
1009 | spin_unlock_bh(&mvmsta->mpdu_counters[q].lock); | |
1010 | } | |
1011 | ||
6958c4be MK |
1012 | IWL_DEBUG_STATS(mvm, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n", |
1013 | total_tx, total_rx); | |
1014 | ||
ec0d43d2 MK |
1015 | /* If we don't have enough MPDUs - exit EMLSR */ |
1016 | if (total_tx < IWL_MVM_ENTER_ESR_TPT_THRESH && | |
df966c93 | 1017 | total_rx < IWL_MVM_ENTER_ESR_TPT_THRESH) { |
ec0d43d2 MK |
1018 | iwl_mvm_block_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_TPT, |
1019 | iwl_mvm_get_primary_link(bss_vif)); | |
df966c93 MK |
1020 | return; |
1021 | } | |
1022 | ||
1023 | /* Calculate the percentage of the secondary link TX/RX */ | |
1024 | sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0; | |
1025 | sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0; | |
1026 | ||
1027 | /* | |
1028 | * The TX/RX percentage is checked only if it exceeds the required | |
1029 | * minimum. In addition, RX is checked only if the TX check failed. | |
1030 | */ | |
1031 | if ((total_tx > SEC_LINK_MIN_TX && | |
1032 | sec_link_tx_perc < SEC_LINK_MIN_PERC) || | |
1033 | (total_rx > SEC_LINK_MIN_RX && | |
1034 | sec_link_rx_perc < SEC_LINK_MIN_PERC)) | |
1035 | iwl_mvm_exit_esr(mvm, bss_vif, IWL_MVM_ESR_EXIT_LINK_USAGE, | |
1036 | iwl_mvm_get_primary_link(bss_vif)); | |
ec0d43d2 MK |
1037 | } |
1038 | ||
b6e3d1ba A |
1039 | void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm, |
1040 | struct iwl_rx_cmd_buffer *rxb) | |
1041 | { | |
1042 | u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; | |
1043 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
1044 | struct iwl_system_statistics_notif_oper *stats; | |
1045 | int i; | |
1046 | u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP, | |
1047 | STATISTICS_OPER_NOTIF, 0); | |
1048 | ||
1049 | if (notif_ver != 3) { | |
1050 | IWL_FW_CHECK_FAILED(mvm, | |
1051 | "Oper stats notif ver %d is not supported\n", | |
1052 | notif_ver); | |
1053 | return; | |
1054 | } | |
1055 | ||
1056 | stats = (void *)&pkt->data; | |
1057 | iwl_mvm_stat_iterator_all_links(mvm, stats->per_link); | |
1058 | ||
1059 | for (i = 0; i < ARRAY_SIZE(average_energy); i++) | |
1060 | average_energy[i] = | |
1061 | le32_to_cpu(stats->per_sta[i].average_energy); | |
1062 | ||
1063 | ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter, | |
1064 | average_energy); | |
740dfecc | 1065 | iwl_mvm_handle_per_phy_stats(mvm, stats->per_phy); |
ec0d43d2 MK |
1066 | |
1067 | iwl_mvm_update_esr_mode_tpt(mvm); | |
b6e3d1ba A |
1068 | } |
1069 | ||
1070 | void iwl_mvm_handle_rx_system_oper_part1_stats(struct iwl_mvm *mvm, | |
1071 | struct iwl_rx_cmd_buffer *rxb) | |
1072 | { | |
1073 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
1074 | struct iwl_system_statistics_part1_notif_oper *part1_stats; | |
1075 | int i; | |
1076 | u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP, | |
1077 | STATISTICS_OPER_PART1_NOTIF, 0); | |
1078 | ||
1079 | if (notif_ver != 4) { | |
1080 | IWL_FW_CHECK_FAILED(mvm, | |
1081 | "Part1 stats notif ver %d is not supported\n", | |
1082 | notif_ver); | |
1083 | return; | |
1084 | } | |
1085 | ||
1086 | part1_stats = (void *)&pkt->data; | |
1087 | mvm->radio_stats.rx_time = 0; | |
1088 | mvm->radio_stats.tx_time = 0; | |
1089 | for (i = 0; i < ARRAY_SIZE(part1_stats->per_link); i++) { | |
1090 | mvm->radio_stats.rx_time += | |
1091 | le64_to_cpu(part1_stats->per_link[i].rx_time); | |
1092 | mvm->radio_stats.tx_time += | |
1093 | le64_to_cpu(part1_stats->per_link[i].tx_time); | |
1094 | } | |
1095 | } | |
1096 | ||
6324c173 MG |
1097 | static void |
1098 | iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm, | |
1099 | struct iwl_rx_packet *pkt) | |
1100 | { | |
1101 | u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; | |
1102 | __le32 air_time[MAC_INDEX_AUX]; | |
1103 | __le32 rx_bytes[MAC_INDEX_AUX]; | |
1104 | __le32 flags = 0; | |
1105 | int i; | |
1106 | u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, | |
1107 | STATISTICS_NOTIFICATION, 0); | |
1108 | ||
1109 | if (WARN_ONCE(notif_ver > 15, | |
1110 | "invalid statistics version id: %d\n", notif_ver)) | |
1111 | return; | |
1112 | ||
1113 | if (notif_ver == 14) { | |
1114 | struct iwl_statistics_operational_ntfy_ver_14 *stats = | |
1115 | (void *)pkt->data; | |
1116 | ||
1117 | if (!iwl_mvm_verify_stats_len(mvm, pkt, sizeof(*stats))) | |
1118 | return; | |
1119 | ||
1120 | iwl_mvm_stats_ver_14(mvm, stats); | |
1121 | ||
1122 | flags = stats->flags; | |
1123 | mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time); | |
1124 | mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time); | |
1125 | mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf); | |
1126 | mvm->radio_stats.on_time_scan = | |
1127 | le64_to_cpu(stats->on_time_scan); | |
1128 | ||
1129 | for (i = 0; i < ARRAY_SIZE(average_energy); i++) | |
1130 | average_energy[i] = le32_to_cpu(stats->average_energy[i]); | |
1131 | ||
1132 | for (i = 0; i < ARRAY_SIZE(air_time); i++) { | |
1133 | air_time[i] = stats->air_time[i]; | |
1134 | rx_bytes[i] = stats->rx_bytes[i]; | |
1135 | } | |
1136 | } | |
1137 | ||
1138 | if (notif_ver == 15) { | |
1139 | struct iwl_statistics_operational_ntfy *stats = | |
1140 | (void *)pkt->data; | |
1141 | ||
1142 | if (!iwl_mvm_verify_stats_len(mvm, pkt, sizeof(*stats))) | |
1143 | return; | |
1144 | ||
1145 | iwl_mvm_stats_ver_15(mvm, stats); | |
1146 | ||
1147 | flags = stats->flags; | |
1148 | mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time); | |
1149 | mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time); | |
1150 | mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf); | |
1151 | mvm->radio_stats.on_time_scan = | |
1152 | le64_to_cpu(stats->on_time_scan); | |
1153 | ||
1154 | for (i = 0; i < ARRAY_SIZE(average_energy); i++) | |
1155 | average_energy[i] = | |
b6e3d1ba | 1156 | le32_to_cpu(stats->per_sta[i].average_energy); |
6324c173 MG |
1157 | |
1158 | for (i = 0; i < ARRAY_SIZE(air_time); i++) { | |
b6e3d1ba A |
1159 | air_time[i] = stats->per_mac[i].air_time; |
1160 | rx_bytes[i] = stats->per_mac[i].rx_bytes; | |
6324c173 MG |
1161 | } |
1162 | } | |
1163 | ||
1164 | iwl_mvm_rx_stats_check_trigger(mvm, pkt); | |
853f4954 | 1165 | |
c6bae216 MG |
1166 | ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter, |
1167 | average_energy); | |
853f4954 MG |
1168 | /* |
1169 | * Don't update in case the statistics are not cleared, since | |
1170 | * we will end up counting twice the same airtime, once in TCM | |
1171 | * request and once in statistics notification. | |
1172 | */ | |
1173 | if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) | |
6324c173 | 1174 | iwl_mvm_update_tcm_from_stats(mvm, air_time, rx_bytes); |
853f4954 MG |
1175 | } |
1176 | ||
91a8bcde JB |
1177 | void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, |
1178 | struct iwl_rx_packet *pkt) | |
9ee718aa | 1179 | { |
a20fd398 | 1180 | struct iwl_mvm_stat_data data = { |
a20fd398 AO |
1181 | .mvm = mvm, |
1182 | }; | |
853f4954 | 1183 | __le32 *bytes, *air_time, flags; |
0e7ac018 | 1184 | int expected_size; |
cfbeb598 | 1185 | u8 *energy; |
b6e3d1ba A |
1186 | u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, |
1187 | WIDE_ID(SYSTEM_GROUP, | |
1188 | SYSTEM_STATISTICS_CMD), | |
1189 | IWL_FW_CMD_VER_UNKNOWN); | |
1190 | ||
1191 | if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) | |
1192 | return; | |
853f4954 MG |
1193 | |
1194 | /* From ver 14 and up we use TLV statistics format */ | |
6324c173 MG |
1195 | if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, |
1196 | STATISTICS_NOTIFICATION, 0) >= 14) | |
853f4954 | 1197 | return iwl_mvm_handle_rx_statistics_tlv(mvm, pkt); |
0e7ac018 | 1198 | |
678d9b6d LK |
1199 | if (!iwl_mvm_has_new_rx_stats_api(mvm)) { |
1200 | if (iwl_mvm_has_new_rx_api(mvm)) | |
1201 | expected_size = sizeof(struct iwl_notif_statistics_v11); | |
1202 | else | |
1203 | expected_size = sizeof(struct iwl_notif_statistics_v10); | |
1204 | } else { | |
606b9ab6 | 1205 | expected_size = sizeof(struct iwl_notif_statistics); |
678d9b6d | 1206 | } |
777c9b6b | 1207 | |
42fa5e0e JB |
1208 | if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) != expected_size, |
1209 | "received invalid statistics size (%d)!\n", | |
1210 | iwl_rx_packet_payload_len(pkt))) | |
6a90f85a | 1211 | return; |
777c9b6b | 1212 | |
678d9b6d LK |
1213 | if (!iwl_mvm_has_new_rx_stats_api(mvm)) { |
1214 | struct iwl_notif_statistics_v11 *stats = (void *)&pkt->data; | |
777c9b6b | 1215 | |
678d9b6d LK |
1216 | data.mac_id = stats->rx.general.mac_id; |
1217 | data.beacon_filter_average_energy = | |
1218 | stats->general.common.beacon_filter_average_energy; | |
777c9b6b | 1219 | |
678d9b6d | 1220 | mvm->rx_stats_v3 = stats->rx; |
91a8bcde | 1221 | |
678d9b6d LK |
1222 | mvm->radio_stats.rx_time = |
1223 | le64_to_cpu(stats->general.common.rx_time); | |
1224 | mvm->radio_stats.tx_time = | |
1225 | le64_to_cpu(stats->general.common.tx_time); | |
1226 | mvm->radio_stats.on_time_rf = | |
1227 | le64_to_cpu(stats->general.common.on_time_rf); | |
1228 | mvm->radio_stats.on_time_scan = | |
1229 | le64_to_cpu(stats->general.common.on_time_scan); | |
1230 | ||
853f4954 MG |
1231 | data.beacon_counter = stats->general.beacon_counter; |
1232 | data.beacon_average_energy = | |
1233 | stats->general.beacon_average_energy; | |
678d9b6d LK |
1234 | flags = stats->flag; |
1235 | } else { | |
606b9ab6 | 1236 | struct iwl_notif_statistics *stats = (void *)&pkt->data; |
678d9b6d LK |
1237 | |
1238 | data.mac_id = stats->rx.general.mac_id; | |
1239 | data.beacon_filter_average_energy = | |
1240 | stats->general.common.beacon_filter_average_energy; | |
1241 | ||
1242 | mvm->rx_stats = stats->rx; | |
1243 | ||
1244 | mvm->radio_stats.rx_time = | |
1245 | le64_to_cpu(stats->general.common.rx_time); | |
1246 | mvm->radio_stats.tx_time = | |
1247 | le64_to_cpu(stats->general.common.tx_time); | |
1248 | mvm->radio_stats.on_time_rf = | |
1249 | le64_to_cpu(stats->general.common.on_time_rf); | |
1250 | mvm->radio_stats.on_time_scan = | |
1251 | le64_to_cpu(stats->general.common.on_time_scan); | |
1252 | ||
853f4954 MG |
1253 | data.beacon_counter = stats->general.beacon_counter; |
1254 | data.beacon_average_energy = | |
1255 | stats->general.beacon_average_energy; | |
678d9b6d LK |
1256 | flags = stats->flag; |
1257 | } | |
290d5e49 | 1258 | data.flags = flags; |
9ee718aa | 1259 | |
5a756c20 EG |
1260 | iwl_mvm_rx_stats_check_trigger(mvm, pkt); |
1261 | ||
a20fd398 AO |
1262 | ieee80211_iterate_active_interfaces(mvm->hw, |
1263 | IEEE80211_IFACE_ITER_NORMAL, | |
1264 | iwl_mvm_stat_iterator, | |
1265 | &data); | |
cfbeb598 GM |
1266 | |
1267 | if (!iwl_mvm_has_new_rx_api(mvm)) | |
1268 | return; | |
1269 | ||
678d9b6d LK |
1270 | if (!iwl_mvm_has_new_rx_stats_api(mvm)) { |
1271 | struct iwl_notif_statistics_v11 *v11 = (void *)&pkt->data; | |
cfbeb598 GM |
1272 | |
1273 | energy = (void *)&v11->load_stats.avg_energy; | |
b0ffe455 | 1274 | bytes = (void *)&v11->load_stats.byte_count; |
cfbeb598 GM |
1275 | air_time = (void *)&v11->load_stats.air_time; |
1276 | } else { | |
606b9ab6 | 1277 | struct iwl_notif_statistics *stats = (void *)&pkt->data; |
678d9b6d | 1278 | |
cfbeb598 | 1279 | energy = (void *)&stats->load_stats.avg_energy; |
b0ffe455 | 1280 | bytes = (void *)&stats->load_stats.byte_count; |
cfbeb598 GM |
1281 | air_time = (void *)&stats->load_stats.air_time; |
1282 | } | |
c6bae216 MG |
1283 | ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter, |
1284 | energy); | |
7d9d0d56 LC |
1285 | |
1286 | /* | |
1287 | * Don't update in case the statistics are not cleared, since | |
1288 | * we will end up counting twice the same airtime, once in TCM | |
1289 | * request and once in statistics notification. | |
1290 | */ | |
853f4954 MG |
1291 | if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) |
1292 | iwl_mvm_update_tcm_from_stats(mvm, air_time, bytes); | |
7d9d0d56 | 1293 | |
91a8bcde JB |
1294 | } |
1295 | ||
0416841d | 1296 | void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) |
91a8bcde JB |
1297 | { |
1298 | iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); | |
9ee718aa | 1299 | } |
3af512d6 SS |
1300 | |
1301 | void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, | |
1302 | struct iwl_rx_cmd_buffer *rxb) | |
1303 | { | |
1304 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
1305 | struct iwl_ba_window_status_notif *notif = (void *)pkt->data; | |
1306 | int i; | |
3af512d6 | 1307 | |
afc857bc JB |
1308 | BUILD_BUG_ON(ARRAY_SIZE(notif->ra_tid) != BA_WINDOW_STREAMS_MAX); |
1309 | BUILD_BUG_ON(ARRAY_SIZE(notif->mpdu_rx_count) != BA_WINDOW_STREAMS_MAX); | |
1310 | BUILD_BUG_ON(ARRAY_SIZE(notif->bitmap) != BA_WINDOW_STREAMS_MAX); | |
1311 | BUILD_BUG_ON(ARRAY_SIZE(notif->start_seq_num) != BA_WINDOW_STREAMS_MAX); | |
3af512d6 SS |
1312 | |
1313 | rcu_read_lock(); | |
1314 | for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) { | |
1315 | struct ieee80211_sta *sta; | |
1316 | u8 sta_id, tid; | |
1317 | u64 bitmap; | |
1318 | u32 ssn; | |
1319 | u16 ratid; | |
1320 | u16 received_mpdu; | |
1321 | ||
1322 | ratid = le16_to_cpu(notif->ra_tid[i]); | |
1323 | /* check that this TID is valid */ | |
1324 | if (!(ratid & BA_WINDOW_STATUS_VALID_MSK)) | |
1325 | continue; | |
1326 | ||
1327 | received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]); | |
1328 | if (received_mpdu == 0) | |
1329 | continue; | |
1330 | ||
1331 | tid = ratid & BA_WINDOW_STATUS_TID_MSK; | |
1332 | /* get the station */ | |
1333 | sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK) | |
1334 | >> BA_WINDOW_STATUS_STA_ID_POS; | |
1335 | sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); | |
1336 | if (IS_ERR_OR_NULL(sta)) | |
1337 | continue; | |
1338 | bitmap = le64_to_cpu(notif->bitmap[i]); | |
1339 | ssn = le32_to_cpu(notif->start_seq_num[i]); | |
1340 | ||
1341 | /* update mac80211 with the bitmap for the reordering buffer */ | |
1342 | ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap, | |
1343 | received_mpdu); | |
1344 | } | |
1345 | rcu_read_unlock(); | |
1346 | } |