mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
This commit is contained in:
commit
0d770a82ad
|
@ -2,10 +2,6 @@ config IWLWIFI
|
|||
tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
|
||||
depends on PCI && MAC80211 && HAS_IOMEM
|
||||
select FW_LOADER
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select LEDS_TRIGGERS
|
||||
select MAC80211_LEDS
|
||||
---help---
|
||||
Select to build the driver supporting the:
|
||||
|
||||
|
@ -43,6 +39,14 @@ config IWLWIFI
|
|||
say M here and read <file:Documentation/kbuild/modules.txt>. The
|
||||
module will be called iwlwifi.
|
||||
|
||||
config IWLWIFI_LEDS
|
||||
bool
|
||||
depends on IWLWIFI
|
||||
depends on LEDS_CLASS
|
||||
select LEDS_TRIGGERS
|
||||
select MAC80211_LEDS
|
||||
default y
|
||||
|
||||
config IWLDVM
|
||||
tristate "Intel Wireless WiFi DVM Firmware support"
|
||||
depends on IWLWIFI
|
||||
|
|
|
@ -4,9 +4,10 @@ iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o
|
|||
iwldvm-objs += lib.o calib.o tt.o sta.o rx.o
|
||||
|
||||
iwldvm-objs += power.o
|
||||
iwldvm-objs += scan.o led.o
|
||||
iwldvm-objs += scan.o
|
||||
iwldvm-objs += rxon.o devices.o
|
||||
|
||||
iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
|
||||
iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
|
||||
|
|
|
@ -888,9 +888,11 @@ struct iwl_priv {
|
|||
|
||||
struct iwl_event_log event_log;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_LEDS
|
||||
struct led_classdev led;
|
||||
unsigned long blink_on, blink_off;
|
||||
bool led_registered;
|
||||
#endif
|
||||
|
||||
/* WoWLAN GTK rekey data */
|
||||
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
|
||||
|
|
|
@ -36,8 +36,20 @@ struct iwl_priv;
|
|||
#define IWL_LED_ACTIVITY (0<<1)
|
||||
#define IWL_LED_LINK (1<<1)
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_LEDS
|
||||
void iwlagn_led_enable(struct iwl_priv *priv);
|
||||
void iwl_leds_init(struct iwl_priv *priv);
|
||||
void iwl_leds_exit(struct iwl_priv *priv);
|
||||
#else
|
||||
static inline void iwlagn_led_enable(struct iwl_priv *priv)
|
||||
{
|
||||
}
|
||||
static inline void iwl_leds_init(struct iwl_priv *priv)
|
||||
{
|
||||
}
|
||||
static inline void iwl_leds_exit(struct iwl_priv *priv)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __iwl_leds_h__ */
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
#define NVM_HW_SECTION_NUM_FAMILY_7000 0
|
||||
|
||||
static const struct iwl_base_params iwl7000_base_params = {
|
||||
.eeprom_size = OTP_LOW_IMAGE_SIZE,
|
||||
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
|
||||
.num_of_queues = IWLAGN_NUM_QUEUES,
|
||||
.pll_cfg_val = 0,
|
||||
.shadow_ram_support = true,
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
#define NVM_HW_SECTION_NUM_FAMILY_8000 10
|
||||
|
||||
static const struct iwl_base_params iwl8000_base_params = {
|
||||
.eeprom_size = OTP_LOW_IMAGE_SIZE,
|
||||
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
|
||||
.num_of_queues = IWLAGN_NUM_QUEUES,
|
||||
.pll_cfg_val = 0,
|
||||
.shadow_ram_support = true,
|
||||
|
|
|
@ -102,9 +102,7 @@
|
|||
|
||||
/* EEPROM */
|
||||
#define IWLAGN_EEPROM_IMG_SIZE 2048
|
||||
/* OTP */
|
||||
/* lower blocks contain EEPROM image and calibration data */
|
||||
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
|
||||
|
||||
/* high blocks contain PAPD data */
|
||||
#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */
|
||||
#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */
|
||||
|
|
|
@ -193,6 +193,11 @@ struct iwl_ht_params {
|
|||
#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80
|
||||
#define EEPROM_REGULATORY_BAND_NO_HT40 0
|
||||
|
||||
/* lower blocks contain EEPROM image and calibration data */
|
||||
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
|
||||
#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (4 * 512 * sizeof(u16)) /* 4 KB */
|
||||
#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
|
||||
|
||||
struct iwl_eeprom_params {
|
||||
const u8 regulatory_bands[7];
|
||||
bool enhanced_txpower;
|
||||
|
@ -269,6 +274,7 @@ struct iwl_cfg {
|
|||
u8 nvm_hw_section_num;
|
||||
bool lp_xtal_workaround;
|
||||
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
|
||||
bool no_power_up_nic_in_init;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -72,11 +72,14 @@
|
|||
* @IWL_FW_ERROR_DUMP_SRAM:
|
||||
* @IWL_FW_ERROR_DUMP_REG:
|
||||
* @IWL_FW_ERROR_DUMP_RXF:
|
||||
* @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
|
||||
* &struct iwl_fw_error_dump_txcmd packets
|
||||
*/
|
||||
enum iwl_fw_error_dump_type {
|
||||
IWL_FW_ERROR_DUMP_SRAM = 0,
|
||||
IWL_FW_ERROR_DUMP_REG = 1,
|
||||
IWL_FW_ERROR_DUMP_RXF = 2,
|
||||
IWL_FW_ERROR_DUMP_TXCMD = 3,
|
||||
|
||||
IWL_FW_ERROR_DUMP_MAX,
|
||||
};
|
||||
|
@ -105,4 +108,27 @@ struct iwl_fw_error_dump_file {
|
|||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_error_dump_txcmd - TX command data
|
||||
* @cmdlen: original length of command
|
||||
* @caplen: captured length of command (may be less)
|
||||
* @data: captured command data, @caplen bytes
|
||||
*/
|
||||
struct iwl_fw_error_dump_txcmd {
|
||||
__le32 cmdlen;
|
||||
__le32 caplen;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* iwl_mvm_fw_error_next_data - advance fw error dump data pointer
|
||||
* @data: previous data block
|
||||
* Returns: next data block
|
||||
*/
|
||||
static inline struct iwl_fw_error_dump_data *
|
||||
iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
|
||||
{
|
||||
return (void *)(data->data + le32_to_cpu(data->len));
|
||||
}
|
||||
|
||||
#endif /* __fw_error_dump_h__ */
|
|
@ -116,9 +116,11 @@ enum iwl_ucode_tlv_flag {
|
|||
/**
|
||||
* enum iwl_ucode_tlv_api - ucode api
|
||||
* @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
|
||||
* @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
|
||||
*/
|
||||
enum iwl_ucode_tlv_api {
|
||||
IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
|
||||
IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include "iwl-drv.h"
|
||||
#include "iwl-modparams.h"
|
||||
#include "iwl-nvm-parse.h"
|
||||
|
@ -450,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
|
|||
struct iwl_nvm_data *data,
|
||||
const __le16 *nvm_sec)
|
||||
{
|
||||
u8 hw_addr[ETH_ALEN];
|
||||
|
||||
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
|
||||
memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
|
||||
else
|
||||
memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
|
||||
ETH_ALEN);
|
||||
const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
|
||||
|
||||
/* The byte order is little endian 16 bit, meaning 214365 */
|
||||
data->hw_addr[0] = hw_addr[1];
|
||||
|
@ -467,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
|
|||
data->hw_addr[5] = hw_addr[4];
|
||||
}
|
||||
|
||||
static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
|
||||
struct iwl_nvm_data *data,
|
||||
const __le16 *mac_override,
|
||||
const __le16 *nvm_hw)
|
||||
{
|
||||
const u8 *hw_addr;
|
||||
|
||||
if (mac_override) {
|
||||
hw_addr = (const u8 *)(mac_override +
|
||||
MAC_ADDRESS_OVERRIDE_FAMILY_8000);
|
||||
|
||||
/* The byte order is little endian 16 bit, meaning 214365 */
|
||||
data->hw_addr[0] = hw_addr[1];
|
||||
data->hw_addr[1] = hw_addr[0];
|
||||
data->hw_addr[2] = hw_addr[3];
|
||||
data->hw_addr[3] = hw_addr[2];
|
||||
data->hw_addr[4] = hw_addr[5];
|
||||
data->hw_addr[5] = hw_addr[4];
|
||||
|
||||
if (is_valid_ether_addr(hw_addr))
|
||||
return;
|
||||
}
|
||||
|
||||
/* take the MAC address from the OTP */
|
||||
hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
|
||||
data->hw_addr[0] = hw_addr[3];
|
||||
data->hw_addr[1] = hw_addr[2];
|
||||
data->hw_addr[2] = hw_addr[1];
|
||||
data->hw_addr[3] = hw_addr[0];
|
||||
|
||||
hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
|
||||
data->hw_addr[4] = hw_addr[1];
|
||||
data->hw_addr[5] = hw_addr[0];
|
||||
}
|
||||
|
||||
struct iwl_nvm_data *
|
||||
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
|
||||
const __le16 *nvm_hw, const __le16 *nvm_sw,
|
||||
|
@ -526,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
|
|||
rx_chains);
|
||||
} else {
|
||||
/* MAC address in family 8000 */
|
||||
iwl_set_hw_address(cfg, data, mac_override);
|
||||
iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
|
||||
|
||||
iwl_init_sbands(dev, cfg, data, regulatory,
|
||||
sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
|
||||
|
|
|
@ -463,6 +463,11 @@ struct iwl_trans;
|
|||
* @unref: release a reference previously taken with @ref. Note that
|
||||
* initially the reference count is 1, making an initial @unref
|
||||
* necessary to allow low power states.
|
||||
* @dump_data: fill a data dump with debug data, maybe containing last
|
||||
* TX'ed commands and similar. When called with a NULL buffer and
|
||||
* zero buffer length, provide only the (estimated) required buffer
|
||||
* length. Return the used buffer length.
|
||||
* Note that the transport must fill in the proper file headers.
|
||||
*/
|
||||
struct iwl_trans_ops {
|
||||
|
||||
|
@ -511,6 +516,10 @@ struct iwl_trans_ops {
|
|||
u32 value);
|
||||
void (*ref)(struct iwl_trans *trans);
|
||||
void (*unref)(struct iwl_trans *trans);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -664,6 +673,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
|
|||
trans->ops->unref(trans);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
|
||||
void *buf, u32 buflen)
|
||||
{
|
||||
if (!trans->ops->dump_data)
|
||||
return 0;
|
||||
return trans->ops->dump_data(trans, buf, buflen);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
|
||||
struct iwl_host_cmd *cmd)
|
||||
{
|
||||
|
|
|
@ -3,8 +3,9 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
|
|||
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
|
||||
iwlmvm-y += scan.o time-event.o rs.o
|
||||
iwlmvm-y += power.o coex.o
|
||||
iwlmvm-y += led.o tt.o offloading.o
|
||||
iwlmvm-y += tt.o offloading.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
|
||||
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
|
||||
|
|
|
@ -611,14 +611,14 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
|
|||
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
|
||||
|
||||
if (IWL_MVM_BT_COEX_CORUNNING) {
|
||||
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 |
|
||||
BT_VALID_CORUN_LUT_40);
|
||||
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
|
||||
BT_VALID_CORUN_LUT_40);
|
||||
bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
|
||||
}
|
||||
|
||||
if (IWL_MVM_BT_COEX_MPLUT) {
|
||||
bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
|
||||
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
|
||||
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
|
||||
}
|
||||
|
||||
if (mvm->cfg->bt_shared_single_ant)
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
#include "iwl-io.h"
|
||||
#include "iwl-prph.h"
|
||||
#include "debugfs.h"
|
||||
#include "fw-error-dump.h"
|
||||
#include "iwl-fw-error-dump.h"
|
||||
|
||||
static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
|
|
@ -187,9 +187,9 @@ enum iwl_scan_type {
|
|||
* this number of packets were received (typically 1)
|
||||
* @passive2active: is auto switching from passive to active during scan allowed
|
||||
* @rxchain_sel_flags: RXON_RX_CHAIN_*
|
||||
* @max_out_time: in usecs, max out of serving channel time
|
||||
* @max_out_time: in TUs, max out of serving channel time
|
||||
* @suspend_time: how long to pause scan when returning to service channel:
|
||||
* bits 0-19: beacon interal in usecs (suspend before executing)
|
||||
* bits 0-19: beacon interal in TUs (suspend before executing)
|
||||
* bits 20-23: reserved
|
||||
* bits 24-31: number of beacons (suspend between channels)
|
||||
* @rxon_flags: RXON_FLG_*
|
||||
|
@ -387,8 +387,8 @@ enum scan_framework_client {
|
|||
* @quiet_plcp_th: quiet channel num of packets threshold
|
||||
* @good_CRC_th: passive to active promotion threshold
|
||||
* @rx_chain: RXON rx chain.
|
||||
* @max_out_time: max uSec to be out of assoceated channel
|
||||
* @suspend_time: pause scan this long when returning to service channel
|
||||
* @max_out_time: max TUs to be out of assoceated channel
|
||||
* @suspend_time: pause scan this TUs when returning to service channel
|
||||
* @flags: RXON flags
|
||||
* @filter_flags: RXONfilter
|
||||
* @tx_cmd: tx command for active scan; for 2GHz and for 5GHz.
|
||||
|
|
|
@ -295,7 +295,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
|
|||
/* Read the NVM only at driver load time, no need to do this twice */
|
||||
if (read_nvm) {
|
||||
/* Read nvm */
|
||||
ret = iwl_nvm_init(mvm);
|
||||
ret = iwl_nvm_init(mvm, true);
|
||||
if (ret) {
|
||||
IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
|
||||
goto error;
|
||||
|
|
|
@ -1237,11 +1237,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
|
|||
u32 rate __maybe_unused =
|
||||
le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
|
||||
status & TX_STATUS_MSK,
|
||||
beacon->beacon_notify_hdr.failure_frame,
|
||||
le64_to_cpu(beacon->tsf),
|
||||
rate);
|
||||
|
||||
if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
|
||||
if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
|
||||
iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
|
||||
} else {
|
||||
ieee80211_csa_finish(mvm->csa_vif);
|
||||
mvm->csa_vif = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -320,6 +320,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
|||
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
|
||||
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
|
||||
|
||||
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
|
||||
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
|
||||
|
||||
hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
|
||||
hw->wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mvm_iface_combinations);
|
||||
|
@ -539,13 +542,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
|
|||
return -EACCES;
|
||||
|
||||
/* return from D0i3 before starting a new Tx aggregation */
|
||||
if (action == IEEE80211_AMPDU_TX_START) {
|
||||
switch (action) {
|
||||
case IEEE80211_AMPDU_TX_START:
|
||||
case IEEE80211_AMPDU_TX_STOP_CONT:
|
||||
case IEEE80211_AMPDU_TX_STOP_FLUSH:
|
||||
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
|
||||
case IEEE80211_AMPDU_TX_OPERATIONAL:
|
||||
iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
|
||||
tx_agg_ref = true;
|
||||
|
||||
/*
|
||||
* wait synchronously until D0i3 exit to get the correct
|
||||
* sequence number for the tid
|
||||
* for tx start, wait synchronously until D0i3 exit to
|
||||
* get the correct sequence number for the tid.
|
||||
* additionally, some other ampdu actions use direct
|
||||
* target access, which is not handled automatically
|
||||
* by the trans layer (unlike commands), so wait for
|
||||
* d0i3 exit in these cases as well.
|
||||
*/
|
||||
if (!wait_event_timeout(mvm->d0i3_exit_waitq,
|
||||
!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
|
||||
|
@ -553,6 +565,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
|
|||
iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
@ -2186,6 +2201,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
/* Unless it's a CSA flow we have nothing to do here */
|
||||
if (vif->csa_active) {
|
||||
mvmvif->ap_ibss_active = true;
|
||||
break;
|
||||
}
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
/*
|
||||
* The AP binding flow is handled as part of the start_ap flow
|
||||
|
@ -2222,6 +2242,12 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
goto out_remove_binding;
|
||||
}
|
||||
|
||||
/* Handle binding during CSA */
|
||||
if (vif->type == NL80211_IFTYPE_AP) {
|
||||
iwl_mvm_update_quotas(mvm, vif);
|
||||
iwl_mvm_mac_ctxt_changed(mvm, vif);
|
||||
}
|
||||
|
||||
goto out_unlock;
|
||||
|
||||
out_remove_binding:
|
||||
|
@ -2246,13 +2272,20 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
goto out_unlock;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
mvmvif->monitor_active = false;
|
||||
iwl_mvm_update_quotas(mvm, NULL);
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
/* This part is triggered only during CSA */
|
||||
if (!vif->csa_active || !mvmvif->ap_ibss_active)
|
||||
goto out_unlock;
|
||||
|
||||
mvmvif->ap_ibss_active = false;
|
||||
iwl_mvm_update_quotas(mvm, NULL);
|
||||
/*TODO: bt_coex notification here? */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2348,6 +2381,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
|
|||
}
|
||||
#endif
|
||||
|
||||
static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
|
||||
"Another CSA is already in progress"))
|
||||
goto out_unlock;
|
||||
|
||||
IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
|
||||
chandef->center_freq1);
|
||||
mvm->csa_vif = vif;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, u32 queues, bool drop)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct iwl_mvm_vif *mvmvif;
|
||||
struct iwl_mvm_sta *mvmsta;
|
||||
|
||||
if (!vif || vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
|
||||
|
||||
if (WARN_ON_ONCE(!mvmsta))
|
||||
goto done;
|
||||
|
||||
if (drop) {
|
||||
if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
|
||||
IWL_ERR(mvm, "flush request fail\n");
|
||||
} else {
|
||||
iwl_trans_wait_tx_queue_empty(mvm->trans,
|
||||
mvmsta->tfd_queue_msk);
|
||||
}
|
||||
done:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
const struct ieee80211_ops iwl_mvm_hw_ops = {
|
||||
.tx = iwl_mvm_mac_tx,
|
||||
.ampdu_action = iwl_mvm_mac_ampdu_action,
|
||||
|
@ -2371,6 +2451,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
|
|||
.sta_rc_update = iwl_mvm_sta_rc_update,
|
||||
.conf_tx = iwl_mvm_mac_conf_tx,
|
||||
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
|
||||
.flush = iwl_mvm_mac_flush,
|
||||
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
|
||||
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
|
||||
.set_key = iwl_mvm_mac_set_key,
|
||||
|
@ -2390,6 +2471,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
|
|||
|
||||
.set_tim = iwl_mvm_set_tim,
|
||||
|
||||
.channel_switch_beacon = iwl_mvm_channel_switch_beacon,
|
||||
|
||||
CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
|
@ -589,7 +589,9 @@ struct iwl_mvm {
|
|||
u32 *fw_error_rxf;
|
||||
u32 fw_error_rxf_len;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_LEDS
|
||||
struct led_classdev led;
|
||||
#endif
|
||||
|
||||
struct ieee80211_vif *p2p_device_vif;
|
||||
|
||||
|
@ -642,6 +644,8 @@ struct iwl_mvm {
|
|||
|
||||
/* Indicate if device power save is allowed */
|
||||
bool ps_disabled;
|
||||
|
||||
struct ieee80211_vif *csa_vif;
|
||||
};
|
||||
|
||||
/* Extract MVM priv from op_mode and _hw */
|
||||
|
@ -757,7 +761,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
|
|||
struct iwl_device_cmd *cmd);
|
||||
|
||||
/* NVM */
|
||||
int iwl_nvm_init(struct iwl_mvm *mvm);
|
||||
int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
|
||||
int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
|
||||
|
||||
int iwl_mvm_up(struct iwl_mvm *mvm);
|
||||
|
@ -896,8 +900,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
|
|||
struct iwl_rx_cmd_buffer *rxb,
|
||||
struct iwl_device_cmd *cmd);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_LEDS
|
||||
int iwl_mvm_leds_init(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
|
||||
#else
|
||||
static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* D3 (WoWLAN, NetDetect) */
|
||||
int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
|
||||
|
|
|
@ -238,13 +238,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
|
|||
return NULL;
|
||||
}
|
||||
} else {
|
||||
/* SW and REGULATORY sections are mandatory */
|
||||
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
|
||||
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data ||
|
||||
!mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
|
||||
IWL_ERR(mvm,
|
||||
"Can't parse empty family 8000 NVM sections\n");
|
||||
return NULL;
|
||||
}
|
||||
/* MAC_OVERRIDE or at least HW section must exist */
|
||||
if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
|
||||
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
|
||||
IWL_ERR(mvm,
|
||||
"Can't parse mac_address, empty sections\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!mvm->cfg))
|
||||
|
@ -427,7 +434,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iwl_nvm_init(struct iwl_mvm *mvm)
|
||||
int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
|
||||
{
|
||||
int ret, i, section;
|
||||
u8 *nvm_buffer, *temp;
|
||||
|
@ -437,13 +444,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
|
|||
if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
|
||||
return -EINVAL;
|
||||
|
||||
/* load external NVM if configured */
|
||||
if (iwlwifi_mod_params.nvm_file) {
|
||||
/* move to External NVM flow */
|
||||
ret = iwl_mvm_read_external_nvm(mvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* load NVM values from nic */
|
||||
if (read_nvm_from_nic) {
|
||||
/* list of NVM sections we are allowed/need to read */
|
||||
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
|
||||
nvm_to_read[0] = mvm->cfg->nvm_hw_section_num;
|
||||
|
@ -463,7 +465,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
|
|||
/* Read From FW NVM */
|
||||
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
|
||||
|
||||
/* TODO: find correct NVM max size for a section */
|
||||
nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
|
||||
GFP_KERNEL);
|
||||
if (!nvm_buffer)
|
||||
|
@ -511,6 +512,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* load external NVM if configured */
|
||||
if (iwlwifi_mod_params.nvm_file) {
|
||||
/* move to External NVM flow */
|
||||
ret = iwl_mvm_read_external_nvm(mvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* parse the relevant nvm sections */
|
||||
mvm->nvm_data = iwl_parse_nvm_sections(mvm);
|
||||
if (!mvm->nvm_data)
|
||||
return -ENODATA;
|
||||
|
|
|
@ -79,8 +79,8 @@
|
|||
#include "iwl-prph.h"
|
||||
#include "rs.h"
|
||||
#include "fw-api-scan.h"
|
||||
#include "fw-error-dump.h"
|
||||
#include "time-event.h"
|
||||
#include "iwl-fw-error-dump.h"
|
||||
|
||||
/*
|
||||
* module name, copyright, version, etc.
|
||||
|
@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
|
|||
RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
|
||||
|
||||
RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
|
||||
RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
|
||||
RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
|
||||
RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
|
||||
RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
|
||||
iwl_mvm_rx_ant_coupling_notif, true),
|
||||
|
@ -467,12 +467,18 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
min_backoff = calc_min_backoff(trans, cfg);
|
||||
iwl_mvm_tt_initialize(mvm, min_backoff);
|
||||
|
||||
if (WARN(cfg->no_power_up_nic_in_init && !iwlwifi_mod_params.nvm_file,
|
||||
"not allowing power-up and not having nvm_file\n"))
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* If the NVM exists in an external file,
|
||||
* there is no need to unnecessarily power up the NIC at driver load
|
||||
* Even if nvm exists in the nvm_file driver should read agin the nvm
|
||||
* from the nic because there might be entries that exist in the OTP
|
||||
* and not in the file.
|
||||
* for nics with no_power_up_nic_in_init: rely completley on nvm_file
|
||||
*/
|
||||
if (iwlwifi_mod_params.nvm_file) {
|
||||
err = iwl_nvm_init(mvm);
|
||||
if (cfg->no_power_up_nic_in_init && iwlwifi_mod_params.nvm_file) {
|
||||
err = iwl_nvm_init(mvm, false);
|
||||
if (err)
|
||||
goto out_free;
|
||||
} else {
|
||||
|
@ -519,7 +525,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
out_free:
|
||||
iwl_phy_db_free(mvm->phy_db);
|
||||
kfree(mvm->scan_cmd);
|
||||
if (!iwlwifi_mod_params.nvm_file)
|
||||
if (!cfg->no_power_up_nic_in_init || !iwlwifi_mod_params.nvm_file)
|
||||
iwl_trans_op_mode_leave(trans);
|
||||
ieee80211_free_hw(mvm->hw);
|
||||
return NULL;
|
||||
|
@ -816,6 +822,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
struct iwl_fw_error_dump_file *dump_file;
|
||||
struct iwl_fw_error_dump_data *dump_data;
|
||||
u32 file_len;
|
||||
u32 trans_len;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
|
@ -827,6 +834,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
sizeof(*dump_file) +
|
||||
sizeof(*dump_data) * 2;
|
||||
|
||||
trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
|
||||
if (trans_len)
|
||||
file_len += trans_len;
|
||||
|
||||
dump_file = vmalloc(file_len);
|
||||
if (!dump_file)
|
||||
return;
|
||||
|
@ -840,7 +851,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
|
||||
memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
|
||||
|
||||
dump_data = (void *)((u8 *)dump_data->data + mvm->fw_error_rxf_len);
|
||||
dump_data = iwl_mvm_fw_error_next_data(dump_data);
|
||||
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
|
||||
dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
|
||||
|
||||
|
@ -858,6 +869,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
kfree(mvm->fw_error_sram);
|
||||
mvm->fw_error_sram = NULL;
|
||||
mvm->fw_error_sram_len = 0;
|
||||
|
||||
if (trans_len) {
|
||||
void *buf = iwl_mvm_fw_error_next_data(dump_data);
|
||||
u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
|
||||
trans_len);
|
||||
dump_data = (void *)((u8 *)buf + real_trans_len);
|
||||
dump_file->file_len =
|
||||
cpu_to_le32(file_len - trans_len + real_trans_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -202,18 +202,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
|
|||
struct cfg80211_chan_def *chandef,
|
||||
u8 chains_static, u8 chains_dynamic)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
|
||||
ctxt->ref);
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
ctxt->channel = chandef->chan;
|
||||
ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
|
||||
chains_static, chains_dynamic,
|
||||
FW_CTXT_ACTION_ADD, 0);
|
||||
|
||||
return ret;
|
||||
return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
|
||||
chains_static, chains_dynamic,
|
||||
FW_CTXT_ACTION_ADD, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
.next_columns = {
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
|
@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
.ant = ANT_B,
|
||||
.next_columns = {
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
|
@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_SISO_ANT_A_SGI,
|
||||
RS_COLUMN_SISO_ANT_B_SGI,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_siso_allow,
|
||||
|
@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_SISO_ANT_B_SGI,
|
||||
RS_COLUMN_SISO_ANT_A_SGI,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_siso_allow,
|
||||
|
@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
RS_COLUMN_SISO_ANT_B_SGI,
|
||||
RS_COLUMN_MIMO2_SGI,
|
||||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_siso_allow,
|
||||
|
@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
RS_COLUMN_SISO_ANT_A_SGI,
|
||||
RS_COLUMN_MIMO2_SGI,
|
||||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_siso_allow,
|
||||
|
@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
.ant = ANT_AB,
|
||||
.next_columns = {
|
||||
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_SGI,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_mimo_allow,
|
||||
|
@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
|
|||
.sgi = true,
|
||||
.next_columns = {
|
||||
RS_COLUMN_SISO_ANT_A_SGI,
|
||||
RS_COLUMN_SISO_ANT_B_SGI,
|
||||
RS_COLUMN_SISO_ANT_A,
|
||||
RS_COLUMN_SISO_ANT_B,
|
||||
RS_COLUMN_MIMO2,
|
||||
RS_COLUMN_LEGACY_ANT_A,
|
||||
RS_COLUMN_LEGACY_ANT_B,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
RS_COLUMN_INVALID,
|
||||
},
|
||||
.checks = {
|
||||
rs_mimo_allow,
|
||||
|
@ -1031,7 +1031,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* Disable last tx check if we are debugging with fixed rate */
|
||||
if (lq_sta->dbg_fixed_rate) {
|
||||
IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
|
||||
|
@ -1335,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
|
|||
tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find starting rate for new "search" high-throughput mode of modulation.
|
||||
* Goal is to find lowest expected rate (under perfect conditions) that is
|
||||
* above the current measured throughput of "active" mode, to give new mode
|
||||
* a fair chance to prove itself without too many challenges.
|
||||
*
|
||||
* This gets called when transitioning to more aggressive modulation
|
||||
* (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
|
||||
* (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need
|
||||
* to decrease to match "active" throughput. When moving from MIMO to SISO,
|
||||
* bit rate will typically need to increase, but not if performance was bad.
|
||||
*/
|
||||
static s32 rs_get_best_rate(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_sta,
|
||||
struct iwl_scale_tbl_info *tbl, /* "search" */
|
||||
u16 rate_mask, s8 index)
|
||||
unsigned long rate_mask, s8 index)
|
||||
{
|
||||
/* "active" values */
|
||||
struct iwl_scale_tbl_info *active_tbl =
|
||||
&(lq_sta->lq_info[lq_sta->active_tbl]);
|
||||
s32 active_sr = active_tbl->win[index].success_ratio;
|
||||
s32 active_tpt = active_tbl->expected_tpt[index];
|
||||
/* expected "search" throughput */
|
||||
s32 success_ratio = active_tbl->win[index].success_ratio;
|
||||
u16 expected_current_tpt = active_tbl->expected_tpt[index];
|
||||
const u16 *tpt_tbl = tbl->expected_tpt;
|
||||
|
||||
s32 new_rate, high, low, start_hi;
|
||||
u16 high_low;
|
||||
s8 rate = index;
|
||||
u32 target_tpt;
|
||||
int rate_idx;
|
||||
|
||||
new_rate = high = low = start_hi = IWL_RATE_INVALID;
|
||||
|
||||
while (1) {
|
||||
high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
|
||||
tbl->rate.type);
|
||||
|
||||
low = high_low & 0xff;
|
||||
high = (high_low >> 8) & 0xff;
|
||||
|
||||
/*
|
||||
* Lower the "search" bit rate, to give new "search" mode
|
||||
* approximately the same throughput as "active" if:
|
||||
*
|
||||
* 1) "Active" mode has been working modestly well (but not
|
||||
* great), and expected "search" throughput (under perfect
|
||||
* conditions) at candidate rate is above the actual
|
||||
* measured "active" throughput (but less than expected
|
||||
* "active" throughput under perfect conditions).
|
||||
* OR
|
||||
* 2) "Active" mode has been working perfectly or very well
|
||||
* and expected "search" throughput (under perfect
|
||||
* conditions) at candidate rate is above expected
|
||||
* "active" throughput (under perfect conditions).
|
||||
*/
|
||||
if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
|
||||
((active_sr > RS_SR_FORCE_DECREASE) &&
|
||||
(active_sr <= IWL_RATE_HIGH_TH) &&
|
||||
(tpt_tbl[rate] <= active_tpt))) ||
|
||||
((active_sr >= IWL_RATE_SCALE_SWITCH) &&
|
||||
(tpt_tbl[rate] > active_tpt))) {
|
||||
/* (2nd or later pass)
|
||||
* If we've already tried to raise the rate, and are
|
||||
* now trying to lower it, use the higher rate. */
|
||||
if (start_hi != IWL_RATE_INVALID) {
|
||||
new_rate = start_hi;
|
||||
break;
|
||||
}
|
||||
|
||||
new_rate = rate;
|
||||
|
||||
/* Loop again with lower rate */
|
||||
if (low != IWL_RATE_INVALID)
|
||||
rate = low;
|
||||
|
||||
/* Lower rate not available, use the original */
|
||||
else
|
||||
break;
|
||||
|
||||
/* Else try to raise the "search" rate to match "active" */
|
||||
} else {
|
||||
/* (2nd or later pass)
|
||||
* If we've already tried to lower the rate, and are
|
||||
* now trying to raise it, use the lower rate. */
|
||||
if (new_rate != IWL_RATE_INVALID)
|
||||
break;
|
||||
|
||||
/* Loop again with higher rate */
|
||||
else if (high != IWL_RATE_INVALID) {
|
||||
start_hi = high;
|
||||
rate = high;
|
||||
|
||||
/* Higher rate not available, use the original */
|
||||
} else {
|
||||
new_rate = rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success_ratio > RS_SR_NO_DECREASE) {
|
||||
target_tpt = 100 * expected_current_tpt;
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
|
||||
success_ratio, target_tpt);
|
||||
} else {
|
||||
target_tpt = lq_sta->last_tpt;
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n",
|
||||
success_ratio, target_tpt);
|
||||
}
|
||||
|
||||
return new_rate;
|
||||
rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
|
||||
|
||||
while (rate_idx != IWL_RATE_INVALID) {
|
||||
if (target_tpt < (100 * tpt_tbl[rate_idx]))
|
||||
break;
|
||||
|
||||
high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
|
||||
tbl->rate.type);
|
||||
|
||||
rate_idx = (high_low >> 8) & 0xff;
|
||||
}
|
||||
|
||||
IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
|
||||
rate_idx, target_tpt,
|
||||
rate_idx != IWL_RATE_INVALID ?
|
||||
100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
|
||||
|
||||
return rate_idx;
|
||||
}
|
||||
|
||||
static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
|
||||
|
@ -1608,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
|
|||
|
||||
tpt = lq_sta->last_tpt / 100;
|
||||
expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
|
||||
tbl->rate.bw);
|
||||
rs_bw_from_sta_bw(sta));
|
||||
if (WARN_ON_ONCE(!expected_tpt_tbl))
|
||||
continue;
|
||||
|
||||
|
@ -1649,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
|
|||
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;
|
||||
unsigned long rate_mask = 0;
|
||||
u32 rate_idx = 0;
|
||||
|
||||
memcpy(search_tbl, tbl, sz);
|
||||
|
@ -1691,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
|
|||
!(BIT(rate_idx) & rate_mask)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"can not switch with index %d"
|
||||
" rate mask %x\n",
|
||||
" rate mask %lx\n",
|
||||
rate_idx, rate_mask);
|
||||
|
||||
goto err;
|
||||
|
@ -1805,16 +1750,21 @@ static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
|
|||
*stronger = TPC_INVALID;
|
||||
}
|
||||
|
||||
static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct rs_rate *rate,
|
||||
enum ieee80211_band band)
|
||||
static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
struct rs_rate *rate, enum ieee80211_band band)
|
||||
{
|
||||
int index = rate->index;
|
||||
bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
|
||||
bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
|
||||
!vif->bss_conf.ps);
|
||||
|
||||
IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
|
||||
cam, sta_ps_disabled);
|
||||
/*
|
||||
* allow tpc only if power management is enabled, or bt coex
|
||||
* activity grade allows it and we are on 2.4Ghz.
|
||||
*/
|
||||
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM &&
|
||||
if ((cam || sta_ps_disabled) &&
|
||||
!iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
|
||||
return false;
|
||||
|
||||
|
@ -1931,7 +1881,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm,
|
|||
band = chanctx_conf->def.chan->band;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!rs_tpc_allowed(mvm, rate, band)) {
|
||||
if (!rs_tpc_allowed(mvm, vif, rate, band)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"tpc is not allowed. remove txp restrictions");
|
||||
lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
|
||||
|
@ -2235,7 +2185,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
break;
|
||||
case RS_ACTION_STAY:
|
||||
/* No change */
|
||||
update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
|
||||
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
|
||||
update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2489,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
|
|||
if (i == IWL_RATE_9M_INDEX)
|
||||
continue;
|
||||
|
||||
/* Disable MCS9 as a workaround */
|
||||
if (i == IWL_RATE_MCS_9_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)
|
||||
|
@ -2511,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
|
|||
if (i == IWL_RATE_9M_INDEX)
|
||||
continue;
|
||||
|
||||
/* Disable MCS9 as a workaround */
|
||||
if (i == IWL_RATE_MCS_9_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)
|
||||
|
|
|
@ -277,51 +277,22 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
|
|||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_scan_condition_iterator,
|
||||
&global_bound);
|
||||
/*
|
||||
* Under low latency traffic passive scan is fragmented meaning
|
||||
* that dwell on a particular channel will be fragmented. Each fragment
|
||||
* dwell time is 20ms and fragments period is 105ms. Skipping to next
|
||||
* channel will be delayed by the same period - 105ms. So suspend_time
|
||||
* parameter describing both fragments and channels skipping periods is
|
||||
* set to 105ms. This value is chosen so that overall passive scan
|
||||
* duration will not be too long. Max_out_time in this case is set to
|
||||
* 70ms, so for active scanning operating channel will be left for 70ms
|
||||
* while for passive still for 20ms (fragment dwell).
|
||||
*/
|
||||
if (global_bound) {
|
||||
if (!iwl_mvm_low_latency(mvm)) {
|
||||
params->suspend_time = ieee80211_tu_to_usec(100);
|
||||
params->max_out_time = ieee80211_tu_to_usec(600);
|
||||
} else {
|
||||
params->suspend_time = ieee80211_tu_to_usec(105);
|
||||
/* P2P doesn't support fragmented passive scan, so
|
||||
* configure max_out_time to be at least longest dwell
|
||||
* time for passive scan.
|
||||
*/
|
||||
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
|
||||
params->max_out_time = ieee80211_tu_to_usec(70);
|
||||
params->passive_fragmented = true;
|
||||
} else {
|
||||
u32 passive_dwell;
|
||||
|
||||
/*
|
||||
* Use band G so that passive channel dwell time
|
||||
* will be assigned with maximum value.
|
||||
*/
|
||||
band = IEEE80211_BAND_2GHZ;
|
||||
passive_dwell = iwl_mvm_get_passive_dwell(band);
|
||||
params->max_out_time =
|
||||
ieee80211_tu_to_usec(passive_dwell);
|
||||
}
|
||||
}
|
||||
if (!global_bound)
|
||||
goto not_bound;
|
||||
|
||||
params->suspend_time = 100;
|
||||
params->max_out_time = 600;
|
||||
|
||||
if (iwl_mvm_low_latency(mvm)) {
|
||||
params->suspend_time = 250;
|
||||
params->max_out_time = 250;
|
||||
}
|
||||
|
||||
not_bound:
|
||||
|
||||
for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
|
||||
if (params->passive_fragmented)
|
||||
params->dwell[band].passive = 20;
|
||||
else
|
||||
params->dwell[band].passive =
|
||||
iwl_mvm_get_passive_dwell(band);
|
||||
params->dwell[band].passive = iwl_mvm_get_passive_dwell(band);
|
||||
params->dwell[band].active = iwl_mvm_get_active_dwell(band,
|
||||
n_ssids);
|
||||
}
|
||||
|
|
|
@ -519,6 +519,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
|
|||
iwl_mvm_dump_umac_error_log(mvm);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
|
||||
{
|
||||
const struct fw_img *img;
|
||||
|
@ -581,6 +582,7 @@ void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
|
|||
}
|
||||
iwl_trans_release_nic_access(mvm->trans, &flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* iwl_mvm_send_lq_cmd() - Send link quality command
|
||||
|
|
|
@ -117,21 +117,19 @@ struct iwl_dma_ptr {
|
|||
/**
|
||||
* iwl_queue_inc_wrap - increment queue index, wrap back to beginning
|
||||
* @index -- current index
|
||||
* @n_bd -- total number of entries in queue (must be power of 2)
|
||||
*/
|
||||
static inline int iwl_queue_inc_wrap(int index, int n_bd)
|
||||
static inline int iwl_queue_inc_wrap(int index)
|
||||
{
|
||||
return ++index & (n_bd - 1);
|
||||
return ++index & (TFD_QUEUE_SIZE_MAX - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_queue_dec_wrap - decrement queue index, wrap back to end
|
||||
* @index -- current index
|
||||
* @n_bd -- total number of entries in queue (must be power of 2)
|
||||
*/
|
||||
static inline int iwl_queue_dec_wrap(int index, int n_bd)
|
||||
static inline int iwl_queue_dec_wrap(int index)
|
||||
{
|
||||
return --index & (n_bd - 1);
|
||||
return --index & (TFD_QUEUE_SIZE_MAX - 1);
|
||||
}
|
||||
|
||||
struct iwl_cmd_meta {
|
||||
|
@ -145,13 +143,13 @@ struct iwl_cmd_meta {
|
|||
*
|
||||
* Contains common data for Rx and Tx queues.
|
||||
*
|
||||
* Note the difference between n_bd and n_window: the hardware
|
||||
* always assumes 256 descriptors, so n_bd is always 256 (unless
|
||||
* Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
|
||||
* always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
|
||||
* there might be HW changes in the future). For the normal TX
|
||||
* queues, n_window, which is the size of the software queue data
|
||||
* is also 256; however, for the command queue, n_window is only
|
||||
* 32 since we don't need so many commands pending. Since the HW
|
||||
* still uses 256 BDs for DMA though, n_bd stays 256. As a result,
|
||||
* still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
|
||||
* the software buffers (in the variables @meta, @txb in struct
|
||||
* iwl_txq) only have 32 entries, while the HW buffers (@tfds in
|
||||
* the same struct) have 256.
|
||||
|
@ -162,7 +160,6 @@ struct iwl_cmd_meta {
|
|||
* data is a window overlayed over the HW queue.
|
||||
*/
|
||||
struct iwl_queue {
|
||||
int n_bd; /* number of BDs in this queue */
|
||||
int write_ptr; /* 1-st empty entry (index) host_w*/
|
||||
int read_ptr; /* last used entry (index) host_r*/
|
||||
/* use for monitoring and recovering the stuck queue */
|
||||
|
@ -373,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
|||
struct sk_buff_head *skbs);
|
||||
void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
|
||||
|
||||
static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
|
||||
{
|
||||
struct iwl_tfd_tb *tb = &tfd->tbs[idx];
|
||||
|
||||
return le16_to_cpu(tb->hi_n_len) >> 4;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* Error handling
|
||||
******************************************************/
|
||||
|
|
|
@ -850,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
|
|||
trans_pcie->ict_index, read);
|
||||
trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
|
||||
trans_pcie->ict_index =
|
||||
iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
|
||||
((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
|
||||
|
||||
read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
|
||||
trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include "iwl-csr.h"
|
||||
#include "iwl-prph.h"
|
||||
#include "iwl-agn-hw.h"
|
||||
#include "iwl-fw-error-dump.h"
|
||||
#include "internal.h"
|
||||
|
||||
static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
|
||||
|
@ -1337,8 +1338,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
|
|||
IWL_ERR(trans,
|
||||
"Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
|
||||
cnt, active ? "" : "in", fifo, tbl_dw,
|
||||
iwl_read_prph(trans,
|
||||
SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1),
|
||||
iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
|
||||
(TFD_QUEUE_SIZE_MAX - 1),
|
||||
iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
|
||||
}
|
||||
|
||||
|
@ -1669,6 +1670,61 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
|
|||
IWL_ERR(trans, "failed to create the trans debugfs entry\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
|
||||
{
|
||||
u32 cmdlen = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IWL_NUM_OF_TBS; i++)
|
||||
cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
|
||||
|
||||
return cmdlen;
|
||||
}
|
||||
|
||||
static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
|
||||
void *buf, u32 buflen)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
struct iwl_fw_error_dump_data *data;
|
||||
struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
|
||||
struct iwl_fw_error_dump_txcmd *txcmd;
|
||||
u32 len;
|
||||
int i, ptr;
|
||||
|
||||
if (!buf)
|
||||
return sizeof(*data) +
|
||||
cmdq->q.n_window * (sizeof(*txcmd) +
|
||||
TFD_MAX_PAYLOAD_SIZE);
|
||||
|
||||
len = 0;
|
||||
data = buf;
|
||||
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
|
||||
txcmd = (void *)data->data;
|
||||
spin_lock_bh(&cmdq->lock);
|
||||
ptr = cmdq->q.write_ptr;
|
||||
for (i = 0; i < cmdq->q.n_window; i++) {
|
||||
u8 idx = get_cmd_index(&cmdq->q, ptr);
|
||||
u32 caplen, cmdlen;
|
||||
|
||||
cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
|
||||
caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
|
||||
|
||||
if (cmdlen) {
|
||||
len += sizeof(*txcmd) + caplen;
|
||||
txcmd->cmdlen = cpu_to_le32(cmdlen);
|
||||
txcmd->caplen = cpu_to_le32(caplen);
|
||||
memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
|
||||
txcmd = (void *)((u8 *)txcmd->data + caplen);
|
||||
}
|
||||
|
||||
ptr = iwl_queue_dec_wrap(ptr);
|
||||
}
|
||||
spin_unlock_bh(&cmdq->lock);
|
||||
|
||||
data->len = cpu_to_le32(len);
|
||||
return sizeof(*data) + len;
|
||||
}
|
||||
#else
|
||||
static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
|
||||
struct dentry *dir)
|
||||
|
@ -1711,6 +1767,10 @@ static const struct iwl_trans_ops trans_ops_pcie = {
|
|||
.grab_nic_access = iwl_trans_pcie_grab_nic_access,
|
||||
.release_nic_access = iwl_trans_pcie_release_nic_access,
|
||||
.set_bits_mask = iwl_trans_pcie_set_bits_mask,
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
.dump_data = iwl_trans_pcie_dump_data,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
|
||||
|
|
|
@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q)
|
|||
|
||||
/*
|
||||
* To avoid ambiguity between empty and completely full queues, there
|
||||
* should always be less than q->n_bd elements in the queue.
|
||||
* If q->n_window is smaller than q->n_bd, there is no need to reserve
|
||||
* any queue entries for this purpose.
|
||||
* should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
|
||||
* If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
|
||||
* to reserve any queue entries for this purpose.
|
||||
*/
|
||||
if (q->n_window < q->n_bd)
|
||||
if (q->n_window < TFD_QUEUE_SIZE_MAX)
|
||||
max = q->n_window;
|
||||
else
|
||||
max = q->n_bd - 1;
|
||||
max = TFD_QUEUE_SIZE_MAX - 1;
|
||||
|
||||
/*
|
||||
* q->n_bd is a power of 2, so the following is equivalent to modulo by
|
||||
* q->n_bd and is well defined for negative dividends.
|
||||
* TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
|
||||
* modulo by TFD_QUEUE_SIZE_MAX and is well defined.
|
||||
*/
|
||||
used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
|
||||
used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
|
||||
|
||||
if (WARN_ON(used > max))
|
||||
return 0;
|
||||
|
@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q)
|
|||
/*
|
||||
* iwl_queue_init - Initialize queue's high/low-water and read/write indexes
|
||||
*/
|
||||
static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id)
|
||||
static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
|
||||
{
|
||||
q->n_bd = count;
|
||||
q->n_window = slots_num;
|
||||
q->id = id;
|
||||
|
||||
/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
|
||||
* and iwl_queue_dec_wrap are broken. */
|
||||
if (WARN_ON(!is_power_of_2(count)))
|
||||
return -EINVAL;
|
||||
|
||||
/* slots_num must be power-of-two size, otherwise
|
||||
* get_cmd_index is broken. */
|
||||
if (WARN_ON(!is_power_of_2(slots_num)))
|
||||
|
@ -197,13 +191,13 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
|
|||
IWL_ERR(trans,
|
||||
"Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
|
||||
i, active ? "" : "in", fifo, tbl_dw,
|
||||
iwl_read_prph(trans,
|
||||
SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1),
|
||||
iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
|
||||
(TFD_QUEUE_SIZE_MAX - 1),
|
||||
iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
|
||||
}
|
||||
|
||||
for (i = q->read_ptr; i != q->write_ptr;
|
||||
i = iwl_queue_inc_wrap(i, q->n_bd))
|
||||
i = iwl_queue_inc_wrap(i))
|
||||
IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
|
||||
le32_to_cpu(txq->scratchbufs[i].scratch));
|
||||
|
||||
|
@ -359,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
|
|||
return addr;
|
||||
}
|
||||
|
||||
static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
|
||||
{
|
||||
struct iwl_tfd_tb *tb = &tfd->tbs[idx];
|
||||
|
||||
return le16_to_cpu(tb->hi_n_len) >> 4;
|
||||
}
|
||||
|
||||
static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
|
||||
dma_addr_t addr, u16 len)
|
||||
{
|
||||
|
@ -425,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
|
|||
{
|
||||
struct iwl_tfd *tfd_tmp = txq->tfds;
|
||||
|
||||
/* rd_ptr is bounded by n_bd and idx is bounded by n_window */
|
||||
/* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
|
||||
* idx is bounded by n_window
|
||||
*/
|
||||
int rd_ptr = txq->q.read_ptr;
|
||||
int idx = get_cmd_index(&txq->q, rd_ptr);
|
||||
|
||||
lockdep_assert_held(&txq->lock);
|
||||
|
||||
/* We have only q->n_window txq->entries, but we use q->n_bd tfds */
|
||||
/* We have only q->n_window txq->entries, but we use
|
||||
* TFD_QUEUE_SIZE_MAX tfds
|
||||
*/
|
||||
iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
|
||||
|
||||
/* free SKB */
|
||||
|
@ -452,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
|
|||
}
|
||||
|
||||
static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
|
||||
dma_addr_t addr, u16 len, u8 reset)
|
||||
dma_addr_t addr, u16 len, bool reset)
|
||||
{
|
||||
struct iwl_queue *q;
|
||||
struct iwl_tfd *tfd, *tfd_tmp;
|
||||
|
@ -565,8 +556,7 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
|
|||
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
|
||||
|
||||
/* Initialize queue's high/low-water marks, and head/tail indexes */
|
||||
ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num,
|
||||
txq_id);
|
||||
ret = iwl_queue_init(&txq->q, slots_num, txq_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -591,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
|
|||
struct iwl_txq *txq = &trans_pcie->txq[txq_id];
|
||||
struct iwl_queue *q = &txq->q;
|
||||
|
||||
if (!q->n_bd)
|
||||
return;
|
||||
|
||||
spin_lock_bh(&txq->lock);
|
||||
while (q->write_ptr != q->read_ptr) {
|
||||
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
|
||||
txq_id, q->read_ptr);
|
||||
iwl_pcie_txq_free_tfd(trans, txq);
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
|
||||
}
|
||||
txq->active = false;
|
||||
spin_unlock_bh(&txq->lock);
|
||||
|
@ -636,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
|
|||
}
|
||||
|
||||
/* De-alloc circular buffer of TFDs */
|
||||
if (txq->q.n_bd) {
|
||||
dma_free_coherent(dev, sizeof(struct iwl_tfd) *
|
||||
txq->q.n_bd, txq->tfds, txq->q.dma_addr);
|
||||
if (txq->tfds) {
|
||||
dma_free_coherent(dev,
|
||||
sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
|
||||
txq->tfds, txq->q.dma_addr);
|
||||
txq->q.dma_addr = 0;
|
||||
txq->tfds = NULL;
|
||||
|
||||
dma_free_coherent(dev,
|
||||
sizeof(*txq->scratchbufs) * txq->q.n_window,
|
||||
|
@ -948,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
|||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
struct iwl_txq *txq = &trans_pcie->txq[txq_id];
|
||||
/* n_bd is usually 256 => n_bd - 1 = 0xff */
|
||||
int tfd_num = ssn & (txq->q.n_bd - 1);
|
||||
int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
|
||||
struct iwl_queue *q = &txq->q;
|
||||
int last_to_free;
|
||||
|
||||
|
@ -973,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
|||
|
||||
/*Since we free until index _not_ inclusive, the one before index is
|
||||
* the last we will free. This one must be used */
|
||||
last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd);
|
||||
last_to_free = iwl_queue_dec_wrap(tfd_num);
|
||||
|
||||
if (!iwl_queue_used(q, last_to_free)) {
|
||||
IWL_ERR(trans,
|
||||
"%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
|
||||
__func__, txq_id, last_to_free, q->n_bd,
|
||||
__func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
|
||||
q->write_ptr, q->read_ptr);
|
||||
goto out;
|
||||
}
|
||||
|
@ -988,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
|||
|
||||
for (;
|
||||
q->read_ptr != tfd_num;
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
|
||||
|
||||
if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
|
||||
continue;
|
||||
|
@ -1027,16 +1015,16 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
|
|||
|
||||
lockdep_assert_held(&txq->lock);
|
||||
|
||||
if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) {
|
||||
if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
|
||||
IWL_ERR(trans,
|
||||
"%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
|
||||
__func__, txq_id, idx, q->n_bd,
|
||||
__func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
|
||||
q->write_ptr, q->read_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
|
||||
for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
|
||||
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
|
||||
|
||||
if (nfreed++ > 0) {
|
||||
IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
|
||||
|
@ -1327,27 +1315,38 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
|
|||
cmd_pos = offsetof(struct iwl_device_cmd, payload);
|
||||
copy_size = sizeof(out_cmd->hdr);
|
||||
for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
|
||||
int copy = 0;
|
||||
int copy;
|
||||
|
||||
if (!cmd->len[i])
|
||||
continue;
|
||||
|
||||
/* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
|
||||
/* copy everything if not nocopy/dup */
|
||||
if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
|
||||
IWL_HCMD_DFL_DUP))) {
|
||||
copy = cmd->len[i];
|
||||
|
||||
memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
|
||||
cmd_pos += copy;
|
||||
copy_size += copy;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
|
||||
* in total (for the scratchbuf handling), but copy up to what
|
||||
* we can fit into the payload for debug dump purposes.
|
||||
*/
|
||||
copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]);
|
||||
|
||||
memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
|
||||
cmd_pos += copy;
|
||||
|
||||
/* However, treat copy_size the proper way, we need it below */
|
||||
if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
|
||||
copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
|
||||
|
||||
if (copy > cmd->len[i])
|
||||
copy = cmd->len[i];
|
||||
}
|
||||
|
||||
/* copy everything if not nocopy/dup */
|
||||
if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
|
||||
IWL_HCMD_DFL_DUP)))
|
||||
copy = cmd->len[i];
|
||||
|
||||
if (copy) {
|
||||
memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
|
||||
cmd_pos += copy;
|
||||
copy_size += copy;
|
||||
}
|
||||
}
|
||||
|
@ -1363,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
|
|||
memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
|
||||
iwl_pcie_txq_build_tfd(trans, txq,
|
||||
iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
|
||||
scratch_size, 1);
|
||||
scratch_size, true);
|
||||
|
||||
/* map first command fragment, if any remains */
|
||||
if (copy_size > scratch_size) {
|
||||
|
@ -1379,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
|
|||
}
|
||||
|
||||
iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
|
||||
copy_size - scratch_size, 0);
|
||||
copy_size - scratch_size, false);
|
||||
}
|
||||
|
||||
/* map the remaining (adjusted) nocopy/dup fragments */
|
||||
|
@ -1402,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
|
|||
goto out;
|
||||
}
|
||||
|
||||
iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0);
|
||||
iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
|
||||
}
|
||||
|
||||
out_meta->flags = cmd->flags;
|
||||
|
@ -1445,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
|
|||
}
|
||||
|
||||
/* Increment and update queue's write index */
|
||||
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
|
||||
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
|
||||
iwl_pcie_txq_inc_wr_ptr(trans, txq);
|
||||
|
||||
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
|
||||
|
@ -1740,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
|||
memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
|
||||
IWL_HCMD_SCRATCHBUF_SIZE);
|
||||
iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
|
||||
IWL_HCMD_SCRATCHBUF_SIZE, 1);
|
||||
IWL_HCMD_SCRATCHBUF_SIZE, true);
|
||||
|
||||
/* there must be data left over for TB1 or this code must be changed */
|
||||
BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE);
|
||||
|
@ -1750,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
|||
tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
|
||||
if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
|
||||
goto out_err;
|
||||
iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0);
|
||||
iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
|
||||
|
||||
/*
|
||||
* Set up TFD's third entry to point directly to remainder
|
||||
|
@ -1766,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
|||
&txq->tfds[q->write_ptr]);
|
||||
goto out_err;
|
||||
}
|
||||
iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0);
|
||||
iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
|
||||
}
|
||||
|
||||
/* Set up entry for this TFD in Tx byte-count array */
|
||||
|
@ -1788,7 +1787,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
|||
mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
|
||||
|
||||
/* Tell device the write index *just past* this latest filled TFD */
|
||||
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
|
||||
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
|
||||
if (!wait_write_ptr)
|
||||
iwl_pcie_txq_inc_wr_ptr(trans, txq);
|
||||
|
||||
|
|
Loading…
Reference in New Issue