linux/drivers/net/wireless/iwlwifi/mvm/rx.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * This file is provided under a dual BSD/GPLv2 license.  When using or
   4 * redistributing this file, you may do so under either license.
   5 *
   6 * GPL LICENSE SUMMARY
   7 *
   8 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
   9 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of version 2 of the GNU General Public License as
  13 * published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
  23 * USA
  24 *
  25 * The full GNU General Public License is included in this distribution
  26 * in the file called COPYING.
  27 *
  28 * Contact Information:
  29 *  Intel Linux Wireless <ilw@linux.intel.com>
  30 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  31 *
  32 * BSD LICENSE
  33 *
  34 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  35 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  36 * All rights reserved.
  37 *
  38 * Redistribution and use in source and binary forms, with or without
  39 * modification, are permitted provided that the following conditions
  40 * are met:
  41 *
  42 *  * Redistributions of source code must retain the above copyright
  43 *    notice, this list of conditions and the following disclaimer.
  44 *  * Redistributions in binary form must reproduce the above copyright
  45 *    notice, this list of conditions and the following disclaimer in
  46 *    the documentation and/or other materials provided with the
  47 *    distribution.
  48 *  * Neither the name Intel Corporation nor the names of its
  49 *    contributors may be used to endorse or promote products derived
  50 *    from this software without specific prior written permission.
  51 *
  52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  53 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  54 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  55 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  56 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  57 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  58 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  59 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  60 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  61 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  62 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  63 *****************************************************************************/
  64#include "iwl-trans.h"
  65#include "mvm.h"
  66#include "fw-api.h"
  67
  68/*
  69 * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
  70 *
  71 * Copies the phy information in mvm->last_phy_info, it will be used when the
  72 * actual data will come from the fw in the next packet.
  73 */
  74int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
  75                          struct iwl_device_cmd *cmd)
  76{
  77        struct iwl_rx_packet *pkt = rxb_addr(rxb);
  78
  79        memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
  80        mvm->ampdu_ref++;
  81
  82#ifdef CONFIG_IWLWIFI_DEBUGFS
  83        if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
  84                spin_lock(&mvm->drv_stats_lock);
  85                mvm->drv_rx_stats.ampdu_count++;
  86                spin_unlock(&mvm->drv_stats_lock);
  87        }
  88#endif
  89
  90        return 0;
  91}
  92
  93/*
  94 * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
  95 *
  96 * Adds the rxb to a new skb and give it to mac80211
  97 */
  98static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
  99                                            struct sk_buff *skb,
 100                                            struct ieee80211_hdr *hdr, u16 len,
 101                                            u32 ampdu_status, u8 crypt_len,
 102                                            struct iwl_rx_cmd_buffer *rxb)
 103{
 104        unsigned int hdrlen, fraglen;
 105
 106        /* If frame is small enough to fit in skb->head, pull it completely.
 107         * If not, only pull ieee80211_hdr (including crypto if present, and
 108         * an additional 8 bytes for SNAP/ethertype, see below) so that
 109         * splice() or TCP coalesce are more efficient.
 110         *
 111         * Since, in addition, ieee80211_data_to_8023() always pull in at
 112         * least 8 bytes (possibly more for mesh) we can do the same here
 113         * to save the cost of doing it later. That still doesn't pull in
 114         * the actual IP header since the typical case has a SNAP header.
 115         * If the latter changes (there are efforts in the standards group
 116         * to do so) we should revisit this and ieee80211_data_to_8023().
 117         */
 118        hdrlen = (len <= skb_tailroom(skb)) ? len :
 119                                              sizeof(*hdr) + crypt_len + 8;
 120
 121        memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
 122        fraglen = len - hdrlen;
 123
 124        if (fraglen) {
 125                int offset = (void *)hdr + hdrlen -
 126                             rxb_addr(rxb) + rxb_offset(rxb);
 127
 128                skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
 129                                fraglen, rxb->truesize);
 130        }
 131
 132        ieee80211_rx(mvm->hw, skb);
 133}
 134
 135/*
 136 * iwl_mvm_get_signal_strength - use new rx PHY INFO API
 137 * values are reported by the fw as positive values - need to negate
 138 * to obtain their dBM.  Account for missing antennas by replacing 0
 139 * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
 140 */
 141static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
 142                                        struct iwl_rx_phy_info *phy_info,
 143                                        struct ieee80211_rx_status *rx_status)
 144{
 145        int energy_a, energy_b, energy_c, max_energy;
 146        u32 val;
 147
 148        val =
 149            le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
 150        energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
 151                                                IWL_RX_INFO_ENERGY_ANT_A_POS;
 152        energy_a = energy_a ? -energy_a : S8_MIN;
 153        energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
 154                                                IWL_RX_INFO_ENERGY_ANT_B_POS;
 155        energy_b = energy_b ? -energy_b : S8_MIN;
 156        energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
 157                                                IWL_RX_INFO_ENERGY_ANT_C_POS;
 158        energy_c = energy_c ? -energy_c : S8_MIN;
 159        max_energy = max(energy_a, energy_b);
 160        max_energy = max(max_energy, energy_c);
 161
 162        IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
 163                        energy_a, energy_b, energy_c, max_energy);
 164
 165        rx_status->signal = max_energy;
 166        rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
 167                                RX_RES_PHY_FLAGS_ANTENNA)
 168                                        >> RX_RES_PHY_FLAGS_ANTENNA_POS;
 169        rx_status->chain_signal[0] = energy_a;
 170        rx_status->chain_signal[1] = energy_b;
 171        rx_status->chain_signal[2] = energy_c;
 172}
 173
 174/*
 175 * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
 176 * @mvm: the mvm object
 177 * @hdr: 80211 header
 178 * @stats: status in mac80211's format
 179 * @rx_pkt_status: status coming from fw
 180 *
 181 * returns non 0 value if the packet should be dropped
 182 */
 183static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
 184                                        struct ieee80211_hdr *hdr,
 185                                        struct ieee80211_rx_status *stats,
 186                                        u32 rx_pkt_status,
 187                                        u8 *crypt_len)
 188{
 189        if (!ieee80211_has_protected(hdr->frame_control) ||
 190            (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
 191                             RX_MPDU_RES_STATUS_SEC_NO_ENC)
 192                return 0;
 193
 194        /* packet was encrypted with unknown alg */
 195        if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
 196                                        RX_MPDU_RES_STATUS_SEC_ENC_ERR)
 197                return 0;
 198
 199        switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
 200        case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
 201                /* alg is CCM: check MIC only */
 202                if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
 203                        return -1;
 204
 205                stats->flag |= RX_FLAG_DECRYPTED;
 206                IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
 207                *crypt_len = IEEE80211_CCMP_HDR_LEN;
 208                return 0;
 209
 210        case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
 211                /* Don't drop the frame and decrypt it in SW */
 212                if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
 213                        return 0;
 214                *crypt_len = IEEE80211_TKIP_IV_LEN;
 215                /* fall through if TTAK OK */
 216
 217        case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
 218                if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
 219                        return -1;
 220
 221                stats->flag |= RX_FLAG_DECRYPTED;
 222                if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
 223                                RX_MPDU_RES_STATUS_SEC_WEP_ENC)
 224                        *crypt_len = IEEE80211_WEP_IV_LEN;
 225                return 0;
 226
 227        case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
 228                if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
 229                        return -1;
 230                stats->flag |= RX_FLAG_DECRYPTED;
 231                return 0;
 232
 233        default:
 234                IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
 235        }
 236
 237        return 0;
 238}
 239
 240/*
 241 * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
 242 *
 243 * Handles the actual data of the Rx packet from the fw
 244 */
 245int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 246                       struct iwl_device_cmd *cmd)
 247{
 248        struct ieee80211_hdr *hdr;
 249        struct ieee80211_rx_status *rx_status;
 250        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 251        struct iwl_rx_phy_info *phy_info;
 252        struct iwl_rx_mpdu_res_start *rx_res;
 253        struct ieee80211_sta *sta;
 254        struct sk_buff *skb;
 255        u32 len;
 256        u32 ampdu_status;
 257        u32 rate_n_flags;
 258        u32 rx_pkt_status;
 259        u8 crypt_len = 0;
 260
 261        phy_info = &mvm->last_phy_info;
 262        rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
 263        hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
 264        len = le16_to_cpu(rx_res->byte_count);
 265        rx_pkt_status = le32_to_cpup((__le32 *)
 266                (pkt->data + sizeof(*rx_res) + len));
 267
 268        /* Dont use dev_alloc_skb(), we'll have enough headroom once
 269         * ieee80211_hdr pulled.
 270         */
 271        skb = alloc_skb(128, GFP_ATOMIC);
 272        if (!skb) {
 273                IWL_ERR(mvm, "alloc_skb failed\n");
 274                return 0;
 275        }
 276
 277        rx_status = IEEE80211_SKB_RXCB(skb);
 278
 279        /*
 280         * drop the packet if it has failed being decrypted by HW
 281         */
 282        if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
 283                                         &crypt_len)) {
 284                IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
 285                               rx_pkt_status);
 286                kfree_skb(skb);
 287                return 0;
 288        }
 289
 290        if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
 291                IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
 292                               phy_info->cfg_phy_cnt);
 293                kfree_skb(skb);
 294                return 0;
 295        }
 296
 297        /*
 298         * Keep packets with CRC errors (and with overrun) for monitor mode
 299         * (otherwise the firmware discards them) but mark them as bad.
 300         */
 301        if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
 302            !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
 303                IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
 304                rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 305        }
 306
 307        /* This will be used in several places later */
 308        rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
 309
 310        /* rx_status carries information about the packet to mac80211 */
 311        rx_status->mactime = le64_to_cpu(phy_info->timestamp);
 312        rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
 313        rx_status->band =
 314                (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
 315                                IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
 316        rx_status->freq =
 317                ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
 318                                               rx_status->band);
 319        /*
 320         * TSF as indicated by the fw is at INA time, but mac80211 expects the
 321         * TSF at the beginning of the MPDU.
 322         */
 323        /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
 324
 325        iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
 326
 327        IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
 328                              (unsigned long long)rx_status->mactime);
 329
 330        rcu_read_lock();
 331        /*
 332         * We have tx blocked stations (with CS bit). If we heard frames from
 333         * a blocked station on a new channel we can TX to it again.
 334         */
 335        if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
 336                sta = ieee80211_find_sta(
 337                        rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
 338                if (sta)
 339                        iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
 340        }
 341
 342        /* This is fine since we don't support multiple AP interfaces */
 343        sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
 344        if (sta) {
 345                struct iwl_mvm_sta *mvmsta;
 346                mvmsta = iwl_mvm_sta_from_mac80211(sta);
 347                rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
 348
 349                if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
 350                    ieee80211_is_beacon(hdr->frame_control)) {
 351                        struct iwl_fw_dbg_trigger_tlv *trig;
 352                        struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
 353                        bool trig_check;
 354                        s32 rssi;
 355
 356                        trig = iwl_fw_dbg_get_trigger(mvm->fw,
 357                                                      FW_DBG_TRIGGER_RSSI);
 358                        rssi_trig = (void *)trig->data;
 359                        rssi = le32_to_cpu(rssi_trig->rssi);
 360
 361                        trig_check =
 362                                iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
 363                                                              trig);
 364                        if (trig_check && rx_status->signal < rssi)
 365                                iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
 366                }
 367        }
 368
 369        rcu_read_unlock();
 370
 371        /* set the preamble flag if appropriate */
 372        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
 373                rx_status->flag |= RX_FLAG_SHORTPRE;
 374
 375        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
 376                /*
 377                 * We know which subframes of an A-MPDU belong
 378                 * together since we get a single PHY response
 379                 * from the firmware for all of them
 380                 */
 381                rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
 382                rx_status->ampdu_reference = mvm->ampdu_ref;
 383        }
 384
 385        /* Set up the HT phy flags */
 386        switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
 387        case RATE_MCS_CHAN_WIDTH_20:
 388                break;
 389        case RATE_MCS_CHAN_WIDTH_40:
 390                rx_status->flag |= RX_FLAG_40MHZ;
 391                break;
 392        case RATE_MCS_CHAN_WIDTH_80:
 393                rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
 394                break;
 395        case RATE_MCS_CHAN_WIDTH_160:
 396                rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
 397                break;
 398        }
 399        if (rate_n_flags & RATE_MCS_SGI_MSK)
 400                rx_status->flag |= RX_FLAG_SHORT_GI;
 401        if (rate_n_flags & RATE_HT_MCS_GF_MSK)
 402                rx_status->flag |= RX_FLAG_HT_GF;
 403        if (rate_n_flags & RATE_MCS_LDPC_MSK)
 404                rx_status->flag |= RX_FLAG_LDPC;
 405        if (rate_n_flags & RATE_MCS_HT_MSK) {
 406                u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
 407                                RATE_MCS_STBC_POS;
 408                rx_status->flag |= RX_FLAG_HT;
 409                rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
 410                rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
 411        } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
 412                u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
 413                                RATE_MCS_STBC_POS;
 414                rx_status->vht_nss =
 415                        ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
 416                                                RATE_VHT_MCS_NSS_POS) + 1;
 417                rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
 418                rx_status->flag |= RX_FLAG_VHT;
 419                rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
 420                if (rate_n_flags & RATE_MCS_BF_MSK)
 421                        rx_status->vht_flag |= RX_VHT_FLAG_BF;
 422        } else {
 423                rx_status->rate_idx =
 424                        iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
 425                                                            rx_status->band);
 426        }
 427
 428#ifdef CONFIG_IWLWIFI_DEBUGFS
 429        iwl_mvm_update_frame_stats(mvm, rate_n_flags,
 430                                   rx_status->flag & RX_FLAG_AMPDU_DETAILS);
 431#endif
 432        iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status,
 433                                        crypt_len, rxb);
 434        return 0;
 435}
 436
 437static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
 438                                         struct mvm_statistics_rx *rx_stats)
 439{
 440        lockdep_assert_held(&mvm->mutex);
 441
 442        mvm->rx_stats = *rx_stats;
 443}
 444
 445struct iwl_mvm_stat_data {
 446        struct iwl_mvm *mvm;
 447        __le32 mac_id;
 448        __s8 beacon_filter_average_energy;
 449        struct mvm_statistics_general_v8 *general;
 450};
 451
 452static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
 453                                  struct ieee80211_vif *vif)
 454{
 455        struct iwl_mvm_stat_data *data = _data;
 456        struct iwl_mvm *mvm = data->mvm;
 457        int sig = -data->beacon_filter_average_energy;
 458        int last_event;
 459        int thold = vif->bss_conf.cqm_rssi_thold;
 460        int hyst = vif->bss_conf.cqm_rssi_hyst;
 461        u16 id = le32_to_cpu(data->mac_id);
 462        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 463
 464        /* This doesn't need the MAC ID check since it's not taking the
 465         * data copied into the "data" struct, but rather the data from
 466         * the notification directly.
 467         */
 468        if (data->general) {
 469                mvmvif->beacon_stats.num_beacons =
 470                        le32_to_cpu(data->general->beacon_counter[mvmvif->id]);
 471                mvmvif->beacon_stats.avg_signal =
 472                        -data->general->beacon_average_energy[mvmvif->id];
 473        }
 474
 475        if (mvmvif->id != id)
 476                return;
 477
 478        if (vif->type != NL80211_IFTYPE_STATION)
 479                return;
 480
 481        if (sig == 0) {
 482                IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
 483                return;
 484        }
 485
 486        mvmvif->bf_data.ave_beacon_signal = sig;
 487
 488        /* BT Coex */
 489        if (mvmvif->bf_data.bt_coex_min_thold !=
 490            mvmvif->bf_data.bt_coex_max_thold) {
 491                last_event = mvmvif->bf_data.last_bt_coex_event;
 492                if (sig > mvmvif->bf_data.bt_coex_max_thold &&
 493                    (last_event <= mvmvif->bf_data.bt_coex_min_thold ||
 494                     last_event == 0)) {
 495                        mvmvif->bf_data.last_bt_coex_event = sig;
 496                        IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n",
 497                                     sig);
 498                        iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH);
 499                } else if (sig < mvmvif->bf_data.bt_coex_min_thold &&
 500                           (last_event >= mvmvif->bf_data.bt_coex_max_thold ||
 501                            last_event == 0)) {
 502                        mvmvif->bf_data.last_bt_coex_event = sig;
 503                        IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n",
 504                                     sig);
 505                        iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW);
 506                }
 507        }
 508
 509        if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
 510                return;
 511
 512        /* CQM Notification */
 513        last_event = mvmvif->bf_data.last_cqm_event;
 514        if (thold && sig < thold && (last_event == 0 ||
 515                                     sig < last_event - hyst)) {
 516                mvmvif->bf_data.last_cqm_event = sig;
 517                IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
 518                             sig);
 519                ieee80211_cqm_rssi_notify(
 520                        vif,
 521                        NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
 522                        GFP_KERNEL);
 523        } else if (sig > thold &&
 524                   (last_event == 0 || sig > last_event + hyst)) {
 525                mvmvif->bf_data.last_cqm_event = sig;
 526                IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
 527                             sig);
 528                ieee80211_cqm_rssi_notify(
 529                        vif,
 530                        NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
 531                        GFP_KERNEL);
 532        }
 533}
 534
 535static inline void
 536iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
 537{
 538        struct iwl_fw_dbg_trigger_tlv *trig;
 539        struct iwl_fw_dbg_trigger_stats *trig_stats;
 540        u32 trig_offset, trig_thold;
 541
 542        if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS))
 543                return;
 544
 545        trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS);
 546        trig_stats = (void *)trig->data;
 547
 548        if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
 549                return;
 550
 551        trig_offset = le32_to_cpu(trig_stats->stop_offset);
 552        trig_thold = le32_to_cpu(trig_stats->stop_threshold);
 553
 554        if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
 555                return;
 556
 557        if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
 558                return;
 559
 560        iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
 561}
 562
 563void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
 564                                  struct iwl_rx_packet *pkt)
 565{
 566        size_t v8_len = sizeof(struct iwl_notif_statistics_v8);
 567        size_t v10_len = sizeof(struct iwl_notif_statistics_v10);
 568        struct iwl_mvm_stat_data data = {
 569                .mvm = mvm,
 570        };
 571        u32 temperature;
 572
 573        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_STATS_V10) {
 574                struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
 575
 576                if (iwl_rx_packet_payload_len(pkt) != v10_len)
 577                        goto invalid;
 578
 579                temperature = le32_to_cpu(stats->general.radio_temperature);
 580                data.mac_id = stats->rx.general.mac_id;
 581                data.beacon_filter_average_energy =
 582                        stats->general.beacon_filter_average_energy;
 583
 584                iwl_mvm_update_rx_statistics(mvm, &stats->rx);
 585
 586                mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
 587                mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
 588                mvm->radio_stats.on_time_rf =
 589                        le64_to_cpu(stats->general.on_time_rf);
 590                mvm->radio_stats.on_time_scan =
 591                        le64_to_cpu(stats->general.on_time_scan);
 592
 593                data.general = &stats->general;
 594        } else {
 595                struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data;
 596
 597                if (iwl_rx_packet_payload_len(pkt) != v8_len)
 598                        goto invalid;
 599
 600                temperature = le32_to_cpu(stats->general.radio_temperature);
 601                data.mac_id = stats->rx.general.mac_id;
 602                data.beacon_filter_average_energy =
 603                        stats->general.beacon_filter_average_energy;
 604
 605                iwl_mvm_update_rx_statistics(mvm, &stats->rx);
 606        }
 607
 608        iwl_mvm_rx_stats_check_trigger(mvm, pkt);
 609
 610        /* Only handle rx statistics temperature changes if async temp
 611         * notifications are not supported
 612         */
 613        if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_ASYNC_DTM))
 614                iwl_mvm_tt_temp_changed(mvm, temperature);
 615
 616        ieee80211_iterate_active_interfaces(mvm->hw,
 617                                            IEEE80211_IFACE_ITER_NORMAL,
 618                                            iwl_mvm_stat_iterator,
 619                                            &data);
 620        return;
 621 invalid:
 622        IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
 623                iwl_rx_packet_payload_len(pkt));
 624}
 625
 626int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
 627                          struct iwl_rx_cmd_buffer *rxb,
 628                          struct iwl_device_cmd *cmd)
 629{
 630        iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
 631        return 0;
 632}
 633