diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
37 files changed, 1634 insertions, 1619 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index f2a86ffc3b4c..23d5f0275ce9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -397,7 +397,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) return cpu_to_le32(flags|(u32)rate); } -extern int iwl_alive_start(struct iwl_priv *priv); +int iwl_alive_start(struct iwl_priv *priv); #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index a79fdd137f95..7434d9edf3b7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -270,7 +270,7 @@ struct iwl_sensitivity_ranges { * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * ****************************************************************************/ -extern void iwl_update_chain_flags(struct iwl_priv *priv); +void iwl_update_chain_flags(struct iwl_priv *priv); extern const u8 iwl_bcast_addr[ETH_ALEN]; #define IWL_OPERATION_MODE_AUTO 0 diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h index 5d83cab22d62..41988f4b8a5a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.h +++ b/drivers/net/wireless/iwlwifi/dvm/rs.h @@ -389,13 +389,6 @@ struct iwl_lq_sta { u8 last_bt_traffic; }; -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - static inline u8 first_antenna(u8 mask) { if (mask & ANT_A) @@ -407,8 +400,8 @@ static inline u8 first_antenna(u8 mask) /* Initialize station's rate scaling information after adding station */ -extern void iwl_rs_rate_init(struct iwl_priv *priv, - struct ieee80211_sta *sta, u8 sta_id); +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, + u8 sta_id); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -420,7 +413,7 @@ extern void iwl_rs_rate_init(struct iwl_priv *priv, * ieee80211_register_hw * */ -extern int iwlagn_rate_control_register(void); +int iwlagn_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -428,6 +421,6 @@ extern int iwlagn_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwlagn_rate_control_unregister(void); +void iwlagn_rate_control_unregister(void); #endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 1fef5240e6ad..e12b1a63c484 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -368,6 +368,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, goto drop_unlock_priv; memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = REPLY_TX; tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; /* Total # bytes to be transmitted */ diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 85879dbaa402..3c34a72a5d64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -67,8 +67,8 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 7 -#define IWL3160_UCODE_API_MAX 7 +#define IWL7260_UCODE_API_MAX 8 +#define IWL3160_UCODE_API_MAX 8 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 7 @@ -130,6 +130,7 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { @@ -140,6 +141,7 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .high_temp = true, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl7260_2n_cfg = { @@ -149,6 +151,7 @@ const struct iwl_cfg iwl7260_2n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl7260_n_cfg = { @@ -158,6 +161,7 @@ const struct iwl_cfg iwl7260_n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl3160_2ac_cfg = { @@ -167,6 +171,7 @@ const struct iwl_cfg iwl3160_2ac_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl3160_2n_cfg = { @@ -176,6 +181,7 @@ const struct iwl_cfg iwl3160_2n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl3160_n_cfg = { @@ -185,6 +191,7 @@ const struct iwl_cfg iwl3160_n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl7265_2ac_cfg = { @@ -196,5 +203,23 @@ const struct iwl_cfg iwl7265_2ac_cfg = { .nvm_calib_ver = IWL7265_TX_POWER_VERSION, }; +const struct iwl_cfg iwl7265_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl7265_n_cfg = { + .name = "Intel(R) Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, +}; + MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 18f232e8e812..0b916249669e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -129,6 +129,12 @@ enum iwl_led_mode { #define ANT_BC (ANT_B | ANT_C) #define ANT_ABC (ANT_A | ANT_B | ANT_C) +static inline u8 num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} /* * @max_ll_items: max number of OTP blocks @@ -207,6 +213,8 @@ struct iwl_eeprom_params { * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device * @high_temp: Is this NIC is designated to be in high temperature. + * @host_interrupt_operation_mode: device needs host interrupt operation + * mode set * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -235,6 +243,7 @@ struct iwl_cfg { enum iwl_led_mode led_mode; const bool rx_with_siso_diversity; const bool internal_wimax_coex; + const bool host_interrupt_operation_mode; bool high_temp; }; @@ -294,6 +303,8 @@ extern const struct iwl_cfg iwl3160_2ac_cfg; extern const struct iwl_cfg iwl3160_2n_cfg; extern const struct iwl_cfg iwl3160_n_cfg; extern const struct iwl_cfg iwl7265_2ac_cfg; +extern const struct iwl_cfg iwl7265_2n_cfg; +extern const struct iwl_cfg iwl7265_n_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 54a4fdc631b7..da4eca8b3007 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -495,14 +495,11 @@ enum secure_load_status_reg { * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit * * default interrupt coalescing timer is 64 x 32 = 2048 usecs - * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs */ #define IWL_HOST_INT_TIMEOUT_MAX (0xFF) #define IWL_HOST_INT_TIMEOUT_DEF (0x40) #define IWL_HOST_INT_TIMEOUT_MIN (0x0) -#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) -#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) -#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) +#define IWL_HOST_INT_OPER_MODE BIT(31) /***************************************************************************** * 7000/3000 series SHR DTS addresses * diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 2fab203d3027..94aef22df73a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -283,7 +283,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - if (data->valid_rx_ant == 1 || cfg->rx_with_siso_diversity) { + if (num_of_ant(data->valid_rx_ant) == 1 || + cfg->rx_with_siso_diversity) { vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; /* this works because NOT_SUPPORTED == 3 */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index a70c7b9d9bad..f6412dae2659 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -102,6 +102,9 @@ /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C +/* Device NMI register */ +#define DEVICE_SET_NMI_REG 0x00a01c30 + /***************************************************************************** * 7000/3000 series SHR DTS addresses * *****************************************************************************/ diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 6d73817850ce..285d8c7486f5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -5,6 +5,7 @@ iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o power_legacy.o bt-coex.o iwlmvm-y += led.o tt.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o +iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 5d066cbc5ac7..d126245c48de 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -391,13 +391,13 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) BT_VALID_LUT | BT_VALID_WIFI_RX_SW_PRIO_BOOST | BT_VALID_WIFI_TX_SW_PRIO_BOOST | - BT_VALID_MULTI_PRIO_LUT | BT_VALID_CORUN_LUT_20 | BT_VALID_CORUN_LUT_40 | BT_VALID_ANT_ISOLATION | BT_VALID_ANT_ISOLATION_THRS | BT_VALID_TXTX_DELTA_FREQ_THRS | - BT_VALID_TXRX_MAX_FREQ_0); + BT_VALID_TXRX_MAX_FREQ_0 | + BT_VALID_SYNC_TO_SCO); if (mvm->cfg->bt_shared_single_ant) memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, @@ -515,7 +515,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, if (IS_ERR_OR_NULL(sta)) return 0; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); /* nothing to do */ if (mvmsta->bt_reduced_txpower == enable) @@ -842,7 +842,12 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], lockdep_is_held(&mvm->mutex)); - mvmsta = (void *)sta->drv_priv; + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); data->num_bss_ifaces++; @@ -913,11 +918,11 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum iwl_bt_coex_lut_type lut_type; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_LOW_TRAFFIC) + BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; lut_type = iwl_get_coex_type(mvm, mvmsta->vif); @@ -932,7 +937,7 @@ u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 6f45966817bb..665f87e788d6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -895,7 +895,7 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, /* new API returns next, not last-used seqno */ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) - err -= 0x10; + err = (u16) (err - 0x10); } iwl_free_resp(&cmd); @@ -1216,10 +1216,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (len >= sizeof(u32) * 2) { mvm->d3_test_pme_ptr = le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); - } else if (test) { - /* in test mode we require the pointer */ - ret = -EIO; - goto out; } #endif iwl_free_resp(&d3_cfg_cmd); @@ -1231,10 +1227,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm->aux_sta.sta_id = old_aux_sta_id; mvm_ap_sta->sta_id = old_ap_sta_id; mvmvif->ap_sta_id = old_ap_sta_id; - out_noreset: - kfree(key_data.rsc_tsc); + if (ret < 0) ieee80211_restart_hw(mvm->hw); + out_noreset: + kfree(key_data.rsc_tsc); mutex_unlock(&mvm->mutex); @@ -1537,10 +1534,16 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, struct iwl_mvm_d3_gtk_iter_data gtkdata = { .status = status, }; + u32 disconnection_reasons = + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; if (!status || !vif->bss_conf.bssid) return false; + if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons) + return false; + /* find last GTK that we used initially, if any */ gtkdata.find_phase = true; ieee80211_iter_keys(mvm->hw, vif, @@ -1549,7 +1552,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, if (gtkdata.unhandled_cipher) return false; if (!gtkdata.num_keys) - return true; + goto out; if (!gtkdata.last_gtk) return false; @@ -1600,6 +1603,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, (void *)&replay_ctr, GFP_KERNEL); } +out: mvmvif->seqno_valid = true; /* +0x10 because the set API expects next-to-use, not last-used */ mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; @@ -1804,6 +1808,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) iwl_mvm_read_d3_sram(mvm); keep = iwl_mvm_query_wakeup_reasons(mvm, vif); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (keep) + mvm->keep_vif = vif; +#endif /* has unlocked the mutex, so skip that */ goto out; @@ -1860,6 +1868,7 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) return err; } mvm->d3_test_active = true; + mvm->keep_vif = NULL; return 0; } @@ -1870,10 +1879,14 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, u32 pme_asserted; while (true) { - pme_asserted = iwl_trans_read_mem32(mvm->trans, - mvm->d3_test_pme_ptr); - if (pme_asserted) - break; + /* read pme_ptr if available */ + if (mvm->d3_test_pme_ptr) { + pme_asserted = iwl_trans_read_mem32(mvm->trans, + mvm->d3_test_pme_ptr); + if (pme_asserted) + break; + } + if (msleep_interruptible(100)) break; } @@ -1884,6 +1897,10 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) { + /* skip the one we keep connection on */ + if (_data == vif) + return; + if (vif->type == NL80211_IFTYPE_STATION) ieee80211_connection_loss(vif); } @@ -1910,7 +1927,7 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_test_disconn_work_iter, NULL); + iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif); ieee80211_wake_queues(mvm->hw); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c new file mode 100644 index 000000000000..67f6a2071653 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -0,0 +1,190 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "mvm.h" +#include "debugfs.h" + +static ssize_t iwl_dbgfs_mac_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u8 ap_sta_id; + struct ieee80211_chanctx_conf *chanctx_conf; + char buf[512]; + int bufsz = sizeof(buf); + int pos = 0; + int i; + + mutex_lock(&mvm->mutex); + + ap_sta_id = mvmvif->ap_sta_id; + + pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", + mvmvif->id, mvmvif->color); + pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", + vif->bss_conf.bssid); + pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); + for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) + pos += scnprintf(buf+pos, bufsz-pos, + "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", + i, mvmvif->queue_params[i].txop, + mvmvif->queue_params[i].cw_min, + mvmvif->queue_params[i].cw_max, + mvmvif->queue_params[i].aifs, + mvmvif->queue_params[i].uapsd); + + if (vif->type == NL80211_IFTYPE_STATION && + ap_sta_id != IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvm_sta; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], + lockdep_is_held(&mvm->mutex)); + mvm_sta = (void *)sta->drv_priv; + pos += scnprintf(buf+pos, bufsz-pos, + "ap_sta_id %d - reduced Tx power %d\n", + ap_sta_id, mvm_sta->bt_reduced_txpower); + } + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (chanctx_conf) + pos += scnprintf(buf+pos, bufsz-pos, + "idle rx chains %d, active rx chains: %d\n", + chanctx_conf->rx_chains_static, + chanctx_conf->rx_chains_dynamic); + rcu_read_unlock(); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, vif, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) + +MVM_DEBUGFS_READ_FILE_OPS(mac_params); + +void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct dentry *dbgfs_dir = vif->debugfs_dir; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[100]; + + /* + * Check if debugfs directory already exist before creating it. + * This may happen when, for example, resetting hw or suspend-resume + */ + if (!dbgfs_dir || mvmvif->dbgfs_dir) + return; + + mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); + mvmvif->mvm = mvm; + + if (!mvmvif->dbgfs_dir) { + IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", + dbgfs_dir->d_name.name); + return; + } + + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, + S_IRUSR); + + /* + * Create symlink for convenience pointing to interface specific + * debugfs entries for the driver. For example, under + * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ + * find + * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ + */ + snprintf(buf, 100, "../../../%s/%s/%s/%s", + dbgfs_dir->d_parent->d_parent->d_name.name, + dbgfs_dir->d_parent->d_name.name, + dbgfs_dir->d_name.name, + mvmvif->dbgfs_dir->d_name.name); + + mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, + mvm->debugfs_dir, buf); + if (!mvmvif->dbgfs_slink) + IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", + dbgfs_dir->d_name.name); + return; +err: + IWL_ERR(mvm, "Can't create debugfs entity\n"); +} + +void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + debugfs_remove(mvmvif->dbgfs_slink); + mvmvif->dbgfs_slink = NULL; + + debugfs_remove_recursive(mvmvif->dbgfs_dir); + mvmvif->dbgfs_dir = NULL; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 9864d713eb2c..e8f62a6a1b57 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -63,30 +63,18 @@ #include "mvm.h" #include "sta.h" #include "iwl-io.h" +#include "iwl-prph.h" +#include "debugfs.h" -struct iwl_dbgfs_mvm_ctx { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; -}; - -static ssize_t iwl_dbgfs_tx_flush_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - - char buf[16]; - int buf_size, ret; + int ret; u32 scd_q_msk; if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) return -EIO; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &scd_q_msk) != 1) return -EINVAL; @@ -99,26 +87,21 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct file *file, return ret; } -static ssize_t iwl_dbgfs_sta_drain_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; struct ieee80211_sta *sta; - - char buf[8]; - int buf_size, sta_id, drain, ret; + int sta_id, drain, ret; if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) return -EIO; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) return -EINVAL; + if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT) + return -EINVAL; + if (drain < 0 || drain > 1) + return -EINVAL; mutex_lock(&mvm->mutex); @@ -190,20 +173,11 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, return ret; } -static ssize_t iwl_dbgfs_sram_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) +static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[64]; - int buf_size; u32 offset, len; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { if ((offset & 0x3) || (len & 0x3)) return -EINVAL; @@ -263,22 +237,14 @@ static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_disable_power_off_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[64] = {}; - int ret; - int val; + int ret, val; if (!mvm->ucode_loaded) return -EIO; - count = min_t(size_t, count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - if (!strncmp("disable_power_off_d0=", buf, 21)) { if (sscanf(buf + 21, "%d", &val) != 1) return -EINVAL; @@ -298,212 +264,6 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct file *file, return ret ?: count; } -static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_dbgfs_pm_mask param, int val) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; - - dbgfs_pm->mask |= param; - - switch (param) { - case MVM_DEBUGFS_PM_KEEP_ALIVE: { - struct ieee80211_hw *hw = mvm->hw; - int dtimper = hw->conf.ps_dtim_period ?: 1; - int dtimper_msec = dtimper * vif->bss_conf.beacon_int; - - IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); - if (val * MSEC_PER_SEC < 3 * dtimper_msec) { - IWL_WARN(mvm, - "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", - val * MSEC_PER_SEC, 3 * dtimper_msec); - } - dbgfs_pm->keep_alive_seconds = val; - break; - } - case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: - IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", - val ? "enabled" : "disabled"); - dbgfs_pm->skip_over_dtim = val; - break; - case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: - IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); - dbgfs_pm->skip_dtim_periods = val; - break; - case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); - dbgfs_pm->rx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); - dbgfs_pm->tx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: - IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); - dbgfs_pm->disable_power_off = val; - break; - case MVM_DEBUGFS_PM_LPRX_ENA: - IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); - dbgfs_pm->lprx_ena = val; - break; - case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: - IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); - dbgfs_pm->lprx_rssi_threshold = val; - break; - case MVM_DEBUGFS_PM_SNOOZE_ENABLE: - IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); - dbgfs_pm->snooze_ena = val; - break; - } -} - -static ssize_t iwl_dbgfs_pm_params_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - enum iwl_dbgfs_pm_mask param; - char buf[32] = {}; - int val; - int ret; - - count = min_t(size_t, count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - if (!strncmp("keep_alive=", buf, 11)) { - if (sscanf(buf + 11, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_KEEP_ALIVE; - } else if (!strncmp("skip_over_dtim=", buf, 15)) { - if (sscanf(buf + 15, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; - } else if (!strncmp("skip_dtim_periods=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; - } else if (!strncmp("rx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; - } else if (!strncmp("tx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; - } else if (!strncmp("disable_power_off=", buf, 18) && - !(mvm->fw->ucode_capa.flags & - IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; - } else if (!strncmp("lprx=", buf, 5)) { - if (sscanf(buf + 5, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_ENA; - } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { - if (sscanf(buf + 20, "%d", &val) != 1) - return -EINVAL; - if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < - POWER_LPRX_RSSI_THRESHOLD_MIN) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; - } else if (!strncmp("snooze_enable=", buf, 14)) { - if (sscanf(buf + 14, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_pm(mvm, vif, param, val); - ret = iwl_mvm_power_update_mode(mvm, vif); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_pm_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - char buf[512]; - int bufsz = sizeof(buf); - int pos; - - pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_mac_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - u8 ap_sta_id; - struct ieee80211_chanctx_conf *chanctx_conf; - char buf[512]; - int bufsz = sizeof(buf); - int pos = 0; - int i; - - mutex_lock(&mvm->mutex); - - ap_sta_id = mvmvif->ap_sta_id; - - pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", - mvmvif->id, mvmvif->color); - pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", - vif->bss_conf.bssid); - pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); - for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) { - pos += scnprintf(buf+pos, bufsz-pos, - "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", - i, mvmvif->queue_params[i].txop, - mvmvif->queue_params[i].cw_min, - mvmvif->queue_params[i].cw_max, - mvmvif->queue_params[i].aifs, - mvmvif->queue_params[i].uapsd); - } - - if (vif->type == NL80211_IFTYPE_STATION && - ap_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvm_sta; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], - lockdep_is_held(&mvm->mutex)); - mvm_sta = (void *)sta->drv_priv; - pos += scnprintf(buf+pos, bufsz-pos, - "ap_sta_id %d - reduced Tx power %d\n", - ap_sta_id, mvm_sta->bt_reduced_txpower); - } - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf) { - pos += scnprintf(buf+pos, bufsz-pos, - "idle rx chains %d, active rx chains: %d\n", - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); - } - rcu_read_unlock(); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - #define BT_MBOX_MSG(_notif, _num, _field) \ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) @@ -779,11 +539,9 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, } #undef PRINT_STAT_LE32 -static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; int ret; mutex_lock(&mvm->mutex); @@ -800,6 +558,14 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + iwl_write_prph(mvm->trans, DEVICE_SET_NMI_REG, 1); + + return count; +} + static ssize_t iwl_dbgfs_scan_ant_rxchain_read(struct file *file, char __user *user_buf, @@ -824,21 +590,11 @@ iwl_dbgfs_scan_ant_rxchain_read(struct file *file, } static ssize_t -iwl_dbgfs_scan_ant_rxchain_write(struct file *file, - const char __user *user_buf, +iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[8]; - int buf_size; u8 scan_rx_ant; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - - /* get the argument from the user and check if it is valid */ - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) return -EINVAL; if (scan_rx_ant > ANT_ABC) @@ -846,228 +602,17 @@ iwl_dbgfs_scan_ant_rxchain_write(struct file *file, if (scan_rx_ant & ~iwl_fw_valid_rx_ant(mvm->fw)) return -EINVAL; - /* change the rx antennas for scan command */ mvm->scan_rx_ant = scan_rx_ant; return count; } - -static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, - enum iwl_dbgfs_bf_mask param, int value) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - dbgfs_bf->mask |= param; - - switch (param) { - case MVM_DEBUGFS_BF_ENERGY_DELTA: - dbgfs_bf->bf_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: - dbgfs_bf->bf_roaming_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_STATE: - dbgfs_bf->bf_roaming_state = value; - break; - case MVM_DEBUGFS_BF_TEMP_THRESHOLD: - dbgfs_bf->bf_temp_threshold = value; - break; - case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: - dbgfs_bf->bf_temp_fast_filter = value; - break; - case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: - dbgfs_bf->bf_temp_slow_filter = value; - break; - case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: - dbgfs_bf->bf_enable_beacon_filter = value; - break; - case MVM_DEBUGFS_BF_DEBUG_FLAG: - dbgfs_bf->bf_debug_flag = value; - break; - case MVM_DEBUGFS_BF_ESCAPE_TIMER: - dbgfs_bf->bf_escape_timer = value; - break; - case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: - dbgfs_bf->ba_enable_beacon_abort = value; - break; - case MVM_DEBUGFS_BA_ESCAPE_TIMER: - dbgfs_bf->ba_escape_timer = value; - break; - } -} - -static ssize_t iwl_dbgfs_bf_params_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - enum iwl_dbgfs_bf_mask param; - char buf[256]; - int buf_size; - int value; - int ret = 0; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (!strncmp("bf_energy_delta=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ENERGY_DELTA_MIN || - value > IWL_BF_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || - value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_state=", buf, 17)) { - if (sscanf(buf+17, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_STATE_MIN || - value > IWL_BF_ROAMING_STATE_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_STATE; - } else if (!strncmp("bf_temp_threshold=", buf, 18)) { - if (sscanf(buf+18, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_THRESHOLD_MIN || - value > IWL_BF_TEMP_THRESHOLD_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; - } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_FAST_FILTER_MIN || - value > IWL_BF_TEMP_FAST_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; - } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || - value > IWL_BF_TEMP_SLOW_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; - } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; - } else if (!strncmp("bf_debug_flag=", buf, 14)) { - if (sscanf(buf+14, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_DEBUG_FLAG; - } else if (!strncmp("bf_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ESCAPE_TIMER_MIN || - value > IWL_BF_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ESCAPE_TIMER; - } else if (!strncmp("ba_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BA_ESCAPE_TIMER_MIN || - value > IWL_BA_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BA_ESCAPE_TIMER; - } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { - if (sscanf(buf+23, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_bf(vif, param, value); - if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { - ret = iwl_mvm_disable_beacon_filter(mvm, vif); - } else { - ret = iwl_mvm_enable_beacon_filter(mvm, vif); - } - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_bf_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = - cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), - .ba_enable_beacon_abort = - cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), - }; - - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - if (mvmvif->bf_data.bf_enabled) - cmd.bf_enable_beacon_filter = cpu_to_le32(1); - else - cmd.bf_enable_beacon_filter = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", - le32_to_cpu(cmd.bf_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", - le32_to_cpu(cmd.bf_roaming_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", - le32_to_cpu(cmd.bf_roaming_state)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", - le32_to_cpu(cmd.bf_temp_threshold)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", - le32_to_cpu(cmd.bf_temp_fast_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", - le32_to_cpu(cmd.bf_temp_slow_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", - le32_to_cpu(cmd.bf_enable_beacon_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", - le32_to_cpu(cmd.bf_debug_flag)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", - le32_to_cpu(cmd.bf_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", - le32_to_cpu(cmd.ba_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", - le32_to_cpu(cmd.ba_enable_beacon_abort)); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - #ifdef CONFIG_PM_SLEEP -static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; int store; - count = min_t(size_t, count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - if (sscanf(buf, "%d", &store) != 1) return -EINVAL; @@ -1120,61 +665,33 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, } #endif -#define MVM_DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -} - -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define MVM_DEBUGFS_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) #define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, mvm, \ &iwl_dbgfs_##name##_ops)) \ goto err; \ } while (0) -#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, vif, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) - /* Device wide debugfs entries */ -MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush); -MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram); +MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); +MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); MVM_DEBUGFS_READ_FILE_OPS(stations); MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); #ifdef CONFIG_PM_SLEEP -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); #endif -/* Interface specific debugfs entries */ -MVM_DEBUGFS_READ_FILE_OPS(mac_params); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params); - int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) { char buf[100]; @@ -1192,6 +709,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, S_IWUSR | S_IRUSR); #ifdef CONFIG_PM_SLEEP @@ -1202,6 +720,19 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) goto err; #endif + if (!debugfs_create_blob("nvm_hw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_hw_blob)) + goto err; + if (!debugfs_create_blob("nvm_sw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_sw_blob)) + goto err; + if (!debugfs_create_blob("nvm_calib", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_calib_blob)) + goto err; + if (!debugfs_create_blob("nvm_prod", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_prod_blob)) + goto err; + /* * Create a symlink with mac80211. It will be removed when mac80211 * exists (before the opmode exists which removes the target.) @@ -1217,72 +748,3 @@ err: IWL_ERR(mvm, "Can't create the mvm debugfs directory\n"); return -ENOMEM; } - -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct dentry *dbgfs_dir = vif->debugfs_dir; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[100]; - - /* - * Check if debugfs directory already exist before creating it. - * This may happen when, for example, resetting hw or suspend-resume - */ - if (!dbgfs_dir || mvmvif->dbgfs_dir) - return; - - mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); - mvmvif->dbgfs_data = mvm; - - if (!mvmvif->dbgfs_dir) { - IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", - dbgfs_dir->d_name.name); - return; - } - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p) - MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | - S_IRUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, - S_IRUSR); - - if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && - mvmvif == mvm->bf_allowed_vif) - MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - /* - * Create symlink for convenience pointing to interface specific - * debugfs entries for the driver. For example, under - * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ - * find - * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ - */ - snprintf(buf, 100, "../../../%s/%s/%s/%s", - dbgfs_dir->d_parent->d_parent->d_name.name, - dbgfs_dir->d_parent->d_name.name, - dbgfs_dir->d_name.name, - mvmvif->dbgfs_dir->d_name.name); - - mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, - mvm->debugfs_dir, buf); - if (!mvmvif->dbgfs_slink) - IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", - dbgfs_dir->d_name.name); - return; -err: - IWL_ERR(mvm, "Can't create debugfs entity\n"); -} - -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - debugfs_remove(mvmvif->dbgfs_slink); - mvmvif->dbgfs_slink = NULL; - - debugfs_remove_recursive(mvmvif->dbgfs_dir); - mvmvif->dbgfs_dir = NULL; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h new file mode 100644 index 000000000000..85f9f958bfd2 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#define MVM_DEBUGFS_READ_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + +#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + argtype *arg = file->private_data; \ + char buf[buflen] = {}; \ + size_t buf_size = min(count, sizeof(buf) - 1); \ + \ + if (copy_from_user(buf, user_buf, buf_size)) \ + return -EFAULT; \ + \ + return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \ +} \ + +#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h index 4ea5e24ca92d..af500996bbf1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h @@ -127,6 +127,7 @@ enum iwl_bt_coex_valid_bit_msk { BT_VALID_ANT_ISOLATION_THRS = BIT(15), BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), + BT_VALID_SYNC_TO_SCO = BIT(18), }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h index 538f1c7a5966..532312c7b937 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h @@ -281,8 +281,31 @@ enum { /* # entries in rate scale table to support Tx retries */ #define LQ_MAX_RETRY_NUM 16 -/* Link quality command flags, only this one is available */ -#define LQ_FLAG_SET_STA_TLC_RTS_MSK BIT(0) +/* Link quality command flags bit fields */ + +/* Bit 0: (0) Don't use RTS (1) Use RTS */ +#define LQ_FLAG_USE_RTS_POS 0 +#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) + +/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ +#define LQ_FLAG_COLOR_POS 1 +#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) + +/* Bit 4-5: Tx RTS BW Signalling + * (0) No RTS BW signalling + * (1) Static BW signalling + * (2) Dynamic BW signalling + */ +#define LQ_FLAG_RTS_BW_SIG_POS 4 +#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) + +/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection + * Dyanmic BW selection allows Tx with narrower BW then requested in rates + */ +#define LQ_FLAG_DYNAMIC_BW_POS 6 +#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) /** * struct iwl_lq_cmd - link quality command diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index c3782b48ded1..b3ed59237cba 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -530,14 +530,13 @@ struct iwl_scan_offload_schedule { /* * iwl_scan_offload_flags * - * IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID: filter mode - upload every beacon or match - * ssid list. + * IWL_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering. * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan. * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan * on A band. */ enum iwl_scan_offload_flags { - IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID = BIT(0), + IWL_SCAN_OFFLOAD_FLAG_PASS_ALL = BIT(0), IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL = BIT(2), IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN = BIT(3), }; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index f41f9b079831..fb93961da750 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -488,6 +488,40 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, *ofdm_rates = ofdm; } +static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_ctx_cmd *cmd) +{ + /* for both sta and ap, ht_operation_mode hold the protection_mode */ + u8 protection_mode = vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + /* The fw does not distinguish between ht and fat */ + u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; + + IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); + /* + * See section 9.23.3.1 of IEEE 80211-2012. + * Nongreenfield HT STAs Present is not supported. + */ + switch (protection_mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* Protect when channel wider than 20MHz */ + if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + default: + IWL_ERR(mvm, "Illegal protection mode %d\n", + protection_mode); + break; + } +} + static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_ctx_cmd *cmd, @@ -495,6 +529,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_chanctx_conf *chanctx; + bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION); u8 cck_ack_rates, ofdm_ack_rates; int i; @@ -573,16 +609,13 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN); } - - /* - * I think that we should enable these 2 flags regardless the HT PROT - * fields in the HT IE, but I am not sure. Someone knows whom to ask?... - */ - if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { + IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", + vif->bss_conf.use_cts_prot, + vif->bss_conf.ht_operation_mode); + if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_HT_PROT | - MAC_PROT_FLG_FAT_PROT); - } + if (ht_enabled) + iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index b56c989ad784..afc4419be46d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -256,7 +256,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) } hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | - NL80211_FEATURE_P2P_GO_OPPPS; + NL80211_FEATURE_P2P_GO_OPPPS | + NL80211_FEATURE_LOW_PRIORITY_SCAN; mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; @@ -990,6 +991,17 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, struct ieee80211_bss_conf *bss_conf, u32 changes) { + enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT | + BSS_CHANGED_BANDWIDTH; + int ret; + + if (changes & ht_change) { + ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + if (ret) + IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + } + /* Need to send a new beacon template to the FW */ if (changes & BSS_CHANGED_BEACON) { if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) @@ -1080,7 +1092,7 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); switch (cmd) { case STA_NOTIFY_SLEEP: @@ -1149,7 +1161,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = iwl_mvm_update_sta(mvm, vif, sta); if (ret == 0) iwl_mvm_rs_rate_init(mvm, sta, - mvmvif->phy_ctxt->channel->band); + mvmvif->phy_ctxt->channel->band, + true); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { /* enable beacon filtering */ diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fed21ef4162d..7dc57cfe5803 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -323,9 +323,9 @@ struct iwl_mvm_vif { #endif #ifdef CONFIG_IWLWIFI_DEBUGFS + struct iwl_mvm *mvm; struct dentry *dbgfs_dir; struct dentry *dbgfs_slink; - void *dbgfs_data; struct iwl_dbgfs_pm dbgfs_pm; struct iwl_dbgfs_bf dbgfs_bf; #endif @@ -494,6 +494,11 @@ struct iwl_mvm { u32 dbgfs_sram_offset, dbgfs_sram_len; bool disable_power_off; bool disable_power_off_d3; + + struct debugfs_blob_wrapper nvm_hw_blob; + struct debugfs_blob_wrapper nvm_sw_blob; + struct debugfs_blob_wrapper nvm_calib_blob; + struct debugfs_blob_wrapper nvm_prod_blob; #endif struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; @@ -531,6 +536,7 @@ struct iwl_mvm { bool store_d3_resume_sram; void *d3_resume_sram; u32 d3_test_pme_ptr; + struct ieee80211_vif *keep_vif; #endif #endif @@ -750,8 +756,7 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #endif /* CONFIG_IWLWIFI_DEBUGFS */ /* rate scaling */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - u8 flags, bool init); +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); /* power managment */ static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 2beffd028b67..48089b1625ff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -443,6 +443,29 @@ int iwl_nvm_init(struct iwl_mvm *mvm) } mvm->nvm_sections[section].data = temp; mvm->nvm_sections[section].length = ret; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + switch (section) { + case NVM_SECTION_TYPE_HW: + mvm->nvm_hw_blob.data = temp; + mvm->nvm_hw_blob.size = ret; + break; + case NVM_SECTION_TYPE_SW: + mvm->nvm_sw_blob.data = temp; + mvm->nvm_sw_blob.size = ret; + break; + case NVM_SECTION_TYPE_CALIBRATION: + mvm->nvm_calib_blob.data = temp; + mvm->nvm_calib_blob.size = ret; + break; + case NVM_SECTION_TYPE_PRODUCTION: + mvm->nvm_prod_blob.data = temp; + mvm->nvm_prod_blob.size = ret; + break; + default: + WARN(1, "section: %d", section); + } +#endif } kfree(nvm_buffer); if (ret < 0) diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 17e2bc827f9a..38165eba2a17 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -217,8 +217,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) } else { cmd.quotas[idx].quota = cpu_to_le32(quota * data.n_interfaces[i]); - cmd.quotas[idx].max_duration = - cpu_to_le32(IWL_MVM_MAX_QUOTA); + cmd.quotas[idx].max_duration = cpu_to_le32(0); } idx++; } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index a0b4cc8d9c3b..bf6e29f5b4d0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -47,28 +47,25 @@ #define IWL_HT_NUMBER_TRY 3 #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */ #define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ /* max allowed rate miss before sync LQ cmd */ #define IWL_MISSED_RATE_MAX 15 -/* max time to accum history 2 seconds */ -#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) +#define RS_STAY_IN_COLUMN_TIMEOUT (5*HZ) + static u8 rs_ht_to_legacy[] = { - [IWL_RATE_1M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_2M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_5M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_11M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_6M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_9M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_12M_INDEX] = IWL_RATE_9M_INDEX, - [IWL_RATE_18M_INDEX] = IWL_RATE_12M_INDEX, - [IWL_RATE_24M_INDEX] = IWL_RATE_18M_INDEX, - [IWL_RATE_36M_INDEX] = IWL_RATE_24M_INDEX, - [IWL_RATE_48M_INDEX] = IWL_RATE_36M_INDEX, - [IWL_RATE_54M_INDEX] = IWL_RATE_48M_INDEX, - [IWL_RATE_60M_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, + [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, + [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, + [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, + [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, + [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, + [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, }; static const u8 ant_toggle_lookup[] = { @@ -126,6 +123,190 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ }; +enum rs_column_mode { + RS_INVALID = 0, + RS_LEGACY, + RS_SISO, + RS_MIMO2, +}; + +#define MAX_NEXT_COLUMNS 5 +#define MAX_COLUMN_CHECKS 3 + +typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl); + +struct rs_tx_column { + enum rs_column_mode mode; + u8 ant; + bool sgi; + enum rs_column next_columns[MAX_NEXT_COLUMNS]; + allow_column_func_t checks[MAX_COLUMN_CHECKS]; +}; + +static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + if (!sta->ht_cap.ht_supported) + return false; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return false; + + if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 2) + return false; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + return false; + + return true; +} + +static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + if (!sta->ht_cap.ht_supported) + return false; + + return true; +} + +static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + struct rs_rate *rate = &tbl->rate; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (is_ht20(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + return true; + if (is_ht40(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + return true; + if (is_ht80(rate) && (vht_cap->cap & + IEEE80211_VHT_CAP_SHORT_GI_80)) + return true; + + return false; +} + +static const struct rs_tx_column rs_tx_columns[] = { + [RS_COLUMN_LEGACY_ANT_A] = { + .mode = RS_LEGACY, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + }, + [RS_COLUMN_LEGACY_ANT_B] = { + .mode = RS_LEGACY, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + }, + [RS_COLUMN_SISO_ANT_A] = { + .mode = RS_SISO, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B] = { + .mode = RS_SISO, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + }, + }, + [RS_COLUMN_SISO_ANT_A_SGI] = { + .mode = RS_SISO, + .ant = ANT_A, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B_SGI] = { + .mode = RS_SISO, + .ant = ANT_B, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_MIMO2] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + }, + }, + [RS_COLUMN_MIMO2_SGI] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + rs_sgi_allow, + }, + }, +}; + static inline u8 rs_extract_rate(u32 rate_n_flags) { /* also works for HT because bits 7:6 are zero there */ @@ -175,7 +356,6 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, u32 rate_n_flags); static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - #ifdef CONFIG_MAC80211_DEBUGFS static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u32 *rate_n_flags); @@ -264,6 +444,52 @@ static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { #define MCS_INDEX_PER_STREAM (8) +static const char *rs_pretty_ant(u8 ant) +{ + static const char * const ant_name[] = { + [ANT_NONE] = "None", + [ANT_A] = "A", + [ANT_B] = "B", + [ANT_AB] = "AB", + [ANT_C] = "C", + [ANT_AC] = "AC", + [ANT_BC] = "BC", + [ANT_ABC] = "ABC", + }; + + if (ant > ANT_ABC) + return "UNKNOWN"; + + return ant_name[ant]; +} + +static const char *rs_pretty_lq_type(enum iwl_table_type type) +{ + static const char * const lq_types[] = { + [LQ_NONE] = "NONE", + [LQ_LEGACY_A] = "LEGACY_A", + [LQ_LEGACY_G] = "LEGACY_G", + [LQ_HT_SISO] = "HT SISO", + [LQ_HT_MIMO2] = "HT MIMO", + [LQ_VHT_SISO] = "VHT SISO", + [LQ_VHT_MIMO2] = "VHT MIMO", + }; + + if (type < LQ_NONE || type >= LQ_MAX) + return "UNKNOWN"; + + return lq_types[type]; +} + +static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, + const char *prefix) +{ + IWL_DEBUG_RATE(mvm, "%s: (%s: %d) ANT: %s BW: %d SGI: %d\n", + prefix, rs_pretty_lq_type(rate->type), + rate->index, rs_pretty_ant(rate->ant), + rate->bw, rate->sgi); +} + static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) { window->data = 0; @@ -271,7 +497,6 @@ static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) window->success_ratio = IWL_INVALID_VALUE; window->counter = 0; window->average_tpt = IWL_INVALID_VALUE; - window->stamp = 0; } static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) @@ -298,7 +523,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, if (lq_sta->dbg_fixed_rate) { rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); + iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false); } } #endif @@ -428,192 +653,174 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, else window->average_tpt = IWL_INVALID_VALUE; - /* Tag this window as having been updated */ - window->stamp = jiffies; - return 0; } -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -/* FIXME:RS:remove this function and put the flags statically in the table */ -static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, int index) +/* Convert rs_rate object into ucode rate bitmask */ +static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, + struct rs_rate *rate) { - u32 rate_n_flags = 0; + u32 ucode_rate = 0; + int index = rate->index; - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & RATE_MCS_ANT_ABC_MSK); - if (is_legacy(tbl->lq_type)) { - rate_n_flags |= iwl_rates[index].plcp; + if (is_legacy(rate)) { + ucode_rate |= iwl_rates[index].plcp; if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - return rate_n_flags; + ucode_rate |= RATE_MCS_CCK_MSK; + return ucode_rate; } - if (is_ht(tbl->lq_type)) { + if (is_ht(rate)) { if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { IWL_ERR(mvm, "Invalid HT rate index %d\n", index); index = IWL_LAST_HT_RATE; } - rate_n_flags |= RATE_MCS_HT_MSK; + ucode_rate |= RATE_MCS_HT_MSK; - if (is_ht_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_ht_siso; - else if (is_ht_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_ht_mimo2; + if (is_ht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_siso; + else if (is_ht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_mimo2; else WARN_ON_ONCE(1); - } else if (is_vht(tbl->lq_type)) { + } else if (is_vht(rate)) { if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); index = IWL_LAST_VHT_RATE; } - rate_n_flags |= RATE_MCS_VHT_MSK; - if (is_vht_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_vht_siso; - else if (is_vht_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_vht_mimo2; + ucode_rate |= RATE_MCS_VHT_MSK; + if (is_vht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_siso; + else if (is_vht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_mimo2; else WARN_ON_ONCE(1); } else { - IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type); + IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); } - rate_n_flags |= tbl->bw; - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; + ucode_rate |= rate->bw; + if (rate->sgi) + ucode_rate |= RATE_MCS_SGI_MSK; - return rate_n_flags; + return ucode_rate; } -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct iwl_scale_tbl_info *tbl, - int *rate_idx) +/* Convert a ucode rate into an rs_rate object */ +static int rs_rate_from_ucode_rate(const u32 ucode_rate, + enum ieee80211_band band, + struct rs_rate *rate) { - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); + u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; + u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); u8 nss; - memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win)); - *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); + memset(rate, 0, sizeof(struct rs_rate)); + rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - if (*rate_idx == IWL_RATE_INVALID) { - *rate_idx = -1; + if (rate->index == IWL_RATE_INVALID) { + rate->index = -1; return -EINVAL; } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->bw = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - tbl->max_search = IWL_MAX_SEARCH; + + rate->ant = (ant_msk >> RATE_MCS_ANT_POS); /* Legacy */ - if (!(rate_n_flags & RATE_MCS_HT_MSK) && - !(rate_n_flags & RATE_MCS_VHT_MSK)) { + if (!(ucode_rate & RATE_MCS_HT_MSK) && + !(ucode_rate & RATE_MCS_VHT_MSK)) { if (num_of_ant == 1) { if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_LEGACY_A; + rate->type = LQ_LEGACY_A; else - tbl->lq_type = LQ_LEGACY_G; + rate->type = LQ_LEGACY_G; } return 0; } /* HT or VHT */ - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; + if (ucode_rate & RATE_MCS_SGI_MSK) + rate->sgi = true; - tbl->bw = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; - if (rate_n_flags & RATE_MCS_HT_MSK) { - nss = ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >> + if (ucode_rate & RATE_MCS_HT_MSK) { + nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; if (nss == 1) { - tbl->lq_type = LQ_HT_SISO; + rate->type = LQ_HT_SISO; WARN_ON_ONCE(num_of_ant != 1); } else if (nss == 2) { - tbl->lq_type = LQ_HT_MIMO2; + rate->type = LQ_HT_MIMO2; WARN_ON_ONCE(num_of_ant != 2); } else { WARN_ON_ONCE(1); } - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + } else if (ucode_rate & RATE_MCS_VHT_MSK) { + nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; if (nss == 1) { - tbl->lq_type = LQ_VHT_SISO; + rate->type = LQ_VHT_SISO; WARN_ON_ONCE(num_of_ant != 1); } else if (nss == 2) { - tbl->lq_type = LQ_VHT_MIMO2; + rate->type = LQ_VHT_MIMO2; WARN_ON_ONCE(num_of_ant != 2); } else { WARN_ON_ONCE(1); } } - WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_160); - WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_80 && - !is_vht(tbl->lq_type)); + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && + !is_vht(rate)); return 0; } /* switch to another antenna/antennas and return 1 */ /* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct iwl_scale_tbl_info *tbl) +static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate, + struct rs_rate *rate) { u8 new_ant_type; - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + if (!rate->ant || rate->ant > ANT_ABC) return 0; - if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) + if (!rs_is_valid_ant(valid_ant, rate->ant)) return 0; - new_ant_type = ant_toggle_lookup[tbl->ant_type]; + new_ant_type = ant_toggle_lookup[rate->ant]; - while ((new_ant_type != tbl->ant_type) && + while ((new_ant_type != rate->ant) && !rs_is_valid_ant(valid_ant, new_ant_type)) new_ant_type = ant_toggle_lookup[new_ant_type]; - if (new_ant_type == tbl->ant_type) + if (new_ant_type == rate->ant) return 0; - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + rate->ant = new_ant_type; + + /* TODO: get rid of ucode_rate here. This should handle only rs_rate */ + *ucode_rate &= ~RATE_MCS_ANT_ABC_MSK; + *ucode_rate |= new_ant_type << RATE_MCS_ANT_POS; return 1; } -/** - * rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum iwl_table_type rate_type) + struct rs_rate *rate) { - if (is_legacy(rate_type)) + if (is_legacy(rate)) return lq_sta->active_legacy_rate; - else if (is_siso(rate_type)) + else if (is_siso(rate)) return lq_sta->active_siso_rate; - else if (is_mimo2(rate_type)) + else if (is_mimo2(rate)) return lq_sta->active_mimo2_rate; WARN_ON_ONCE(1); @@ -628,7 +835,7 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, /* 802.11A or ht walks to the next literal adjacent rate in * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { + if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { int i; u32 mask; @@ -677,7 +884,7 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, } static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, + struct rs_rate *rate, u8 scale_index, u8 ht_possible) { s32 low; @@ -689,30 +896,31 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, /* check if we need to switch from HT to legacy rates. * assumption is that mandatory rates (1Mbps or 6Mbps) * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { + if (!is_legacy(rate) && (!ht_possible || !scale_index)) { switch_to_legacy = 1; + WARN_ON_ONCE(scale_index < IWL_RATE_MCS_0_INDEX && + scale_index > IWL_RATE_MCS_9_INDEX); scale_index = rs_ht_to_legacy[scale_index]; if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_LEGACY_A; + rate->type = LQ_LEGACY_A; else - tbl->lq_type = LQ_LEGACY_G; + rate->type = LQ_LEGACY_G; - if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = + if (num_of_ant(rate->ant) > 1) + rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - tbl->bw = 0; - tbl->is_SGI = 0; - tbl->max_search = IWL_MAX_SEARCH; + rate->bw = RATE_MCS_CHAN_WIDTH_20; + rate->sgi = false; } - rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + rate_mask = rs_get_supported_rates(lq_sta, rate); /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { + if (is_legacy(rate)) { /* supp_rates has no CCK bits in A mode */ if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & + rate_mask = (u16)(rate_mask & (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); else rate_mask = (u16)(rate_mask & lq_sta->supp_rates); @@ -725,24 +933,22 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, } high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - tbl->lq_type); + rate->type); low = high_low & 0xff; if (low == IWL_RATE_INVALID) low = scale_index; out: - return rate_n_flags_from_tbl(lq_sta->drv, tbl, low); + rate->index = low; + return ucode_rate_from_rs_rate(lq_sta->drv, rate); } -/* - * Simple function to compare two rate scale table types - */ -static bool table_type_matches(struct iwl_scale_tbl_info *a, - struct iwl_scale_tbl_info *b) +/* Simple function to compare two rate scale table types */ +static inline bool rs_rate_match(struct rs_rate *a, + struct rs_rate *b) { - return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && - (a->is_SGI == b->is_SGI); + return (a->type == b->type) && (a->ant == b->ant) && (a->sgi == b->sgi); } static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags) @@ -766,7 +972,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, { int legacy_success; int retries; - int rs_index, mac_index, i; + int mac_index, i; struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_lq_cmd *table; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -774,13 +980,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); enum mac80211_rate_control_flags mac_flags; - u32 tx_rate; - struct iwl_scale_tbl_info tbl_type; + u32 ucode_rate; + struct rs_rate rate; struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - IWL_DEBUG_RATE_LIMIT(mvm, - "get frame ack response, update rate scale window\n"); - /* Treat uninitialized rate scaling data same as non-existing. */ if (!lq_sta) { IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); @@ -808,10 +1011,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, * to a new "search" mode (which might become the new "active" mode). */ table = &lq_sta->lq; - tx_rate = le32_to_cpu(table->rs_table[0]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, &rs_index); + ucode_rate = le32_to_cpu(table->rs_table[0]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); if (info->band == IEEE80211_BAND_5GHZ) - rs_index -= IWL_FIRST_OFDM_RATE; + rate.index -= IWL_FIRST_OFDM_RATE; mac_flags = info->status.rates[0].flags; mac_index = info->status.rates[0].idx; /* For HT packets, map MCS to PLCP */ @@ -834,19 +1037,19 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, /* Here we actually compare this rate to the latest LQ command */ if ((mac_index < 0) || - (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.bw != rs_ch_width_from_mac_flags(mac_flags)) || - (tbl_type.ant_type != info->status.antenna) || - (!!(tx_rate & RATE_MCS_HT_MSK) != + (rate.sgi != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || + (rate.bw != rs_ch_width_from_mac_flags(mac_flags)) || + (rate.ant != info->status.antenna) || + (!!(ucode_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || - (!!(tx_rate & RATE_MCS_VHT_MSK) != + (!!(ucode_rate & RATE_MCS_VHT_MSK) != !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) || - (!!(tx_rate & RATE_HT_MCS_GF_MSK) != + (!!(ucode_rate & RATE_HT_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || - (rs_index != mac_index)) { + (rate.index != mac_index)) { IWL_DEBUG_RATE(mvm, "initial rate %d does not match %d (0x%x)\n", - mac_index, rs_index, tx_rate); + mac_index, rate.index, ucode_rate); /* * Since rates mis-match, the last LQ command may have failed. * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with @@ -855,7 +1058,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + IWL_DEBUG_RATE(mvm, + "Too many rates mismatch. Send sync LQ. rs_state %d\n", + lq_sta->rs_state); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } /* Regardless, ignore this status info for outdated rate */ return; @@ -864,28 +1070,23 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter = 0; /* Figure out if rate scale algorithm is in active or search table */ - if (table_type_matches(&tbl_type, - &(lq_sta->lq_info[lq_sta->active_tbl]))) { + if (rs_rate_match(&rate, + &(lq_sta->lq_info[lq_sta->active_tbl].rate))) { curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else if (table_type_matches( - &tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + } else if (rs_rate_match(&rate, + &lq_sta->lq_info[1 - lq_sta->active_tbl].rate)) { curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); } else { IWL_DEBUG_RATE(mvm, "Neither active nor search matches tx rate\n"); tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - IWL_DEBUG_RATE(mvm, "active- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, - tmp_tbl->is_SGI); + rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - IWL_DEBUG_RATE(mvm, "search- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, - tmp_tbl->is_SGI); - IWL_DEBUG_RATE(mvm, "actual- lq:%x, ant:%x, SGI:%d\n", - tbl_type.lq_type, tbl_type.ant_type, - tbl_type.is_SGI); + rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); + rs_dump_rate(mvm, &rate, "ACTUAL"); + /* * no matching table found, let's by-pass the data collection * and continue to perform rate scale to find the rate table @@ -902,15 +1103,14 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, * first index into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { - tx_rate = le32_to_cpu(table->rs_table[0]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, - &rs_index); - rs_collect_tx_data(curr_tbl, rs_index, + ucode_rate = le32_to_cpu(table->rs_table[0]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); + rs_collect_tx_data(curr_tbl, rate.index, info->status.ampdu_len, info->status.ampdu_ack_len); /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { lq_sta->total_success += info->status.ampdu_ack_len; lq_sta->total_failed += (info->status.ampdu_len - info->status.ampdu_ack_len); @@ -927,31 +1127,36 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); /* Collect data for each rate used during failed TX attempts */ for (i = 0; i <= retries; ++i) { - tx_rate = le32_to_cpu(table->rs_table[i]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, - &tbl_type, &rs_index); + ucode_rate = le32_to_cpu(table->rs_table[i]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); /* * Only collect stats if retried rate is in the same RS * table as active/search. */ - if (table_type_matches(&tbl_type, curr_tbl)) + if (rs_rate_match(&rate, &curr_tbl->rate)) tmp_tbl = curr_tbl; - else if (table_type_matches(&tbl_type, other_tbl)) + else if (rs_rate_match(&rate, &other_tbl->rate)) tmp_tbl = other_tbl; - else + else { + IWL_DEBUG_RATE(mvm, + "Tx packet rate doesn't match ACTIVE or SEARCH tables\n"); + rs_dump_rate(mvm, &rate, "Tx PACKET:"); + rs_dump_rate(mvm, &curr_tbl->rate, "CURRENT:"); + rs_dump_rate(mvm, &other_tbl->rate, "OTHER:"); continue; - rs_collect_tx_data(tmp_tbl, rs_index, 1, + } + rs_collect_tx_data(tmp_tbl, rate.index, 1, i < retries ? 0 : legacy_success); } /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { lq_sta->total_success += legacy_success; lq_sta->total_failed += retries + (1 - legacy_success); } } /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = tx_rate; + lq_sta->last_rate_n_flags = ucode_rate; done: /* See if there's a better rate or modulation mode to try. */ if (sta && sta->supp_rates[sband->band]) @@ -969,8 +1174,8 @@ done: static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, struct iwl_lq_sta *lq_sta) { - IWL_DEBUG_RATE(mvm, "we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ + IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); + lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; if (is_legacy) { lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; @@ -984,37 +1189,31 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = jiffies; - lq_sta->action_counter = 0; + lq_sta->visited_columns = 0; } -/* - * Find correct throughput table for given mode of modulation - */ -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) +static s32 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, + const struct rs_tx_column *column, + u32 bw) { /* Used to choose among HT tables */ s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_ht(tbl->lq_type) && - !(is_vht(tbl->lq_type)))) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } + if (WARN_ON_ONCE(column->mode != RS_LEGACY && + column->mode != RS_SISO && + column->mode != RS_MIMO2)) + return expected_tpt_legacy; /* Legacy rates have only one table */ - if (is_legacy(tbl->lq_type)) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } + if (column->mode == RS_LEGACY) + return expected_tpt_legacy; ht_tbl_pointer = expected_tpt_mimo2_20MHz; /* Choose among many HT tables depending on number of streams * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation * status */ - if (is_siso(tbl->lq_type)) { - switch (tbl->bw) { + if (column->mode == RS_SISO) { + switch (bw) { case RATE_MCS_CHAN_WIDTH_20: ht_tbl_pointer = expected_tpt_siso_20MHz; break; @@ -1027,8 +1226,8 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, default: WARN_ON_ONCE(1); } - } else if (is_mimo2(tbl->lq_type)) { - switch (tbl->bw) { + } else if (column->mode == RS_MIMO2) { + switch (bw) { case RATE_MCS_CHAN_WIDTH_20: ht_tbl_pointer = expected_tpt_mimo2_20MHz; break; @@ -1045,14 +1244,23 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, WARN_ON_ONCE(1); } - if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ - tbl->expected_tpt = ht_tbl_pointer[0]; - else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ - tbl->expected_tpt = ht_tbl_pointer[1]; - else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ - tbl->expected_tpt = ht_tbl_pointer[2]; + if (!column->sgi && !lq_sta->is_agg) /* Normal */ + return ht_tbl_pointer[0]; + else if (column->sgi && !lq_sta->is_agg) /* SGI */ + return ht_tbl_pointer[1]; + else if (!column->sgi && lq_sta->is_agg) /* AGG */ + return ht_tbl_pointer[2]; else /* AGG+SGI */ - tbl->expected_tpt = ht_tbl_pointer[3]; + return ht_tbl_pointer[3]; +} + +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + struct rs_rate *rate = &tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; + + tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); } /* @@ -1089,7 +1297,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, while (1) { high_low = rs_get_adjacent_rate(mvm, rate, rate_mask, - tbl->lq_type); + tbl->rate.type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; @@ -1110,7 +1318,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, * "active" throughput (under perfect conditions). */ if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > IWL_RATE_DECREASE_TH) && + ((active_sr > RS_SR_FORCE_DECREASE) && (active_sr <= IWL_RATE_HIGH_TH) && (tpt_tbl[rate] <= active_tpt))) || ((active_sr >= IWL_RATE_SCALE_SWITCH) && @@ -1157,417 +1365,14 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, return new_rate; } -/* Move to the next action and wrap around to the first action in case - * we're at the last action. Assumes actions start at 0. - */ -static inline void rs_move_next_action(struct iwl_scale_tbl_info *tbl, - u8 last_action) -{ - BUILD_BUG_ON(IWL_LEGACY_FIRST_ACTION != 0); - BUILD_BUG_ON(IWL_SISO_FIRST_ACTION != 0); - BUILD_BUG_ON(IWL_MIMO2_FIRST_ACTION != 0); - - tbl->action = (tbl->action + 1) % (last_action + 1); -} - -static void rs_set_bw_from_sta(struct iwl_scale_tbl_info *tbl, - struct ieee80211_sta *sta) +static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) { if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) - tbl->bw = RATE_MCS_CHAN_WIDTH_80; + return RATE_MCS_CHAN_WIDTH_80; else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) - tbl->bw = RATE_MCS_CHAN_WIDTH_40; - else - tbl->bw = RATE_MCS_CHAN_WIDTH_20; -} - -static bool rs_sgi_allowed(struct iwl_scale_tbl_info *tbl, - struct ieee80211_sta *sta) -{ - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - - if (is_ht20(tbl) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - return true; - if (is_ht40(tbl) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - return true; - if (is_ht80(tbl) && (vht_cap->cap & - IEEE80211_VHT_CAP_SHORT_GI_80)) - return true; - - return false; -} - -/* - * Set up search table for MIMO2 - */ -static int rs_switch_to_mimo2(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - - if (!sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 2) - return -1; - - IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n"); - - tbl->lq_type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_mimo2_rate; - - rs_set_bw_from_sta(tbl, sta); - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 best rate %d mask %X\n", - rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate); - - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n", - tbl->current_rate); - return 0; -} - -/* - * Set up search table for SISO - */ -static int rs_switch_to_siso(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - - if (!sta->ht_cap.ht_supported) - return -1; - - IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n"); - - tbl->lq_type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_siso_rate; - - rs_set_bw_from_sta(tbl, sta); - rs_set_expected_tpt_table(lq_sta, tbl); - rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(mvm, "LQ: get best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(mvm, - "can not switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate); - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n", - tbl->current_rate); - return 0; -} - -/* - * Try to switch to new modulation mode from legacy - */ -static int rs_move_legacy_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - int index) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - int ret; - u8 update_search_tbl_counter = 0; - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA: - IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n"); - - if (tx_chains_num <= 1) - break; - - /* Don't change antenna if success has been great */ - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); - - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - rs_set_expected_tpt_table(lq_sta, search_tbl); - goto out; - } - break; - case IWL_LEGACY_SWITCH_SISO: - IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = rs_switch_to_siso(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - - break; - case IWL_LEGACY_SWITCH_MIMO2: - IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_AB; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - default: - WARN_ON_ONCE(1); - } - rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION); - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - -out: - lq_sta->search_better_tbl = 1; - rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION); - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - return 0; -} - -/* - * Try to switch to new modulation mode from SISO - */ -static int rs_move_siso_to_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, int index) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - u8 update_search_tbl_counter = 0; - int ret; - - if (tbl->action == IWL_SISO_SWITCH_MIMO2 && - !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - tbl->action = IWL_SISO_SWITCH_ANTENNA; - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA: - IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n"); - if (tx_chains_num <= 1) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - BT_MBOX_MSG(&mvm->last_bt_notif, 3, - TRAFFIC_LOAD) == 0) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IWL_SISO_SWITCH_MIMO2: - IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_AB; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - break; - case IWL_SISO_SWITCH_GI: - if (!rs_sgi_allowed(tbl, sta)) - break; - - IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, index); - update_search_tbl_counter = 1; - goto out; - default: - WARN_ON_ONCE(1); - } - rs_move_next_action(tbl, IWL_SISO_LAST_ACTION); - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - - out: - lq_sta->search_better_tbl = 1; - rs_move_next_action(tbl, IWL_SISO_LAST_ACTION); - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; -} - -/* - * Try to switch to new modulation mode from MIMO2 - */ -static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, int index) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 update_search_tbl_counter = 0; - int ret; - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO2_SWITCH_SISO_A: - case IWL_MIMO2_SWITCH_SISO_B: - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else /* tbl->action == IWL_MIMO2_SWITCH_SISO_B */ - search_tbl->ant_type = ANT_B; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO2_SWITCH_GI: - if (!rs_sgi_allowed(tbl, sta)) - break; - - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO2 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, index); - update_search_tbl_counter = 1; - goto out; - default: - WARN_ON_ONCE(1); - } - rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION); - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - out: - lq_sta->search_better_tbl = 1; - rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION); - if (update_search_tbl_counter) - search_tbl->action = tbl->action; + return RATE_MCS_CHAN_WIDTH_40; - return 0; + return RATE_MCS_CHAN_WIDTH_20; } /* @@ -1591,13 +1396,13 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) tbl = &(lq_sta->lq_info[active_tbl]); /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { /* Elapsed time using current modulation mode */ if (lq_sta->flush_timer) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + - IWL_RATE_SCALE_FLUSH_INTVL)); + RS_STAY_IN_COLUMN_TIMEOUT)); /* * Check if we should allow search for new modulation mode. @@ -1619,10 +1424,14 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) flush_interval_passed); /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; + IWL_DEBUG_RATE(mvm, + "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = 0; + /* mark the current column as visited */ + lq_sta->visited_columns = BIT(tbl->column); /* * Else if we've used this modulation mode enough repetitions * (regardless of elapsed time or success/failure), reset @@ -1646,7 +1455,8 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) /* If transitioning to allow "search", reset all history * bitmaps and stats in active table (this will become the new * "search" table). */ - if (!lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { + IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&(tbl->win[i])); } @@ -1659,15 +1469,13 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) static void rs_update_rate_tbl(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int index) + struct rs_rate *rate) { - u32 rate; + u32 ucode_rate; - /* Update uCode's rate table. */ - rate = rate_n_flags_from_tbl(mvm, tbl, index); - rs_fill_link_cmd(mvm, sta, lq_sta, rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + ucode_rate = ucode_rate_from_rs_rate(mvm, rate); + rs_fill_link_cmd(mvm, sta, lq_sta, ucode_rate); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } static u8 rs_get_tid(struct iwl_lq_sta *lq_data, @@ -1686,6 +1494,162 @@ static u8 rs_get_tid(struct iwl_lq_sta *lq_data, return tid; } +static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + int i, j, n; + enum rs_column next_col_id; + const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; + const struct rs_tx_column *next_col; + allow_column_func_t allow_func; + u8 valid_ants = iwl_fw_valid_tx_ant(mvm->fw); + s32 *expected_tpt_tbl; + s32 tpt, max_expected_tpt; + + for (i = 0; i < MAX_NEXT_COLUMNS; i++) { + next_col_id = curr_col->next_columns[i]; + + if (next_col_id == RS_COLUMN_INVALID) + continue; + + if (lq_sta->visited_columns & BIT(next_col_id)) { + IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", + next_col_id); + continue; + } + + next_col = &rs_tx_columns[next_col_id]; + + if (!rs_is_valid_ant(valid_ants, next_col->ant)) { + IWL_DEBUG_RATE(mvm, + "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", + next_col_id, valid_ants, next_col->ant); + continue; + } + + for (j = 0; j < MAX_COLUMN_CHECKS; j++) { + allow_func = next_col->checks[j]; + if (allow_func && !allow_func(mvm, sta, tbl)) + break; + } + + if (j != MAX_COLUMN_CHECKS) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: not allowed (check %d failed)\n", + next_col_id, j); + + continue; + } + + tpt = lq_sta->last_tpt / 100; + expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, + tbl->rate.bw); + if (WARN_ON_ONCE(!expected_tpt_tbl)) + continue; + + max_expected_tpt = 0; + for (n = 0; n < IWL_RATE_COUNT; n++) + if (expected_tpt_tbl[n] > max_expected_tpt) + max_expected_tpt = expected_tpt_tbl[n]; + + if (tpt >= max_expected_tpt) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: can't beat current TPT. Max expected %d current %d\n", + next_col_id, max_expected_tpt, tpt); + continue; + } + + break; + } + + if (i == MAX_NEXT_COLUMNS) + return RS_COLUMN_INVALID; + + IWL_DEBUG_RATE(mvm, "Found potential column %d\n", next_col_id); + + return next_col_id; +} + +static int rs_switch_to_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + enum rs_column col_id) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct rs_rate *rate = &search_tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[col_id]; + const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u16 rate_mask = 0; + u32 rate_idx = 0; + + memcpy(search_tbl, tbl, sz); + + rate->sgi = column->sgi; + rate->ant = column->ant; + + if (column->mode == RS_LEGACY) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + + rate_mask = lq_sta->active_legacy_rate; + } else if (column->mode == RS_SISO) { + rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; + rate_mask = lq_sta->active_siso_rate; + } else if (column->mode == RS_MIMO2) { + rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; + rate_mask = lq_sta->active_mimo2_rate; + } else { + WARN_ON_ONCE("Bad column mode"); + } + + rate->bw = rs_bw_from_sta_bw(sta); + search_tbl->column = col_id; + rs_set_expected_tpt_table(lq_sta, search_tbl); + + /* Get the best matching rate if we're changing modes. e.g. + * SISO->MIMO, LEGACY->SISO, MIMO->SISO + */ + if (curr_column->mode != column->mode) { + rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, + rate_mask, rate->index); + + if ((rate_idx == IWL_RATE_INVALID) || + !(BIT(rate_idx) & rate_mask)) { + IWL_DEBUG_RATE(mvm, + "can not switch with index %d" + " rate mask %x\n", + rate_idx, rate_mask); + + goto err; + } + + rate->index = rate_idx; + } + + /* TODO: remove current_rate and keep using rs_rate all the way until + * we need to fill in the rs_table in the LQ command + */ + search_tbl->current_rate = ucode_rate_from_rs_rate(mvm, rate); + IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", + col_id, rate->index); + + lq_sta->visited_columns |= BIT(col_id); + return 0; + +err: + rate->type = LQ_NONE; + return -1; +} + + /* * Do rate scaling and search for new modulation mode. */ @@ -1715,10 +1679,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, u16 high_low; s32 sr; u8 tid = IWL_MAX_TID_COUNT; + u8 prev_agg = lq_sta->is_agg; struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv; struct iwl_mvm_tid_data *tid_data; - - IWL_DEBUG_RATE(mvm, "rate scale calculate new rate for skb\n"); + struct rs_rate *rate; /* Send management frames and NO_ACK data using lowest rate. */ /* TODO: this could probably be improved.. */ @@ -1751,20 +1715,23 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); + rate = &tbl->rate; + + if (prev_agg != lq_sta->is_agg) { + IWL_DEBUG_RATE(mvm, + "Aggregation changed: prev %d current %d. Update expected TPT table\n", + prev_agg, lq_sta->is_agg); + rs_set_expected_tpt_table(lq_sta, tbl); + } /* current tx rate */ index = lq_sta->last_txrate_idx; - IWL_DEBUG_RATE(mvm, "Rate scale index %d for type %d\n", index, - tbl->lq_type); - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - IWL_DEBUG_RATE(mvm, "mask 0x%04X\n", rate_mask); + rate_mask = rs_get_supported_rates(lq_sta, rate); /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { + if (is_legacy(rate)) { if (lq_sta->band == IEEE80211_BAND_5GHZ) /* supp_rates has no CCK bits in A mode */ rate_scale_index_msk = (u16) (rate_mask & @@ -1780,16 +1747,17 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (!rate_scale_index_msk) rate_scale_index_msk = rate_mask; - if (!((1 << index) & rate_scale_index_msk)) { + if (!((BIT(index) & rate_scale_index_msk))) { IWL_ERR(mvm, "Current Rate is not valid\n"); if (lq_sta->search_better_tbl) { /* revert to active table if search table is not valid*/ - tbl->lq_type = LQ_NONE; + rate->type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); /* get "active" rate info */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index); + tbl->rate.index = index; + rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); } return; } @@ -1806,6 +1774,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, index = lq_sta->max_rate_idx; update_lq = 1; window = &(tbl->win[index]); + IWL_DEBUG_RATE(mvm, + "Forcing user max rate %d\n", + index); goto lq_update; } @@ -1822,8 +1793,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(mvm, - "LQ: still below TH. succ=%d total=%d for index %d\n", - window->success_counter, window->counter, index); + "(%s: %d): Test Window: succ %d total %d\n", + rs_pretty_lq_type(rate->type), + index, window->success_counter, window->counter); /* Can't calculate this yet; not enough history */ window->average_tpt = IWL_INVALID_VALUE; @@ -1838,8 +1810,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * actual average throughput */ if (window->average_tpt != ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128)) { - IWL_ERR(mvm, - "expected_tpt should have been calculated by now\n"); window->average_tpt = ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128); } @@ -1851,27 +1821,26 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * continuing to use the setup that we've been trying. */ if (window->average_tpt > lq_sta->last_tpt) { IWL_DEBUG_RATE(mvm, - "LQ: SWITCHING TO NEW TABLE suc=%d cur-tpt=%d old-tpt=%d\n", + "SWITCHING TO NEW TABLE SR: %d " + "cur-tpt %d old-tpt %d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - /* Swap tables; "search" becomes "active" */ lq_sta->active_tbl = active_tbl; current_tpt = window->average_tpt; /* Else poor success; go back to mode in "active" table */ } else { IWL_DEBUG_RATE(mvm, - "LQ: GOING BACK TO THE OLD TABLE suc=%d cur-tpt=%d old-tpt=%d\n", + "GOING BACK TO THE OLD TABLE: SR %d " + "cur-tpt %d old-tpt %d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; + rate->type = LQ_NONE; /* Revert to "active" table */ active_tbl = lq_sta->active_tbl; @@ -1895,7 +1864,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* (Else) not in search of better modulation mode, try for better * starting rate, while staying in this mode. */ high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk, - tbl->lq_type); + rate->type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; @@ -1913,20 +1882,31 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (high != IWL_RATE_INVALID) high_tpt = tbl->win[high].average_tpt; + IWL_DEBUG_RATE(mvm, + "(%s: %d): cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", + rs_pretty_lq_type(rate->type), index, current_tpt, sr, + low, high, low_tpt, high_tpt); + scale_action = 0; /* Too many failures, decrease rate */ - if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { + if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { IWL_DEBUG_RATE(mvm, - "decrease rate because of low success_ratio\n"); + "decrease rate because of low SR\n"); scale_action = -1; /* No throughput measured yet for adjacent rates; try increase. */ } else if ((low_tpt == IWL_INVALID_VALUE) && (high_tpt == IWL_INVALID_VALUE)) { - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Good SR and no high rate measurement. " + "Increase rate\n"); scale_action = 1; - else if (low != IWL_RATE_INVALID) + } else if (low != IWL_RATE_INVALID) { + IWL_DEBUG_RATE(mvm, + "Remain in current rate\n"); scale_action = 0; + } } /* Both adjacent throughputs are measured, but neither one has better @@ -1934,8 +1914,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, else if ((low_tpt != IWL_INVALID_VALUE) && (high_tpt != IWL_INVALID_VALUE) && (low_tpt < current_tpt) && - (high_tpt < current_tpt)) + (high_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Both high and low are worse. " + "Maintain rate\n"); scale_action = 0; + } /* At least one adjacent rate's throughput is measured, * and may have better performance. */ @@ -1945,8 +1929,14 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* Higher rate has better throughput */ if (high_tpt > current_tpt && sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Higher rate is better and good " + "SR. Increate rate\n"); scale_action = 1; } else { + IWL_DEBUG_RATE(mvm, + "Higher rate isn't better OR " + "no good SR. Maintain rate\n"); scale_action = 0; } @@ -1955,9 +1945,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* Lower rate has better throughput */ if (low_tpt > current_tpt) { IWL_DEBUG_RATE(mvm, - "decrease rate because of low tpt\n"); + "Lower rate is better. " + "Decrease rate\n"); scale_action = -1; } else if (sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Lower rate isn't better and " + "good SR. Increase rate\n"); scale_action = 1; } } @@ -1967,29 +1961,17 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * has been good at old rate. Don't change it. */ if ((scale_action == -1) && (low != IWL_RATE_INVALID) && ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) + (current_tpt > (100 * tbl->expected_tpt[low])))) { + IWL_DEBUG_RATE(mvm, + "Sanity check failed. Maintain rate\n"); scale_action = 0; - - if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) { - if (lq_sta->last_bt_traffic > - le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) { - /* - * don't set scale_action, don't want to scale up if - * the rate scale doesn't otherwise think that is a - * good idea. - */ - } else if (lq_sta->last_bt_traffic <= - le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) { - scale_action = -1; - } } - lq_sta->last_bt_traffic = - le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); - if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) { - /* search for a new modulation */ + /* Force a search in case BT doesn't like us being in MIMO */ + if (is_mimo(rate) && + !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { + IWL_DEBUG_RATE(mvm, + "BT Coex forbids MIMO. Search for new config\n"); rs_stay_in_table(lq_sta, true); goto lq_update; } @@ -2000,6 +1982,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (low != IWL_RATE_INVALID) { update_lq = 1; index = low; + } else { + IWL_DEBUG_RATE(mvm, + "At the bottom rate. Can't decrease\n"); } break; @@ -2008,6 +1993,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (high != IWL_RATE_INVALID) { update_lq = 1; index = high; + } else { + IWL_DEBUG_RATE(mvm, + "At the top rate. Can't increase\n"); } break; @@ -2017,14 +2005,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, break; } - IWL_DEBUG_RATE(mvm, - "choose rate scale index %d action %d low %d high %d type %d\n", - index, scale_action, low, high, tbl->lq_type); - lq_update: /* Replace uCode's rate table for the destination station. */ - if (update_lq) - rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index); + if (update_lq) { + tbl->rate.index = index; + rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); + } rs_stay_in_table(lq_sta, false); @@ -2035,20 +2021,29 @@ lq_update: * 3) Allowing a new search */ if (!update_lq && !done_search && - !lq_sta->stay_in_tbl && window->counter) { + lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED + && window->counter) { + enum rs_column next_column; + /* Save current throughput to compare with "search" throughput*/ lq_sta->last_tpt = current_tpt; - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(mvm, lq_sta, sta, index); - else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(mvm, lq_sta, sta, index); - else if (is_mimo2(tbl->lq_type)) - rs_move_mimo2_to_other(mvm, lq_sta, sta, index); - else - WARN_ON_ONCE(1); + IWL_DEBUG_RATE(mvm, + "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", + update_lq, done_search, lq_sta->rs_state, + window->counter); + + next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); + if (next_column != RS_COLUMN_INVALID) { + int ret = rs_switch_to_column(mvm, lq_sta, sta, + next_column); + if (!ret) + lq_sta->search_better_tbl = 1; + } else { + IWL_DEBUG_RATE(mvm, + "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; + } /* If new "search" mode was selected, set up in uCode table */ if (lq_sta->search_better_tbl) { @@ -2060,34 +2055,29 @@ lq_update: /* Use new "search" start rate */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - IWL_DEBUG_RATE(mvm, - "Switch current mcs: %X index: %d\n", - tbl->current_rate, index); + rs_dump_rate(mvm, &tbl->rate, + "Switch to SEARCH TABLE:"); rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } else { done_search = 1; } } - if (done_search && !lq_sta->stay_in_tbl) { + if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { /* If the "active" (non-search) mode was legacy, * and we've tried switching antennas, * but we haven't been able to try HT modes (not available), * stay with best antenna legacy modulation for a while * before next round of mode comparisons. */ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && !sta->ht_cap.ht_supported && - lq_sta->action_counter > tbl1->max_search) { + if (is_legacy(&tbl1->rate) && !sta->ht_cap.ht_supported) { IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); rs_set_stay_in_table(mvm, 1, lq_sta); - } - + } else { /* If we're in an HT mode, and all 3 mode switch actions * have been tried and compared, stay in this best modulation * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - (lq_sta->action_counter >= tbl1->max_search)) { if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { @@ -2105,7 +2095,8 @@ lq_update: } out: - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index); + tbl->rate.index = index; + tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate); lq_sta->last_txrate_idx = index; } @@ -2126,12 +2117,13 @@ out: static void rs_initialize_lq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - enum ieee80211_band band) + enum ieee80211_band band, + bool init) { struct iwl_scale_tbl_info *tbl; - int rate_idx; + struct rs_rate *rate; int i; - u32 rate; + u32 ucode_rate; u8 active_tbl = 0; u8 valid_tx_ant; @@ -2148,27 +2140,33 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); + rate = &tbl->rate; if ((i < 0) || (i >= IWL_RATE_COUNT)) i = 0; - rate = iwl_rates[i].plcp; - tbl->ant_type = first_antenna(valid_tx_ant); - rate |= tbl->ant_type << RATE_MCS_ANT_POS; + rate->index = i; + rate->ant = first_antenna(valid_tx_ant); + rate->sgi = false; + rate->bw = RATE_MCS_CHAN_WIDTH_20; + if (band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; - if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; + ucode_rate = ucode_rate_from_rs_rate(mvm, rate); + tbl->current_rate = ucode_rate; - rs_get_tbl_info_from_mcs(rate, band, tbl, &rate_idx); - if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - rs_toggle_antenna(valid_tx_ant, &rate, tbl); + WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); + if (rate->ant == ANT_A) + tbl->column = RS_COLUMN_LEGACY_ANT_A; + else + tbl->column = RS_COLUMN_LEGACY_ANT_B; - rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx); - tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, NULL, lq_sta, rate); + rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate); /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); } static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, @@ -2182,8 +2180,6 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = mvm_sta; - IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n"); - /* Get max rate if user set max rate */ if (lq_sta) { lq_sta->max_rate_idx = txrc->max_rate_idx; @@ -2242,11 +2238,51 @@ static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, return -1; } +static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, + struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_lq_sta *lq_sta) +{ + int i; + int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); + + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_siso_rate |= BIT(i); + } + } + + if (sta->rx_nss < 2) + return; + + highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_mimo2_rate |= BIT(i); + } + } +} + /* * Called after adding a new station to initialize rate scaling */ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band) + enum ieee80211_band band, bool init) { int i, j; struct ieee80211_hw *hw = mvm->hw; @@ -2259,6 +2295,8 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; lq_sta = &sta_priv->lq_sta; + memset(lq_sta, 0, sizeof(*lq_sta)); + sband = hw->wiphy->bands[band]; lq_sta->lq.sta_id = sta_priv->sta_id; @@ -2308,27 +2346,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->is_vht = false; } else { - int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - lq_sta->active_siso_rate |= BIT(i); - } - } - - highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - lq_sta->active_mimo2_rate |= BIT(i); - } - } - - /* TODO: avoid MCS9 in 20Mhz which isn't valid for 11ac */ + rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); lq_sta->is_vht = true; } @@ -2341,15 +2359,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - lq_sta->lq.dual_stream_ant_msk = - iwl_fw_valid_tx_ant(mvm->fw) & - ~first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - if (!lq_sta->lq.dual_stream_ant_msk) { - lq_sta->lq.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) == 2) { - lq_sta->lq.dual_stream_ant_msk = - iwl_fw_valid_tx_ant(mvm->fw); - } + lq_sta->lq.dual_stream_ant_msk = ANT_AB; /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; @@ -2364,16 +2374,33 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->dbg_fixed_rate = 0; #endif - rs_initialize_lq(mvm, sta, lq_sta, band); + rs_initialize_lq(mvm, sta, lq_sta, band, init); +} + +static void rs_rate_update(void *mvm_r, + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta, + u32 changed) +{ + u8 tid; + struct iwl_op_mode *op_mode = + (struct iwl_op_mode *)mvm_r; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + /* Stop any ongoing aggregations as rs starts off assuming no agg */ + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_stop_tx_ba_session(sta, tid); + + iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); } static void rs_fill_link_cmd(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, u32 new_rate) { - struct iwl_scale_tbl_info tbl_type; + struct rs_rate rate; int index = 0; - int rate_idx; int repeat_rate = 0; u8 ant_toggle_cnt = 0; u8 use_ht_possible = 1; @@ -2383,12 +2410,10 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, /* Override starting rate (index 0) if needed for debug purposes */ rs_dbgfs_set_mcs(lq_sta, &new_rate); - /* Interpret new_rate (rate_n_flags) */ - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); + rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate); /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { + if (is_legacy(&rate)) { ant_toggle_cnt = 1; repeat_rate = IWL_NUMBER_TRY; } else { @@ -2396,15 +2421,13 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, LINK_QUAL_AGG_DISABLE_START_DEF - 1); } - lq_cmd->mimo_delim = is_mimo(tbl_type.lq_type) ? 1 : 0; + lq_cmd->mimo_delim = is_mimo(&rate) ? 1 : 0; /* Fill 1st table entry (index 0) */ lq_cmd->rs_table[index] = cpu_to_le32(new_rate); - if (num_of_ant(tbl_type.ant_type) == 1) - lq_cmd->single_stream_ant_msk = tbl_type.ant_type; - else if (num_of_ant(tbl_type.ant_type) == 2) - lq_cmd->dual_stream_ant_msk = tbl_type.ant_type; + if (num_of_ant(rate.ant) == 1) + lq_cmd->single_stream_ant_msk = rate.ant; /* otherwise we don't modify the existing value */ index++; @@ -2418,12 +2441,12 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(tbl_type.lq_type)) { + if (is_legacy(&rate)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (mvm && rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) + &new_rate, &rate)) ant_toggle_cnt = 1; } @@ -2437,26 +2460,25 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, index++; } - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); + rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate); /* Indicate to uCode which entries might be MIMO. * If initial rate was MIMO, this will finally end up * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) + if (is_mimo(&rate)) lq_cmd->mimo_delim = index; /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, + new_rate = rs_get_lower_rate(lq_sta, &rate, rate.index, use_ht_possible); /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { + if (is_legacy(&rate)) { if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) ant_toggle_cnt++; else if (mvm && rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) + &new_rate, &rate)) ant_toggle_cnt = 1; repeat_rate = IWL_NUMBER_TRY; @@ -2527,7 +2549,6 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, >> RATE_MCS_ANT_POS); if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { *rate_n_flags = lq_sta->dbg_fixed_rate; - IWL_DEBUG_RATE(mvm, "Fixed rate ON\n"); } else { lq_sta->dbg_fixed_rate = 0; IWL_ERR(mvm, @@ -2535,9 +2556,60 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, ant_sel_tx, valid_tx_ant); IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n"); } + } +} + +static int rs_pretty_print_rate(char *buf, const u32 rate) +{ + + char *type, *bw; + u8 mcs = 0, nss = 0; + u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; + + if (!(rate & RATE_MCS_HT_MSK) && + !(rate & RATE_MCS_VHT_MSK)) { + int index = iwl_hwrate_to_plcp_idx(rate); + + return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", + rs_pretty_ant(ant), iwl_rate_mcs[index].mbps); + } + + if (rate & RATE_MCS_VHT_MSK) { + type = "VHT"; + mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_VHT_MCS_NSS_MSK) + >> RATE_VHT_MCS_NSS_POS) + 1; + } else if (rate & RATE_MCS_HT_MSK) { + type = "HT"; + mcs = rate & RATE_HT_MCS_INDEX_MSK; } else { - IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n"); + type = "Unknown"; /* shouldn't happen */ + } + + switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + bw = "20Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_40: + bw = "40Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_80: + bw = "80Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_160: + bw = "160Mhz"; + break; + default: + bw = "BAD BW"; } + + return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", + type, rs_pretty_ant(ant), bw, mcs, nss, + (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", + (rate & RATE_MCS_STBC_MSK) ? "STBC " : "", + (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", + (rate & RATE_MCS_BF_MSK) ? "BF " : "", + (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); } static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, @@ -2572,15 +2644,14 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, char *buff; int desc = 0; int i = 0; - int index = 0; ssize_t ret; struct iwl_lq_sta *lq_sta = file->private_data; struct iwl_mvm *mvm; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - + struct rs_rate *rate = &tbl->rate; mvm = lq_sta->drv; - buff = kmalloc(1024, GFP_KERNEL); + buff = kmalloc(2048, GFP_KERNEL); if (!buff) return -ENOMEM; @@ -2595,23 +2666,23 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "", (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : - is_vht(tbl->lq_type) ? "VHT" : "HT"); - if (is_ht(tbl->lq_type)) { + (is_legacy(rate)) ? "legacy" : + is_vht(rate) ? "VHT" : "HT"); + if (!is_legacy(rate)) { desc += sprintf(buff+desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); + (is_siso(rate)) ? "SISO" : "MIMO2"); desc += sprintf(buff+desc, " %s", - (is_ht20(tbl)) ? "20MHz" : - (is_ht40(tbl)) ? "40MHz" : - (is_ht80(tbl)) ? "80Mhz" : "BAD BW"); + (is_ht20(rate)) ? "20MHz" : + (is_ht40(rate)) ? "40MHz" : + (is_ht80(rate)) ? "80Mhz" : "BAD BW"); desc += sprintf(buff+desc, " %s %s\n", - (tbl->is_SGI) ? "SGI" : "", + (rate->sgi) ? "SGI" : "NGI", (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); desc += sprintf(buff+desc, - "general: flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", lq_sta->lq.flags, lq_sta->lq.mimo_delim, lq_sta->lq.single_stream_ant_msk, @@ -2631,19 +2702,12 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, lq_sta->lq.initial_rate_index[3]); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - index = iwl_hwrate_to_plcp_idx( - le32_to_cpu(lq_sta->lq.rs_table[i])); - if (is_legacy(tbl->lq_type)) { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i]), - iwl_rate_mcs[index].mbps); - } else { - desc += sprintf(buff+desc, - " rate[%d] 0x%X %smbps (%s)\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i]), - iwl_rate_mcs[index].mbps, - iwl_rate_mcs[index].mcs); - } + u32 rate = le32_to_cpu(lq_sta->lq.rs_table[i]); + desc += sprintf(buff+desc, + " rate[%d] 0x%X ", + i, rate); + + desc += rs_pretty_print_rate(buff+desc, rate); } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); @@ -2665,6 +2729,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, int i, j; ssize_t ret; struct iwl_scale_tbl_info *tbl; + struct rs_rate *rate; struct iwl_lq_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); @@ -2673,15 +2738,16 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, for (i = 0; i < LQ_SIZE; i++) { tbl = &(lq_sta->lq_info[i]); + rate = &tbl->rate; desc += sprintf(buff+desc, "%s type=%d SGI=%d BW=%s DUP=0\n" "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", - tbl->lq_type, - tbl->is_SGI, - is_ht20(tbl) ? "20Mhz" : - is_ht40(tbl) ? "40Mhz" : - is_ht80(tbl) ? "80Mhz" : "ERR", + rate->type, + rate->sgi, + is_ht20(rate) ? "20Mhz" : + is_ht40(rate) ? "40Mhz" : + is_ht80(rate) ? "80Mhz" : "ERR", tbl->current_rate); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, @@ -2746,6 +2812,7 @@ static struct rate_control_ops rs_mvm_ops = { .free = rs_free, .alloc_sta = rs_alloc_sta, .free_sta = rs_free_sta, + .rate_update = rs_rate_update, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = rs_add_debugfs, .remove_sta_debugfs = rs_remove_debugfs, @@ -2778,13 +2845,13 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, if (enable) { if (mvmsta->tx_protection == 0) - lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + lq->flags |= LQ_FLAG_USE_RTS_MSK; mvmsta->tx_protection++; } else { mvmsta->tx_protection--; if (mvmsta->tx_protection == 0) - lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK; + lq->flags &= ~LQ_FLAG_USE_RTS_MSK; } - return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false); + return iwl_mvm_send_lq_cmd(mvm, lq, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 721e6b31b712..b32960796384 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -155,38 +155,7 @@ enum { #define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ #define IWL_RATE_HIGH_TH 10880 /* 85% */ #define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define IWL_RATE_DECREASE_TH 1920 /* 15% */ - -/* possible actions when in legacy mode */ -enum { - IWL_LEGACY_SWITCH_ANTENNA, - IWL_LEGACY_SWITCH_SISO, - IWL_LEGACY_SWITCH_MIMO2, - IWL_LEGACY_FIRST_ACTION = IWL_LEGACY_SWITCH_ANTENNA, - IWL_LEGACY_LAST_ACTION = IWL_LEGACY_SWITCH_MIMO2, -}; - -/* possible actions when in siso mode */ -enum { - IWL_SISO_SWITCH_ANTENNA, - IWL_SISO_SWITCH_MIMO2, - IWL_SISO_SWITCH_GI, - IWL_SISO_FIRST_ACTION = IWL_SISO_SWITCH_ANTENNA, - IWL_SISO_LAST_ACTION = IWL_SISO_SWITCH_GI, -}; - -/* possible actions when in mimo mode */ -enum { - IWL_MIMO2_SWITCH_SISO_A, - IWL_MIMO2_SWITCH_SISO_B, - IWL_MIMO2_SWITCH_GI, - IWL_MIMO2_FIRST_ACTION = IWL_MIMO2_SWITCH_SISO_A, - IWL_MIMO2_LAST_ACTION = IWL_MIMO2_SWITCH_GI, -}; - -#define IWL_MAX_SEARCH IWL_MIMO2_LAST_ACTION - -#define IWL_ACTION_LIMIT 3 /* # possible actions */ +#define RS_SR_FORCE_DECREASE 1920 /* 15% */ #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) @@ -224,22 +193,45 @@ enum iwl_table_type { LQ_MAX, }; -#define is_legacy(tbl) (((tbl) == LQ_LEGACY_G) || ((tbl) == LQ_LEGACY_A)) -#define is_ht_siso(tbl) ((tbl) == LQ_HT_SISO) -#define is_ht_mimo2(tbl) ((tbl) == LQ_HT_MIMO2) -#define is_vht_siso(tbl) ((tbl) == LQ_VHT_SISO) -#define is_vht_mimo2(tbl) ((tbl) == LQ_VHT_MIMO2) -#define is_siso(tbl) (is_ht_siso(tbl) || is_vht_siso(tbl)) -#define is_mimo2(tbl) (is_ht_mimo2(tbl) || is_vht_mimo2(tbl)) -#define is_mimo(tbl) (is_mimo2(tbl)) -#define is_ht(tbl) (is_ht_siso(tbl) || is_ht_mimo2(tbl)) -#define is_vht(tbl) (is_vht_siso(tbl) || is_vht_mimo2(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_LEGACY_A) -#define is_g_band(tbl) ((tbl) == LQ_LEGACY_G) - -#define is_ht20(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_20) -#define is_ht40(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_40) -#define is_ht80(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_80) +struct rs_rate { + int index; + enum iwl_table_type type; + u8 ant; + u32 bw; + bool sgi; +}; + + +#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \ + ((type) == LQ_LEGACY_A)) +#define is_type_ht_siso(type) ((type) == LQ_HT_SISO) +#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2) +#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO) +#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2) +#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type)) +#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type)) +#define is_type_mimo(type) (is_type_mimo2(type)) +#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type)) +#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type)) +#define is_type_a_band(type) ((type) == LQ_LEGACY_A) +#define is_type_g_band(type) ((type) == LQ_LEGACY_G) + +#define is_legacy(rate) is_type_legacy((rate)->type) +#define is_ht_siso(rate) is_type_ht_siso((rate)->type) +#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type) +#define is_vht_siso(rate) is_type_vht_siso((rate)->type) +#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type) +#define is_siso(rate) is_type_siso((rate)->type) +#define is_mimo2(rate) is_type_mimo2((rate)->type) +#define is_mimo(rate) is_type_mimo((rate)->type) +#define is_ht(rate) is_type_ht((rate)->type) +#define is_vht(rate) is_type_vht((rate)->type) +#define is_a_band(rate) is_type_a_band((rate)->type) +#define is_g_band(rate) is_type_g_band((rate)->type) + +#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) +#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) +#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) #define IWL_MAX_MCS_DISPLAY_SIZE 12 @@ -257,7 +249,23 @@ struct iwl_rate_scale_data { s32 success_ratio; /* per-cent * 128 */ s32 counter; /* number of frames attempted */ s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; +}; + +/* Possible Tx columns + * Tx Column = a combo of legacy/siso/mimo x antenna x SGI + */ +enum rs_column { + RS_COLUMN_LEGACY_ANT_A = 0, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2, + RS_COLUMN_MIMO2_SGI, + + RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI, + RS_COLUMN_INVALID, }; /** @@ -267,17 +275,19 @@ struct iwl_rate_scale_data { * one for "active", and one for "search". */ struct iwl_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u32 bw; /* channel bandwidth; RATE_MCS_CHAN_WIDTH_XX */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ + struct rs_rate rate; + enum rs_column column; s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ u32 current_rate; /* rate_n_flags, uCode API format */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; +enum { + RS_STATE_SEARCH_CYCLE_STARTED, + RS_STATE_SEARCH_CYCLE_ENDED, + RS_STATE_STAY_IN_COLUMN, +}; + /** * struct iwl_lq_sta -- driver's rate scaling private structure * @@ -285,8 +295,7 @@ struct iwl_scale_tbl_info { */ struct iwl_lq_sta { u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 rs_state; /* RS_STATE_* */ u8 search_better_tbl; /* 1: currently trying alternate mode */ s32 last_tpt; @@ -299,7 +308,9 @@ struct iwl_lq_sta { u32 total_success; /* total successful frames, any/all rates */ u64 flush_timer; /* time staying in mode before new search */ - u8 action_counter; /* # mode-switch actions tried */ + u32 visited_columns; /* Bitmask marking which Tx columns were + * explored during a search cycle + */ bool is_vht; enum ieee80211_band band; @@ -328,33 +339,11 @@ struct iwl_lq_sta { u32 last_rate_n_flags; /* packets destined for this STA are aggregated */ u8 is_agg; - /* BT traffic this sta was last updated in */ - u8 last_bt_traffic; -}; - -enum iwl_bt_coex_profile_traffic_load { - IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, - IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, - IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, - IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, -/* - * There are no more even though below is a u8, the - * indication from the BT device only has two bits. - */ }; - -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - /* Initialize station's rate scaling information after adding station */ -extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_band band); +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + enum ieee80211_band band, bool init); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -366,7 +355,7 @@ extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, * ieee80211_register_hw * */ -extern int iwl_mvm_rate_control_register(void); +int iwl_mvm_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -374,7 +363,7 @@ extern int iwl_mvm_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwl_mvm_rate_control_unregister(void); +void iwl_mvm_rate_control_unregister(void); struct iwl_mvm_sta; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index e0cd100b40cd..4ce9bb581144 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -70,6 +70,9 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 +#define LONG_OUT_TIME_PERIOD 600 +#define SHORT_OUT_TIME_PERIOD 200 +#define SUSPEND_TIME_PERIOD 100 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) { @@ -87,20 +90,22 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) return cpu_to_le16(rx_chain); } -static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif) +static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif, + u32 flags, bool is_assoc) { - if (vif->bss_conf.assoc) - return cpu_to_le32(200 * 1024); - else + if (!is_assoc) return 0; + if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) + return cpu_to_le32(ieee80211_tu_to_usec(SHORT_OUT_TIME_PERIOD)); + return cpu_to_le32(ieee80211_tu_to_usec(LONG_OUT_TIME_PERIOD)); } -static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif) +static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif, + bool is_assoc) { - if (!vif->bss_conf.assoc) + if (!is_assoc) return 0; - - return cpu_to_le32(ieee80211_tu_to_usec(vif->bss_conf.beacon_int)); + return cpu_to_le32(ieee80211_tu_to_usec(SUSPEND_TIME_PERIOD)); } static inline __le32 @@ -262,6 +267,15 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, return (u16)len; } +static void iwl_mvm_vif_assoc_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *is_assoc = data; + + if (vif->bss_conf.assoc) + *is_assoc = true; +} + int iwl_mvm_scan_request(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) @@ -274,6 +288,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; struct iwl_scan_cmd *cmd = mvm->scan_cmd; + bool is_assoc = false; int ret; u32 status; int ssid_len = 0; @@ -289,13 +304,17 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, memset(cmd, 0, sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel))); - + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_vif_assoc_iterator, + &is_assoc); cmd->channel_count = (u8)req->n_channels; cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm); - cmd->max_out_time = iwl_mvm_scan_max_out_time(vif); - cmd->suspend_time = iwl_mvm_scan_suspend_time(vif); + cmd->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags, + is_assoc); + cmd->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc); cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); @@ -522,6 +541,12 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req, struct iwl_scan_offload_cmd *scan) { + bool is_assoc = false; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_vif_assoc_iterator, + &is_assoc); scan->channel_count = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; @@ -529,8 +554,9 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm, scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT; scan->rx_chain = iwl_mvm_scan_rx_chain(mvm); - scan->max_out_time = cpu_to_le32(200 * 1024); - scan->suspend_time = iwl_mvm_scan_suspend_time(vif); + scan->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags, + is_assoc); + scan->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc); scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND); @@ -817,11 +843,10 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, IWL_DEBUG_SCAN(mvm, "Sending scheduled scan with filtering, filter len %d\n", req->n_match_sets); - scan_req.flags |= - cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID); } else { IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); + scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL); } return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC, diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 329952363a54..7a5b7473eafa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -840,7 +840,7 @@ static const u8 tid_to_ac[] = { int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; int txq_id; @@ -895,7 +895,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u8 buf_size) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; int queue, fifo, ret; u16 ssn; @@ -945,13 +945,13 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, */ } - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); + return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); } int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; u16 txq_id; int err; @@ -1023,7 +1023,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; u16 txq_id; enum iwl_mvm_agg_state old_state; @@ -1416,7 +1416,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd_v6 cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, @@ -1438,7 +1438,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, u16 sleep_state_flags = (reason == IEEE80211_FRAME_RELEASE_UAPSD) ? STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd_v6 cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 4dfc359a4bdd..b34941148a98 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -298,6 +298,12 @@ struct iwl_mvm_sta { bool tt_tx_protection; }; +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) +{ + return (void *)sta->drv_priv; +} + /** * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or * broadcast) diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 33cf56fdfc41..95ce4b601fef 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -176,8 +176,11 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * P2P Device discoveribility, while there are other higher priority * events in the system). */ - if (WARN_ONCE(!le32_to_cpu(notif->status), - "Failed to schedule time event\n")) { + if (!le32_to_cpu(notif->status)) { + bool start = le32_to_cpu(notif->action) & + TE_V2_NOTIF_HOST_EVENT_START; + IWL_WARN(mvm, "Time Event %s notification failure\n", + start ? "start" : "end"); if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) { iwl_mvm_te_clear_data(mvm, te_data); return; diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 1f3282dff513..18be04da8e3f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -388,7 +388,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta)) continue; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (enable == mvmsta->tt_tx_protection) continue; err = iwl_mvm_tx_protection(mvm, mvmsta, enable); diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 43d97c33a75a..d87649ac88e1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -276,6 +276,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, return NULL; memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = TX_CMD; tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; if (info->control.hw_key) @@ -361,7 +362,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, u8 txq_id = info->hw_queue; bool is_data_qos = false, is_ampdu = false; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); fc = hdr->frame_control; if (WARN_ON_ONCE(!mvmsta)) @@ -432,7 +433,7 @@ drop: static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; @@ -662,7 +663,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (!IS_ERR_OR_NULL(sta)) { - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (tid != IWL_TID_NON_QOS) { struct iwl_mvm_tid_data *tid_data = @@ -793,7 +794,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta->tid_data[tid].rate_n_flags = le32_to_cpu(tx_resp->initial_rate); } @@ -849,7 +850,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); tid_data = &mvmsta->tid_data[tid]; if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d", diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index ed69e9b78e82..56cf819bc0c7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -486,22 +486,18 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm) * this case to clear the state indicating that station creation is in * progress. */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - u8 flags, bool init) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) { struct iwl_host_cmd cmd = { .id = LQ_CMD, .len = { sizeof(struct iwl_lq_cmd), }, - .flags = flags, + .flags = init ? CMD_SYNC : CMD_ASYNC, .data = { lq, }, }; if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) return -EINVAL; - if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) - return -EINVAL; - return iwl_mvm_send_cmd(mvm, &cmd); } diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 941c0c88f982..2e97a3995333 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -297,6 +297,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, @@ -350,9 +353,32 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)}, /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x500A, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index fa22639b63c9..051268c037b1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -477,4 +477,12 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); } +static inline void iwl_nic_error(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + set_bit(STATUS_FW_ERROR, &trans_pcie->status); + iwl_op_mode_nic_error(trans->op_mode); +} + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 3f237b42eb36..1d6bf7b98e2d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -489,6 +489,10 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) /* Set interrupt coalescing timer to default (2048 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + + /* W/A for interrupt coalescing bug in 7260 and 3160 */ + if (trans->cfg->host_interrupt_operation_mode) + iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); } static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) @@ -796,12 +800,13 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) iwl_pcie_dump_csr(trans); iwl_dump_fh(trans, NULL); + /* set the ERROR bit before we wake up the caller */ set_bit(STATUS_FW_ERROR, &trans_pcie->status); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); wake_up(&trans_pcie->wait_command_queue); local_bh_disable(); - iwl_op_mode_nic_error(trans->op_mode); + iwl_nic_error(trans); local_bh_enable(); } @@ -1121,7 +1126,6 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 inta, inta_mask; - irqreturn_t ret = IRQ_NONE; lockdep_assert_held(&trans_pcie->irq_lock); @@ -1150,7 +1154,16 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) * or due to sporadic interrupts thrown from our NIC. */ if (!inta) { IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - goto none; + /* + * Re-enable interrupts here since we don't have anything to + * service, but only in case the handler won't run. Note that + * the handler can be scheduled because of a previous + * interrupt. + */ + if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + !trans_pcie->inta) + iwl_enable_interrupts(trans); + return IRQ_NONE; } if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { @@ -1168,19 +1181,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) trans_pcie->inta |= inta; /* the thread will service interrupts and re-enable them */ - if (likely(inta)) - return IRQ_WAKE_THREAD; - - ret = IRQ_HANDLED; - -none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if disabled by irq and no schedules tasklet. */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - return ret; + return IRQ_WAKE_THREAD; } /* interrupt handler using ict table, with this interrupt driver will diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 5d9337bec67a..cde9c16f6e4f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -279,9 +279,6 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_pcie_apm_init(trans); - /* Set interrupt coalescing calibration timer to default (512 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); iwl_pcie_set_pwr(trans, false); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 059c5acad3a0..a4ef5cc11100 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -207,7 +207,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "scratch %d = 0x%08x\n", i, le32_to_cpu(txq->scratchbufs[i].scratch)); - iwl_op_mode_nic_error(trans->op_mode); + iwl_nic_error(trans); } /* @@ -1023,7 +1023,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) if (nfreed++ > 0) { IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); - iwl_op_mode_nic_error(trans->op_mode); + iwl_nic_error(trans); } } @@ -1542,30 +1542,24 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, } if (!ret) { - if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { - struct iwl_txq *txq = - &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; - IWL_ERR(trans, - "Error sending %s: time out after %dms.\n", - get_cmd_string(trans_pcie, cmd->id), - jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + IWL_ERR(trans, "Error sending %s: time out after %dms.\n", + get_cmd_string(trans_pcie, cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - IWL_ERR(trans, - "Current CMD queue read_ptr %d write_ptr %d\n", - q->read_ptr, q->write_ptr); + IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", + q->read_ptr, q->write_ptr); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - IWL_DEBUG_INFO(trans, - "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); - ret = -ETIMEDOUT; + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + ret = -ETIMEDOUT; - iwl_op_mode_nic_error(trans->op_mode); + iwl_nic_error(trans); - goto cancel; - } + goto cancel; } if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { @@ -1674,7 +1668,6 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq->entries[q->write_ptr].skb = skb; txq->entries[q->write_ptr].cmd = dev_cmd; - dev_cmd->hdr.cmd = REPLY_TX; dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(q->write_ptr))); |