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