18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 108c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 118c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 148c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 158c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 188c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 208c2ecf20Sopenharmony_ci * General Public License for more details. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 238c2ecf20Sopenharmony_ci * in the file called COPYING. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Contact Information: 268c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 278c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * BSD LICENSE 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 328c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 338c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 348c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 358c2ecf20Sopenharmony_ci * All rights reserved. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 388c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 398c2ecf20Sopenharmony_ci * are met: 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 428c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 438c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 448c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 458c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 468c2ecf20Sopenharmony_ci * distribution. 478c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 488c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 498c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 528c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 538c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 548c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 558c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 568c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 578c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 588c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 598c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 608c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 618c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 628c2ecf20Sopenharmony_ci *****************************************************************************/ 638c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 648c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 658c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 668c2ecf20Sopenharmony_ci#include "iwl-trans.h" 678c2ecf20Sopenharmony_ci#include "mvm.h" 688c2ecf20Sopenharmony_ci#include "fw-api.h" 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Copies the phy information in mvm->last_phy_info, it will be used when the 748c2ecf20Sopenharmony_ci * actual data will come from the fw in the next packet. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_civoid iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); 818c2ecf20Sopenharmony_ci mvm->ampdu_ref++; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 848c2ecf20Sopenharmony_ci if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { 858c2ecf20Sopenharmony_ci spin_lock(&mvm->drv_stats_lock); 868c2ecf20Sopenharmony_ci mvm->drv_rx_stats.ampdu_count++; 878c2ecf20Sopenharmony_ci spin_unlock(&mvm->drv_stats_lock); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Adds the rxb to a new skb and give it to mac80211 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, 988c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 998c2ecf20Sopenharmony_ci struct napi_struct *napi, 1008c2ecf20Sopenharmony_ci struct sk_buff *skb, 1018c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr, u16 len, 1028c2ecf20Sopenharmony_ci u8 crypt_len, 1038c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); 1068c2ecf20Sopenharmony_ci unsigned int fraglen; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len, 1108c2ecf20Sopenharmony_ci * but those are all multiples of 4 long) all goes away, but we 1118c2ecf20Sopenharmony_ci * want the *end* of it, which is going to be the start of the IP 1128c2ecf20Sopenharmony_ci * header, to be aligned when it gets pulled in. 1138c2ecf20Sopenharmony_ci * The beginning of the skb->data is aligned on at least a 4-byte 1148c2ecf20Sopenharmony_ci * boundary after allocation. Everything here is aligned at least 1158c2ecf20Sopenharmony_ci * on a 2-byte boundary so we can just take hdrlen & 3 and pad by 1168c2ecf20Sopenharmony_ci * the result. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci skb_reserve(skb, hdrlen & 3); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* If frame is small enough to fit in skb->head, pull it completely. 1218c2ecf20Sopenharmony_ci * If not, only pull ieee80211_hdr (including crypto if present, and 1228c2ecf20Sopenharmony_ci * an additional 8 bytes for SNAP/ethertype, see below) so that 1238c2ecf20Sopenharmony_ci * splice() or TCP coalesce are more efficient. 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * Since, in addition, ieee80211_data_to_8023() always pull in at 1268c2ecf20Sopenharmony_ci * least 8 bytes (possibly more for mesh) we can do the same here 1278c2ecf20Sopenharmony_ci * to save the cost of doing it later. That still doesn't pull in 1288c2ecf20Sopenharmony_ci * the actual IP header since the typical case has a SNAP header. 1298c2ecf20Sopenharmony_ci * If the latter changes (there are efforts in the standards group 1308c2ecf20Sopenharmony_ci * to do so) we should revisit this and ieee80211_data_to_8023(). 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci skb_put_data(skb, hdr, hdrlen); 1358c2ecf20Sopenharmony_ci fraglen = len - hdrlen; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (fraglen) { 1388c2ecf20Sopenharmony_ci int offset = (void *)hdr + hdrlen - 1398c2ecf20Sopenharmony_ci rxb_addr(rxb) + rxb_offset(rxb); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, 1428c2ecf20Sopenharmony_ci fraglen, rxb->truesize); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ieee80211_rx_napi(mvm->hw, sta, skb, napi); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * iwl_mvm_get_signal_strength - use new rx PHY INFO API 1508c2ecf20Sopenharmony_ci * values are reported by the fw as positive values - need to negate 1518c2ecf20Sopenharmony_ci * to obtain their dBM. Account for missing antennas by replacing 0 1528c2ecf20Sopenharmony_ci * values by -256dBm: practically 0 power and a non-feasible 8 bit value. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, 1558c2ecf20Sopenharmony_ci struct iwl_rx_phy_info *phy_info, 1568c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int energy_a, energy_b, energy_c, max_energy; 1598c2ecf20Sopenharmony_ci u32 val; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci val = 1628c2ecf20Sopenharmony_ci le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); 1638c2ecf20Sopenharmony_ci energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> 1648c2ecf20Sopenharmony_ci IWL_RX_INFO_ENERGY_ANT_A_POS; 1658c2ecf20Sopenharmony_ci energy_a = energy_a ? -energy_a : S8_MIN; 1668c2ecf20Sopenharmony_ci energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> 1678c2ecf20Sopenharmony_ci IWL_RX_INFO_ENERGY_ANT_B_POS; 1688c2ecf20Sopenharmony_ci energy_b = energy_b ? -energy_b : S8_MIN; 1698c2ecf20Sopenharmony_ci energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >> 1708c2ecf20Sopenharmony_ci IWL_RX_INFO_ENERGY_ANT_C_POS; 1718c2ecf20Sopenharmony_ci energy_c = energy_c ? -energy_c : S8_MIN; 1728c2ecf20Sopenharmony_ci max_energy = max(energy_a, energy_b); 1738c2ecf20Sopenharmony_ci max_energy = max(max_energy, energy_c); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", 1768c2ecf20Sopenharmony_ci energy_a, energy_b, energy_c, max_energy); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci rx_status->signal = max_energy; 1798c2ecf20Sopenharmony_ci rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & 1808c2ecf20Sopenharmony_ci RX_RES_PHY_FLAGS_ANTENNA) 1818c2ecf20Sopenharmony_ci >> RX_RES_PHY_FLAGS_ANTENNA_POS; 1828c2ecf20Sopenharmony_ci rx_status->chain_signal[0] = energy_a; 1838c2ecf20Sopenharmony_ci rx_status->chain_signal[1] = energy_b; 1848c2ecf20Sopenharmony_ci rx_status->chain_signal[2] = energy_c; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format 1898c2ecf20Sopenharmony_ci * @mvm: the mvm object 1908c2ecf20Sopenharmony_ci * @hdr: 80211 header 1918c2ecf20Sopenharmony_ci * @stats: status in mac80211's format 1928c2ecf20Sopenharmony_ci * @rx_pkt_status: status coming from fw 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * returns non 0 value if the packet should be dropped 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, 1978c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr, 1988c2ecf20Sopenharmony_ci struct ieee80211_rx_status *stats, 1998c2ecf20Sopenharmony_ci u32 rx_pkt_status, 2008c2ecf20Sopenharmony_ci u8 *crypt_len) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci if (!ieee80211_has_protected(hdr->frame_control) || 2038c2ecf20Sopenharmony_ci (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == 2048c2ecf20Sopenharmony_ci RX_MPDU_RES_STATUS_SEC_NO_ENC) 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* packet was encrypted with unknown alg */ 2088c2ecf20Sopenharmony_ci if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == 2098c2ecf20Sopenharmony_ci RX_MPDU_RES_STATUS_SEC_ENC_ERR) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { 2138c2ecf20Sopenharmony_ci case RX_MPDU_RES_STATUS_SEC_CCM_ENC: 2148c2ecf20Sopenharmony_ci /* alg is CCM: check MIC only */ 2158c2ecf20Sopenharmony_ci if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) 2168c2ecf20Sopenharmony_ci return -1; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci stats->flag |= RX_FLAG_DECRYPTED; 2198c2ecf20Sopenharmony_ci *crypt_len = IEEE80211_CCMP_HDR_LEN; 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: 2238c2ecf20Sopenharmony_ci /* Don't drop the frame and decrypt it in SW */ 2248c2ecf20Sopenharmony_ci if (!fw_has_api(&mvm->fw->ucode_capa, 2258c2ecf20Sopenharmony_ci IWL_UCODE_TLV_API_DEPRECATE_TTAK) && 2268c2ecf20Sopenharmony_ci !(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci *crypt_len = IEEE80211_TKIP_IV_LEN; 2298c2ecf20Sopenharmony_ci /* fall through */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci case RX_MPDU_RES_STATUS_SEC_WEP_ENC: 2328c2ecf20Sopenharmony_ci if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) 2338c2ecf20Sopenharmony_ci return -1; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci stats->flag |= RX_FLAG_DECRYPTED; 2368c2ecf20Sopenharmony_ci if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == 2378c2ecf20Sopenharmony_ci RX_MPDU_RES_STATUS_SEC_WEP_ENC) 2388c2ecf20Sopenharmony_ci *crypt_len = IEEE80211_WEP_IV_LEN; 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case RX_MPDU_RES_STATUS_SEC_EXT_ENC: 2428c2ecf20Sopenharmony_ci if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) 2438c2ecf20Sopenharmony_ci return -1; 2448c2ecf20Sopenharmony_ci stats->flag |= RX_FLAG_DECRYPTED; 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci default: 2488c2ecf20Sopenharmony_ci /* Expected in monitor (not having the keys) */ 2498c2ecf20Sopenharmony_ci if (!mvm->monitor_on) 2508c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm, 2578c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 2588c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr, u32 len, 2598c2ecf20Sopenharmony_ci struct iwl_rx_phy_info *phy_info, 2608c2ecf20Sopenharmony_ci u32 rate_n_flags) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 2638c2ecf20Sopenharmony_ci struct iwl_mvm_tcm_mac *mdata; 2648c2ecf20Sopenharmony_ci int mac; 2658c2ecf20Sopenharmony_ci int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */ 2668c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif; 2678c2ecf20Sopenharmony_ci /* expected throughput in 100Kbps, single stream, 20 MHz */ 2688c2ecf20Sopenharmony_ci static const u8 thresh_tpt[] = { 2698c2ecf20Sopenharmony_ci 9, 18, 30, 42, 60, 78, 90, 96, 120, 135, 2708c2ecf20Sopenharmony_ci }; 2718c2ecf20Sopenharmony_ci u16 thr; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) 2748c2ecf20Sopenharmony_ci ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)]; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 2778c2ecf20Sopenharmony_ci mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) 2808c2ecf20Sopenharmony_ci schedule_delayed_work(&mvm->tcm.work, 0); 2818c2ecf20Sopenharmony_ci mdata = &mvm->tcm.data[mac]; 2828c2ecf20Sopenharmony_ci mdata->rx.pkts[ac]++; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* count the airtime only once for each ampdu */ 2858c2ecf20Sopenharmony_ci if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) { 2868c2ecf20Sopenharmony_ci mdata->rx.last_ampdu_ref = mvm->ampdu_ref; 2878c2ecf20Sopenharmony_ci mdata->rx.airtime += le16_to_cpu(phy_info->frame_time); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK))) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (mdata->opened_rx_ba_sessions || 2968c2ecf20Sopenharmony_ci mdata->uapsd_nonagg_detect.detected || 2978c2ecf20Sopenharmony_ci (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && 2988c2ecf20Sopenharmony_ci !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && 2998c2ecf20Sopenharmony_ci !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && 3008c2ecf20Sopenharmony_ci !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) || 3018c2ecf20Sopenharmony_ci mvmsta->sta_id != mvmvif->ap_sta_id) 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 3058c2ecf20Sopenharmony_ci thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK]; 3068c2ecf20Sopenharmony_ci thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >> 3078c2ecf20Sopenharmony_ci RATE_HT_MCS_NSS_POS); 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >= 3108c2ecf20Sopenharmony_ci ARRAY_SIZE(thresh_tpt))) 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK]; 3138c2ecf20Sopenharmony_ci thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> 3148c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) >> 3188c2ecf20Sopenharmony_ci RATE_MCS_CHAN_WIDTH_POS); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci mdata->uapsd_nonagg_detect.rx_bytes += len; 3218c2ecf20Sopenharmony_ci ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_csum(struct ieee80211_sta *sta, 3258c2ecf20Sopenharmony_ci struct sk_buff *skb, 3268c2ecf20Sopenharmony_ci u32 status) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 3298c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (mvmvif->features & NETIF_F_RXCSUM && 3328c2ecf20Sopenharmony_ci status & RX_MPDU_RES_STATUS_CSUM_DONE && 3338c2ecf20Sopenharmony_ci status & RX_MPDU_RES_STATUS_CSUM_OK) 3348c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* 3388c2ecf20Sopenharmony_ci * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Handles the actual data of the Rx packet from the fw 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_civoid iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, 3438c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 3468c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status; 3478c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 3488c2ecf20Sopenharmony_ci struct iwl_rx_phy_info *phy_info; 3498c2ecf20Sopenharmony_ci struct iwl_rx_mpdu_res_start *rx_res; 3508c2ecf20Sopenharmony_ci struct ieee80211_sta *sta = NULL; 3518c2ecf20Sopenharmony_ci struct sk_buff *skb; 3528c2ecf20Sopenharmony_ci u32 len; 3538c2ecf20Sopenharmony_ci u32 rate_n_flags; 3548c2ecf20Sopenharmony_ci u32 rx_pkt_status; 3558c2ecf20Sopenharmony_ci u8 crypt_len = 0; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci phy_info = &mvm->last_phy_info; 3588c2ecf20Sopenharmony_ci rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; 3598c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); 3608c2ecf20Sopenharmony_ci len = le16_to_cpu(rx_res->byte_count); 3618c2ecf20Sopenharmony_ci rx_pkt_status = get_unaligned_le32((__le32 *) 3628c2ecf20Sopenharmony_ci (pkt->data + sizeof(*rx_res) + len)); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Dont use dev_alloc_skb(), we'll have enough headroom once 3658c2ecf20Sopenharmony_ci * ieee80211_hdr pulled. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci skb = alloc_skb(128, GFP_ATOMIC); 3688c2ecf20Sopenharmony_ci if (!skb) { 3698c2ecf20Sopenharmony_ci IWL_ERR(mvm, "alloc_skb failed\n"); 3708c2ecf20Sopenharmony_ci return; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci rx_status = IEEE80211_SKB_RXCB(skb); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * drop the packet if it has failed being decrypted by HW 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, 3798c2ecf20Sopenharmony_ci &crypt_len)) { 3808c2ecf20Sopenharmony_ci IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", 3818c2ecf20Sopenharmony_ci rx_pkt_status); 3828c2ecf20Sopenharmony_ci kfree_skb(skb); 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * Keep packets with CRC errors (and with overrun) for monitor mode 3888c2ecf20Sopenharmony_ci * (otherwise the firmware discards them) but mark them as bad. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || 3918c2ecf20Sopenharmony_ci !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { 3928c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); 3938c2ecf20Sopenharmony_ci rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* This will be used in several places later */ 3978c2ecf20Sopenharmony_ci rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* rx_status carries information about the packet to mac80211 */ 4008c2ecf20Sopenharmony_ci rx_status->mactime = le64_to_cpu(phy_info->timestamp); 4018c2ecf20Sopenharmony_ci rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp); 4028c2ecf20Sopenharmony_ci rx_status->band = 4038c2ecf20Sopenharmony_ci (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? 4048c2ecf20Sopenharmony_ci NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 4058c2ecf20Sopenharmony_ci rx_status->freq = 4068c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), 4078c2ecf20Sopenharmony_ci rx_status->band); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* TSF as indicated by the firmware is at INA time */ 4108c2ecf20Sopenharmony_ci rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, 4158c2ecf20Sopenharmony_ci (unsigned long long)rx_status->mactime); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci rcu_read_lock(); 4188c2ecf20Sopenharmony_ci if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) { 4198c2ecf20Sopenharmony_ci u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!WARN_ON_ONCE(id >= mvm->fw->ucode_capa.num_stations)) { 4248c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); 4258c2ecf20Sopenharmony_ci if (IS_ERR(sta)) 4268c2ecf20Sopenharmony_ci sta = NULL; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci } else if (!is_multicast_ether_addr(hdr->addr2)) { 4298c2ecf20Sopenharmony_ci /* This is fine since we prevent two stations with the same 4308c2ecf20Sopenharmony_ci * address from being added. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (sta) { 4368c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 4378c2ecf20Sopenharmony_ci struct ieee80211_vif *tx_blocked_vif = 4388c2ecf20Sopenharmony_ci rcu_dereference(mvm->csa_tx_blocked_vif); 4398c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tlv *trig; 4408c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = mvmsta->vif; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* We have tx blocked stations (with CS bit). If we heard 4438c2ecf20Sopenharmony_ci * frames from a blocked station on a new channel we can 4448c2ecf20Sopenharmony_ci * TX to it again. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci if (unlikely(tx_blocked_vif) && vif == tx_blocked_vif) { 4478c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = 4488c2ecf20Sopenharmony_ci iwl_mvm_vif_from_mac80211(tx_blocked_vif); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (mvmvif->csa_target_freq == rx_status->freq) 4518c2ecf20Sopenharmony_ci iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, 4528c2ecf20Sopenharmony_ci false); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci rs_update_last_rssi(mvm, mvmsta, rx_status); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, 4588c2ecf20Sopenharmony_ci ieee80211_vif_to_wdev(vif), 4598c2ecf20Sopenharmony_ci FW_DBG_TRIGGER_RSSI); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (trig && ieee80211_is_beacon(hdr->frame_control)) { 4628c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; 4638c2ecf20Sopenharmony_ci s32 rssi; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci rssi_trig = (void *)trig->data; 4668c2ecf20Sopenharmony_ci rssi = le32_to_cpu(rssi_trig->rssi); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (rx_status->signal < rssi) 4698c2ecf20Sopenharmony_ci iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 4708c2ecf20Sopenharmony_ci NULL); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (!mvm->tcm.paused && len >= sizeof(*hdr) && 4748c2ecf20Sopenharmony_ci !is_multicast_ether_addr(hdr->addr1) && 4758c2ecf20Sopenharmony_ci ieee80211_is_data(hdr->frame_control)) 4768c2ecf20Sopenharmony_ci iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info, 4778c2ecf20Sopenharmony_ci rate_n_flags); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (ieee80211_is_data(hdr->frame_control)) 4808c2ecf20Sopenharmony_ci iwl_mvm_rx_csum(sta, skb, rx_pkt_status); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci rcu_read_unlock(); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* set the preamble flag if appropriate */ 4858c2ecf20Sopenharmony_ci if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) 4868c2ecf20Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * We know which subframes of an A-MPDU belong 4918c2ecf20Sopenharmony_ci * together since we get a single PHY response 4928c2ecf20Sopenharmony_ci * from the firmware for all of them 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci rx_status->flag |= RX_FLAG_AMPDU_DETAILS; 4958c2ecf20Sopenharmony_ci rx_status->ampdu_reference = mvm->ampdu_ref; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Set up the HT phy flags */ 4998c2ecf20Sopenharmony_ci switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { 5008c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 5038c2ecf20Sopenharmony_ci rx_status->bw = RATE_INFO_BW_40; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 5068c2ecf20Sopenharmony_ci rx_status->bw = RATE_INFO_BW_80; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 5098c2ecf20Sopenharmony_ci rx_status->bw = RATE_INFO_BW_160; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci if (!(rate_n_flags & RATE_MCS_CCK_MSK) && 5138c2ecf20Sopenharmony_ci rate_n_flags & RATE_MCS_SGI_MSK) 5148c2ecf20Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; 5158c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_HT_MCS_GF_MSK) 5168c2ecf20Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_HT_GF; 5178c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_LDPC_MSK) 5188c2ecf20Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_LDPC; 5198c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 5208c2ecf20Sopenharmony_ci u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> 5218c2ecf20Sopenharmony_ci RATE_MCS_STBC_POS; 5228c2ecf20Sopenharmony_ci rx_status->encoding = RX_ENC_HT; 5238c2ecf20Sopenharmony_ci rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; 5248c2ecf20Sopenharmony_ci rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; 5258c2ecf20Sopenharmony_ci } else if (rate_n_flags & RATE_MCS_VHT_MSK) { 5268c2ecf20Sopenharmony_ci u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> 5278c2ecf20Sopenharmony_ci RATE_MCS_STBC_POS; 5288c2ecf20Sopenharmony_ci rx_status->nss = 5298c2ecf20Sopenharmony_ci ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> 5308c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS) + 1; 5318c2ecf20Sopenharmony_ci rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; 5328c2ecf20Sopenharmony_ci rx_status->encoding = RX_ENC_VHT; 5338c2ecf20Sopenharmony_ci rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; 5348c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_BF_MSK) 5358c2ecf20Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_BF; 5368c2ecf20Sopenharmony_ci } else { 5378c2ecf20Sopenharmony_ci int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, 5388c2ecf20Sopenharmony_ci rx_status->band); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (WARN(rate < 0 || rate > 0xFF, 5418c2ecf20Sopenharmony_ci "Invalid rate flags 0x%x, band %d,\n", 5428c2ecf20Sopenharmony_ci rate_n_flags, rx_status->band)) { 5438c2ecf20Sopenharmony_ci kfree_skb(skb); 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci rx_status->rate_idx = rate; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 5508c2ecf20Sopenharmony_ci iwl_mvm_update_frame_stats(mvm, rate_n_flags, 5518c2ecf20Sopenharmony_ci rx_status->flag & RX_FLAG_AMPDU_DETAILS); 5528c2ecf20Sopenharmony_ci#endif 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (unlikely((ieee80211_is_beacon(hdr->frame_control) || 5558c2ecf20Sopenharmony_ci ieee80211_is_probe_resp(hdr->frame_control)) && 5568c2ecf20Sopenharmony_ci mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)) 5578c2ecf20Sopenharmony_ci mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (unlikely(ieee80211_is_beacon(hdr->frame_control) || 5608c2ecf20Sopenharmony_ci ieee80211_is_probe_resp(hdr->frame_control))) 5618c2ecf20Sopenharmony_ci rx_status->boottime_ns = ktime_get_boottime_ns(); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci iwl_mvm_pass_packet_to_mac80211(mvm, sta, napi, skb, hdr, len, 5648c2ecf20Sopenharmony_ci crypt_len, rxb); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistruct iwl_mvm_stat_data { 5688c2ecf20Sopenharmony_ci struct iwl_mvm *mvm; 5698c2ecf20Sopenharmony_ci __le32 flags; 5708c2ecf20Sopenharmony_ci __le32 mac_id; 5718c2ecf20Sopenharmony_ci u8 beacon_filter_average_energy; 5728c2ecf20Sopenharmony_ci __le32 *beacon_counter; 5738c2ecf20Sopenharmony_ci u8 *beacon_average_energy; 5748c2ecf20Sopenharmony_ci}; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic void iwl_mvm_stat_iterator(void *_data, u8 *mac, 5778c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct iwl_mvm_stat_data *data = _data; 5808c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = data->mvm; 5818c2ecf20Sopenharmony_ci int sig = -data->beacon_filter_average_energy; 5828c2ecf20Sopenharmony_ci int last_event; 5838c2ecf20Sopenharmony_ci int thold = vif->bss_conf.cqm_rssi_thold; 5848c2ecf20Sopenharmony_ci int hyst = vif->bss_conf.cqm_rssi_hyst; 5858c2ecf20Sopenharmony_ci u16 id = le32_to_cpu(data->mac_id); 5868c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 5878c2ecf20Sopenharmony_ci u16 vif_id = mvmvif->id; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* This doesn't need the MAC ID check since it's not taking the 5908c2ecf20Sopenharmony_ci * data copied into the "data" struct, but rather the data from 5918c2ecf20Sopenharmony_ci * the notification directly. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci mvmvif->beacon_stats.num_beacons = 5948c2ecf20Sopenharmony_ci le32_to_cpu(data->beacon_counter[vif_id]); 5958c2ecf20Sopenharmony_ci mvmvif->beacon_stats.avg_signal = 5968c2ecf20Sopenharmony_ci -data->beacon_average_energy[vif_id]; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* make sure that beacon statistics don't go backwards with TCM 5998c2ecf20Sopenharmony_ci * request to clear statistics 6008c2ecf20Sopenharmony_ci */ 6018c2ecf20Sopenharmony_ci if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) 6028c2ecf20Sopenharmony_ci mvmvif->beacon_stats.accu_num_beacons += 6038c2ecf20Sopenharmony_ci mvmvif->beacon_stats.num_beacons; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (mvmvif->id != id) 6068c2ecf20Sopenharmony_ci return; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION) 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (sig == 0) { 6128c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n"); 6138c2ecf20Sopenharmony_ci return; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci mvmvif->bf_data.ave_beacon_signal = sig; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* BT Coex */ 6198c2ecf20Sopenharmony_ci if (mvmvif->bf_data.bt_coex_min_thold != 6208c2ecf20Sopenharmony_ci mvmvif->bf_data.bt_coex_max_thold) { 6218c2ecf20Sopenharmony_ci last_event = mvmvif->bf_data.last_bt_coex_event; 6228c2ecf20Sopenharmony_ci if (sig > mvmvif->bf_data.bt_coex_max_thold && 6238c2ecf20Sopenharmony_ci (last_event <= mvmvif->bf_data.bt_coex_min_thold || 6248c2ecf20Sopenharmony_ci last_event == 0)) { 6258c2ecf20Sopenharmony_ci mvmvif->bf_data.last_bt_coex_event = sig; 6268c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", 6278c2ecf20Sopenharmony_ci sig); 6288c2ecf20Sopenharmony_ci iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); 6298c2ecf20Sopenharmony_ci } else if (sig < mvmvif->bf_data.bt_coex_min_thold && 6308c2ecf20Sopenharmony_ci (last_event >= mvmvif->bf_data.bt_coex_max_thold || 6318c2ecf20Sopenharmony_ci last_event == 0)) { 6328c2ecf20Sopenharmony_ci mvmvif->bf_data.last_bt_coex_event = sig; 6338c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", 6348c2ecf20Sopenharmony_ci sig); 6358c2ecf20Sopenharmony_ci iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) 6408c2ecf20Sopenharmony_ci return; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* CQM Notification */ 6438c2ecf20Sopenharmony_ci last_event = mvmvif->bf_data.last_cqm_event; 6448c2ecf20Sopenharmony_ci if (thold && sig < thold && (last_event == 0 || 6458c2ecf20Sopenharmony_ci sig < last_event - hyst)) { 6468c2ecf20Sopenharmony_ci mvmvif->bf_data.last_cqm_event = sig; 6478c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", 6488c2ecf20Sopenharmony_ci sig); 6498c2ecf20Sopenharmony_ci ieee80211_cqm_rssi_notify( 6508c2ecf20Sopenharmony_ci vif, 6518c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, 6528c2ecf20Sopenharmony_ci sig, 6538c2ecf20Sopenharmony_ci GFP_KERNEL); 6548c2ecf20Sopenharmony_ci } else if (sig > thold && 6558c2ecf20Sopenharmony_ci (last_event == 0 || sig > last_event + hyst)) { 6568c2ecf20Sopenharmony_ci mvmvif->bf_data.last_cqm_event = sig; 6578c2ecf20Sopenharmony_ci IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", 6588c2ecf20Sopenharmony_ci sig); 6598c2ecf20Sopenharmony_ci ieee80211_cqm_rssi_notify( 6608c2ecf20Sopenharmony_ci vif, 6618c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, 6628c2ecf20Sopenharmony_ci sig, 6638c2ecf20Sopenharmony_ci GFP_KERNEL); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic inline void 6688c2ecf20Sopenharmony_ciiwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tlv *trig; 6718c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_stats *trig_stats; 6728c2ecf20Sopenharmony_ci u32 trig_offset, trig_thold; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, NULL, FW_DBG_TRIGGER_STATS); 6758c2ecf20Sopenharmony_ci if (!trig) 6768c2ecf20Sopenharmony_ci return; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci trig_stats = (void *)trig->data; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci trig_offset = le32_to_cpu(trig_stats->stop_offset); 6818c2ecf20Sopenharmony_ci trig_thold = le32_to_cpu(trig_stats->stop_threshold); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt))) 6848c2ecf20Sopenharmony_ci return; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) 6878c2ecf20Sopenharmony_ci return; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, NULL); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void iwl_mvm_update_avg_energy(struct iwl_mvm *mvm, 6938c2ecf20Sopenharmony_ci u8 energy[IWL_MVM_STATION_COUNT_MAX]) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci int i; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (WARN_ONCE(mvm->fw->ucode_capa.num_stations > 6988c2ecf20Sopenharmony_ci IWL_MVM_STATION_COUNT_MAX, 6998c2ecf20Sopenharmony_ci "Driver and FW station count mismatch %d\n", 7008c2ecf20Sopenharmony_ci mvm->fw->ucode_capa.num_stations)) 7018c2ecf20Sopenharmony_ci return; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci rcu_read_lock(); 7048c2ecf20Sopenharmony_ci for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { 7058c2ecf20Sopenharmony_ci struct iwl_mvm_sta *sta; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (!energy[i]) 7088c2ecf20Sopenharmony_ci continue; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci sta = iwl_mvm_sta_from_staid_rcu(mvm, i); 7118c2ecf20Sopenharmony_ci if (!sta) 7128c2ecf20Sopenharmony_ci continue; 7138c2ecf20Sopenharmony_ci sta->avg_energy = energy[i]; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci rcu_read_unlock(); 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic void 7198c2ecf20Sopenharmony_ciiwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le, 7208c2ecf20Sopenharmony_ci __le32 *rx_bytes_le) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int i; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci spin_lock(&mvm->tcm.lock); 7258c2ecf20Sopenharmony_ci for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) { 7268c2ecf20Sopenharmony_ci struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i]; 7278c2ecf20Sopenharmony_ci u32 rx_bytes = le32_to_cpu(rx_bytes_le[i]); 7288c2ecf20Sopenharmony_ci u32 airtime = le32_to_cpu(air_time_le[i]); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci mdata->rx.airtime += airtime; 7318c2ecf20Sopenharmony_ci mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes; 7328c2ecf20Sopenharmony_ci if (airtime) { 7338c2ecf20Sopenharmony_ci /* re-init every time to store rate from FW */ 7348c2ecf20Sopenharmony_ci ewma_rate_init(&mdata->uapsd_nonagg_detect.rate); 7358c2ecf20Sopenharmony_ci ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, 7368c2ecf20Sopenharmony_ci rx_bytes * 8 / airtime); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci spin_unlock(&mvm->tcm.lock); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic void 7438c2ecf20Sopenharmony_ciiwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm, 7448c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct iwl_mvm_stat_data data = { 7478c2ecf20Sopenharmony_ci .mvm = mvm, 7488c2ecf20Sopenharmony_ci }; 7498c2ecf20Sopenharmony_ci u8 beacon_average_energy[MAC_INDEX_AUX]; 7508c2ecf20Sopenharmony_ci u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; 7518c2ecf20Sopenharmony_ci struct iwl_statistics_operational_ntfy *stats; 7528c2ecf20Sopenharmony_ci int expected_size; 7538c2ecf20Sopenharmony_ci __le32 flags; 7548c2ecf20Sopenharmony_ci int i; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci expected_size = sizeof(*stats); 7578c2ecf20Sopenharmony_ci if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) < expected_size, 7588c2ecf20Sopenharmony_ci "received invalid statistics size (%d)!, expected_size: %d\n", 7598c2ecf20Sopenharmony_ci iwl_rx_packet_payload_len(pkt), expected_size)) 7608c2ecf20Sopenharmony_ci return; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci stats = (void *)&pkt->data; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (WARN_ONCE(stats->hdr.type != FW_STATISTICS_OPERATIONAL || 7658c2ecf20Sopenharmony_ci stats->hdr.version != 1, 7668c2ecf20Sopenharmony_ci "received unsupported hdr type %d, version %d\n", 7678c2ecf20Sopenharmony_ci stats->hdr.type, stats->hdr.version)) 7688c2ecf20Sopenharmony_ci return; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci flags = stats->flags; 7718c2ecf20Sopenharmony_ci mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time); 7728c2ecf20Sopenharmony_ci mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time); 7738c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf); 7748c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_scan = le64_to_cpu(stats->on_time_scan); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci iwl_mvm_rx_stats_check_trigger(mvm, pkt); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci data.mac_id = stats->mac_id; 7798c2ecf20Sopenharmony_ci data.beacon_filter_average_energy = 7808c2ecf20Sopenharmony_ci le32_to_cpu(stats->beacon_filter_average_energy); 7818c2ecf20Sopenharmony_ci data.flags = flags; 7828c2ecf20Sopenharmony_ci data.beacon_counter = stats->beacon_counter; 7838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(beacon_average_energy); i++) 7848c2ecf20Sopenharmony_ci beacon_average_energy[i] = 7858c2ecf20Sopenharmony_ci le32_to_cpu(stats->beacon_average_energy[i]); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci data.beacon_average_energy = beacon_average_energy; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces(mvm->hw, 7908c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_NORMAL, 7918c2ecf20Sopenharmony_ci iwl_mvm_stat_iterator, 7928c2ecf20Sopenharmony_ci &data); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(average_energy); i++) 7958c2ecf20Sopenharmony_ci average_energy[i] = le32_to_cpu(stats->average_energy[i]); 7968c2ecf20Sopenharmony_ci iwl_mvm_update_avg_energy(mvm, average_energy); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* 7998c2ecf20Sopenharmony_ci * Don't update in case the statistics are not cleared, since 8008c2ecf20Sopenharmony_ci * we will end up counting twice the same airtime, once in TCM 8018c2ecf20Sopenharmony_ci * request and once in statistics notification. 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) 8048c2ecf20Sopenharmony_ci iwl_mvm_update_tcm_from_stats(mvm, stats->air_time, 8058c2ecf20Sopenharmony_ci stats->rx_bytes); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_civoid iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, 8098c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct iwl_mvm_stat_data data = { 8128c2ecf20Sopenharmony_ci .mvm = mvm, 8138c2ecf20Sopenharmony_ci }; 8148c2ecf20Sopenharmony_ci __le32 *bytes, *air_time, flags; 8158c2ecf20Sopenharmony_ci int expected_size; 8168c2ecf20Sopenharmony_ci u8 *energy; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* From ver 14 and up we use TLV statistics format */ 8198c2ecf20Sopenharmony_ci if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, 8208c2ecf20Sopenharmony_ci STATISTICS_CMD, 0) >= 14) 8218c2ecf20Sopenharmony_ci return iwl_mvm_handle_rx_statistics_tlv(mvm, pkt); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_stats_api(mvm)) { 8248c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_rx_api(mvm)) 8258c2ecf20Sopenharmony_ci expected_size = sizeof(struct iwl_notif_statistics_v11); 8268c2ecf20Sopenharmony_ci else 8278c2ecf20Sopenharmony_ci expected_size = sizeof(struct iwl_notif_statistics_v10); 8288c2ecf20Sopenharmony_ci } else { 8298c2ecf20Sopenharmony_ci expected_size = sizeof(struct iwl_notif_statistics); 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) != expected_size, 8338c2ecf20Sopenharmony_ci "received invalid statistics size (%d)!\n", 8348c2ecf20Sopenharmony_ci iwl_rx_packet_payload_len(pkt))) 8358c2ecf20Sopenharmony_ci return; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_stats_api(mvm)) { 8388c2ecf20Sopenharmony_ci struct iwl_notif_statistics_v11 *stats = (void *)&pkt->data; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci data.mac_id = stats->rx.general.mac_id; 8418c2ecf20Sopenharmony_ci data.beacon_filter_average_energy = 8428c2ecf20Sopenharmony_ci stats->general.common.beacon_filter_average_energy; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci mvm->rx_stats_v3 = stats->rx; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci mvm->radio_stats.rx_time = 8478c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.rx_time); 8488c2ecf20Sopenharmony_ci mvm->radio_stats.tx_time = 8498c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.tx_time); 8508c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_rf = 8518c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.on_time_rf); 8528c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_scan = 8538c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.on_time_scan); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci data.beacon_counter = stats->general.beacon_counter; 8568c2ecf20Sopenharmony_ci data.beacon_average_energy = 8578c2ecf20Sopenharmony_ci stats->general.beacon_average_energy; 8588c2ecf20Sopenharmony_ci flags = stats->flag; 8598c2ecf20Sopenharmony_ci } else { 8608c2ecf20Sopenharmony_ci struct iwl_notif_statistics *stats = (void *)&pkt->data; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci data.mac_id = stats->rx.general.mac_id; 8638c2ecf20Sopenharmony_ci data.beacon_filter_average_energy = 8648c2ecf20Sopenharmony_ci stats->general.common.beacon_filter_average_energy; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci mvm->rx_stats = stats->rx; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci mvm->radio_stats.rx_time = 8698c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.rx_time); 8708c2ecf20Sopenharmony_ci mvm->radio_stats.tx_time = 8718c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.tx_time); 8728c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_rf = 8738c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.on_time_rf); 8748c2ecf20Sopenharmony_ci mvm->radio_stats.on_time_scan = 8758c2ecf20Sopenharmony_ci le64_to_cpu(stats->general.common.on_time_scan); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci data.beacon_counter = stats->general.beacon_counter; 8788c2ecf20Sopenharmony_ci data.beacon_average_energy = 8798c2ecf20Sopenharmony_ci stats->general.beacon_average_energy; 8808c2ecf20Sopenharmony_ci flags = stats->flag; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci data.flags = flags; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci iwl_mvm_rx_stats_check_trigger(mvm, pkt); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces(mvm->hw, 8878c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_NORMAL, 8888c2ecf20Sopenharmony_ci iwl_mvm_stat_iterator, 8898c2ecf20Sopenharmony_ci &data); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_api(mvm)) 8928c2ecf20Sopenharmony_ci return; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_stats_api(mvm)) { 8958c2ecf20Sopenharmony_ci struct iwl_notif_statistics_v11 *v11 = (void *)&pkt->data; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci energy = (void *)&v11->load_stats.avg_energy; 8988c2ecf20Sopenharmony_ci bytes = (void *)&v11->load_stats.byte_count; 8998c2ecf20Sopenharmony_ci air_time = (void *)&v11->load_stats.air_time; 9008c2ecf20Sopenharmony_ci } else { 9018c2ecf20Sopenharmony_ci struct iwl_notif_statistics *stats = (void *)&pkt->data; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci energy = (void *)&stats->load_stats.avg_energy; 9048c2ecf20Sopenharmony_ci bytes = (void *)&stats->load_stats.byte_count; 9058c2ecf20Sopenharmony_ci air_time = (void *)&stats->load_stats.air_time; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci iwl_mvm_update_avg_energy(mvm, energy); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * Don't update in case the statistics are not cleared, since 9128c2ecf20Sopenharmony_ci * we will end up counting twice the same airtime, once in TCM 9138c2ecf20Sopenharmony_ci * request and once in statistics notification. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) 9168c2ecf20Sopenharmony_ci iwl_mvm_update_tcm_from_stats(mvm, air_time, bytes); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_civoid iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_civoid iwl_mvm_window_status_notif(struct iwl_mvm *mvm, 9268c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 9298c2ecf20Sopenharmony_ci struct iwl_ba_window_status_notif *notif = (void *)pkt->data; 9308c2ecf20Sopenharmony_ci int i; 9318c2ecf20Sopenharmony_ci u32 pkt_len = iwl_rx_packet_payload_len(pkt); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (WARN_ONCE(pkt_len != sizeof(*notif), 9348c2ecf20Sopenharmony_ci "Received window status notification of wrong size (%u)\n", 9358c2ecf20Sopenharmony_ci pkt_len)) 9368c2ecf20Sopenharmony_ci return; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci rcu_read_lock(); 9398c2ecf20Sopenharmony_ci for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) { 9408c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 9418c2ecf20Sopenharmony_ci u8 sta_id, tid; 9428c2ecf20Sopenharmony_ci u64 bitmap; 9438c2ecf20Sopenharmony_ci u32 ssn; 9448c2ecf20Sopenharmony_ci u16 ratid; 9458c2ecf20Sopenharmony_ci u16 received_mpdu; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci ratid = le16_to_cpu(notif->ra_tid[i]); 9488c2ecf20Sopenharmony_ci /* check that this TID is valid */ 9498c2ecf20Sopenharmony_ci if (!(ratid & BA_WINDOW_STATUS_VALID_MSK)) 9508c2ecf20Sopenharmony_ci continue; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]); 9538c2ecf20Sopenharmony_ci if (received_mpdu == 0) 9548c2ecf20Sopenharmony_ci continue; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci tid = ratid & BA_WINDOW_STATUS_TID_MSK; 9578c2ecf20Sopenharmony_ci /* get the station */ 9588c2ecf20Sopenharmony_ci sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK) 9598c2ecf20Sopenharmony_ci >> BA_WINDOW_STATUS_STA_ID_POS; 9608c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 9618c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) 9628c2ecf20Sopenharmony_ci continue; 9638c2ecf20Sopenharmony_ci bitmap = le64_to_cpu(notif->bitmap[i]); 9648c2ecf20Sopenharmony_ci ssn = le32_to_cpu(notif->start_seq_num[i]); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* update mac80211 with the bitmap for the reordering buffer */ 9678c2ecf20Sopenharmony_ci ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap, 9688c2ecf20Sopenharmony_ci received_mpdu); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci rcu_read_unlock(); 9718c2ecf20Sopenharmony_ci} 972