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
623c43875f
|
@ -322,12 +322,6 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
|
|||
|
||||
flush_workqueue(priv->workqueue);
|
||||
|
||||
/* User space software may expect getting rfkill changes
|
||||
* even if interface is down, trans->down will leave the RF
|
||||
* kill interrupt enabled
|
||||
*/
|
||||
iwl_trans_stop_hw(priv->trans, false);
|
||||
|
||||
IWL_DEBUG_MAC80211(priv, "leave\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -1313,7 +1313,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
|
|||
}
|
||||
|
||||
/* Reset chip to save power until we load uCode during "up". */
|
||||
iwl_trans_stop_hw(priv->trans, false);
|
||||
iwl_trans_stop_device(priv->trans);
|
||||
|
||||
priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg,
|
||||
priv->eeprom_blob,
|
||||
|
@ -1458,7 +1458,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
|
|||
|
||||
dev_kfree_skb(priv->beacon_skb);
|
||||
|
||||
iwl_trans_stop_hw(priv->trans, true);
|
||||
iwl_trans_op_mode_leave(priv->trans);
|
||||
ieee80211_free_hw(priv->hw);
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ static const struct iwl_base_params iwl7000_base_params = {
|
|||
};
|
||||
|
||||
static const struct iwl_ht_params iwl7000_ht_params = {
|
||||
.use_rts_for_aggregation = true, /* use rts/cts protection */
|
||||
.stbc = true,
|
||||
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
|
||||
};
|
||||
|
||||
|
|
|
@ -162,12 +162,14 @@ struct iwl_base_params {
|
|||
};
|
||||
|
||||
/*
|
||||
* @stbc: support Tx STBC and 1*SS Rx STBC
|
||||
* @use_rts_for_aggregation: use rts/cts protection for HT traffic
|
||||
* @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40
|
||||
*/
|
||||
struct iwl_ht_params {
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
const bool ht_greenfield_support; /* if used set to true */
|
||||
const bool stbc;
|
||||
bool use_rts_for_aggregation;
|
||||
u8 ht40_bands;
|
||||
};
|
||||
|
|
|
@ -322,6 +322,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces,
|
|||
pieces->img[type].sec[sec].offset = offset;
|
||||
}
|
||||
|
||||
static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
|
||||
{
|
||||
int i, j;
|
||||
struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
|
||||
struct iwl_fw_cipher_scheme *fwcs;
|
||||
struct ieee80211_cipher_scheme *cs;
|
||||
u32 cipher;
|
||||
|
||||
if (len < sizeof(*l) ||
|
||||
len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
|
||||
fwcs = &l->cs[j];
|
||||
cipher = le32_to_cpu(fwcs->cipher);
|
||||
|
||||
/* we skip schemes with zero cipher suite selector */
|
||||
if (!cipher)
|
||||
continue;
|
||||
|
||||
cs = &fw->cs[j++];
|
||||
cs->cipher = cipher;
|
||||
cs->iftype = BIT(NL80211_IFTYPE_STATION);
|
||||
cs->hdr_len = fwcs->hdr_len;
|
||||
cs->pn_len = fwcs->pn_len;
|
||||
cs->pn_off = fwcs->pn_off;
|
||||
cs->key_idx_off = fwcs->key_idx_off;
|
||||
cs->key_idx_mask = fwcs->key_idx_mask;
|
||||
cs->key_idx_shift = fwcs->key_idx_shift;
|
||||
cs->mic_len = fwcs->mic_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets uCode section from tlv.
|
||||
*/
|
||||
|
@ -729,6 +764,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IWL_UCODE_TLV_CSCHEME:
|
||||
if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
|
||||
goto invalid_tlv_len;
|
||||
break;
|
||||
default:
|
||||
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
|
||||
break;
|
||||
|
|
|
@ -751,6 +751,13 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
|
|||
ht_info->ht_supported = true;
|
||||
ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
|
||||
|
||||
if (cfg->ht_params->stbc) {
|
||||
ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
|
||||
|
||||
if (tx_chains > 1)
|
||||
ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
|
||||
}
|
||||
|
||||
if (iwlwifi_mod_params.amsdu_size_8K)
|
||||
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ enum iwl_ucode_tlv_type {
|
|||
IWL_UCODE_TLV_SECURE_SEC_INIT = 25,
|
||||
IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
|
||||
IWL_UCODE_TLV_NUM_OF_CPU = 27,
|
||||
IWL_UCODE_TLV_CSCHEME = 28,
|
||||
};
|
||||
|
||||
struct iwl_ucode_tlv {
|
||||
|
|
|
@ -92,6 +92,9 @@
|
|||
* @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
|
||||
* @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
|
||||
* containing CAM (Continuous Active Mode) indication.
|
||||
* @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a
|
||||
* single bound interface).
|
||||
* @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
|
||||
*/
|
||||
enum iwl_ucode_tlv_flag {
|
||||
IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
|
||||
|
@ -113,7 +116,9 @@ enum iwl_ucode_tlv_flag {
|
|||
IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17),
|
||||
IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19),
|
||||
IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD = BIT(20),
|
||||
IWL_UCODE_TLV_FLAGS_P2P_PS = BIT(21),
|
||||
IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
|
||||
IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
|
||||
};
|
||||
|
||||
/* The default calibrate table size if not specified by firmware file */
|
||||
|
@ -209,6 +214,44 @@ enum iwl_fw_phy_cfg {
|
|||
FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
|
||||
};
|
||||
|
||||
#define IWL_UCODE_MAX_CS 1
|
||||
|
||||
/**
|
||||
* struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
|
||||
* @cipher: a cipher suite selector
|
||||
* @flags: cipher scheme flags (currently reserved for a future use)
|
||||
* @hdr_len: a size of MPDU security header
|
||||
* @pn_len: a size of PN
|
||||
* @pn_off: an offset of pn from the beginning of the security header
|
||||
* @key_idx_off: an offset of key index byte in the security header
|
||||
* @key_idx_mask: a bit mask of key_idx bits
|
||||
* @key_idx_shift: bit shift needed to get key_idx
|
||||
* @mic_len: mic length in bytes
|
||||
* @hw_cipher: a HW cipher index used in host commands
|
||||
*/
|
||||
struct iwl_fw_cipher_scheme {
|
||||
__le32 cipher;
|
||||
u8 flags;
|
||||
u8 hdr_len;
|
||||
u8 pn_len;
|
||||
u8 pn_off;
|
||||
u8 key_idx_off;
|
||||
u8 key_idx_mask;
|
||||
u8 key_idx_shift;
|
||||
u8 mic_len;
|
||||
u8 hw_cipher;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_cscheme_list - a cipher scheme list
|
||||
* @size: a number of entries
|
||||
* @cs: cipher scheme entries
|
||||
*/
|
||||
struct iwl_fw_cscheme_list {
|
||||
u8 size;
|
||||
struct iwl_fw_cipher_scheme cs[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw - variables associated with the firmware
|
||||
*
|
||||
|
@ -224,6 +267,7 @@ enum iwl_fw_phy_cfg {
|
|||
* @inst_evtlog_size: event log size for runtime ucode.
|
||||
* @inst_errlog_ptr: error log offfset for runtime ucode.
|
||||
* @mvm_fw: indicates this is MVM firmware
|
||||
* @cipher_scheme: optional external cipher scheme.
|
||||
*/
|
||||
struct iwl_fw {
|
||||
u32 ucode_ver;
|
||||
|
@ -243,6 +287,8 @@ struct iwl_fw {
|
|||
u32 phy_config;
|
||||
|
||||
bool mvm_fw;
|
||||
|
||||
struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
|
||||
};
|
||||
|
||||
static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
|
||||
|
|
|
@ -263,13 +263,20 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
|
|||
struct iwl_nvm_data *data,
|
||||
struct ieee80211_sta_vht_cap *vht_cap)
|
||||
{
|
||||
int num_ants = num_of_ant(data->valid_rx_ant);
|
||||
int bf_sts_cap = num_ants - 1;
|
||||
|
||||
vht_cap->vht_supported = true;
|
||||
|
||||
vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_1 |
|
||||
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||
bf_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
|
||||
7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
|
||||
|
||||
if (num_ants > 1)
|
||||
vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
|
||||
|
||||
if (iwlwifi_mod_params.amsdu_size_8K)
|
||||
vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
|
||||
|
||||
|
@ -283,16 +290,22 @@ 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 (num_of_ant(data->valid_rx_ant) == 1 ||
|
||||
/* Max rate for Long GI NSS=2 80Mhz is 780Mbps */
|
||||
vht_cap->vht_mcs.rx_highest = cpu_to_le16(780);
|
||||
|
||||
if (num_ants == 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 */
|
||||
vht_cap->vht_mcs.rx_mcs_map |=
|
||||
cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
|
||||
/* Max rate for Long GI NSS=1 80Mhz is 390Mbps */
|
||||
vht_cap->vht_mcs.rx_highest = cpu_to_le16(390);
|
||||
}
|
||||
|
||||
vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
|
||||
vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest;
|
||||
}
|
||||
|
||||
static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
|
||||
|
|
|
@ -155,14 +155,12 @@ void iwl_opmode_deregister(const char *name);
|
|||
|
||||
/**
|
||||
* struct iwl_op_mode - operational mode
|
||||
* @ops - pointer to its own ops
|
||||
*
|
||||
* This holds an implementation of the mac80211 / fw API.
|
||||
*
|
||||
* @ops - pointer to its own ops
|
||||
*/
|
||||
struct iwl_op_mode {
|
||||
const struct iwl_op_mode_ops *ops;
|
||||
const struct iwl_trans *trans;
|
||||
|
||||
char op_mode_specific[0] __aligned(sizeof(void *));
|
||||
};
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#include "iwl-debug.h"
|
||||
#include "iwl-config.h"
|
||||
#include "iwl-fw.h"
|
||||
#include "iwl-op-mode.h"
|
||||
|
||||
/**
|
||||
* DOC: Transport layer - what is it ?
|
||||
|
@ -100,8 +101,7 @@
|
|||
* start_fw
|
||||
*
|
||||
* 5) Then when finished (or reset):
|
||||
* stop_fw (a.k.a. stop device for the moment)
|
||||
* stop_hw
|
||||
* stop_device
|
||||
*
|
||||
* 6) Eventually, the free function will be called.
|
||||
*/
|
||||
|
@ -317,6 +317,24 @@ enum iwl_d3_status {
|
|||
IWL_D3_STATUS_RESET,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_trans_status: transport status flags
|
||||
* @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
|
||||
* @STATUS_DEVICE_ENABLED: APM is enabled
|
||||
* @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
|
||||
* @STATUS_INT_ENABLED: interrupts are enabled
|
||||
* @STATUS_RFKILL: the HW RFkill switch is in KILL position
|
||||
* @STATUS_FW_ERROR: the fw is in error state
|
||||
*/
|
||||
enum iwl_trans_status {
|
||||
STATUS_SYNC_HCMD_ACTIVE,
|
||||
STATUS_DEVICE_ENABLED,
|
||||
STATUS_TPOWER_PMI,
|
||||
STATUS_INT_ENABLED,
|
||||
STATUS_RFKILL,
|
||||
STATUS_FW_ERROR,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_trans_config - transport configuration
|
||||
*
|
||||
|
@ -361,9 +379,7 @@ struct iwl_trans;
|
|||
*
|
||||
* @start_hw: starts the HW- from that point on, the HW can send interrupts
|
||||
* May sleep
|
||||
* @stop_hw: stops the HW- from that point on, the HW will be in low power but
|
||||
* will still issue interrupt if the HW RF kill is triggered unless
|
||||
* op_mode_leaving is true.
|
||||
* @op_mode_leave: Turn off the HW RF kill indication if on
|
||||
* May sleep
|
||||
* @start_fw: allocates and inits all the resources for the transport
|
||||
* layer. Also kick a fw image.
|
||||
|
@ -371,8 +387,11 @@ struct iwl_trans;
|
|||
* @fw_alive: called when the fw sends alive notification. If the fw provides
|
||||
* the SCD base address in SRAM, then provide it here, or 0 otherwise.
|
||||
* May sleep
|
||||
* @stop_device:stops the whole device (embedded CPU put to reset)
|
||||
* May sleep
|
||||
* @stop_device: stops the whole device (embedded CPU put to reset) and stops
|
||||
* the HW. From that point on, the HW will be in low power but will still
|
||||
* issue interrupt if the HW RF kill is triggered. This callback must do
|
||||
* the right thing and not crash even if start_hw() was called but not
|
||||
* start_fw(). May sleep
|
||||
* @d3_suspend: put the device into the correct mode for WoWLAN during
|
||||
* suspend. This is optional, if not implemented WoWLAN will not be
|
||||
* supported. This callback may sleep.
|
||||
|
@ -418,7 +437,7 @@ struct iwl_trans;
|
|||
struct iwl_trans_ops {
|
||||
|
||||
int (*start_hw)(struct iwl_trans *iwl_trans);
|
||||
void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving);
|
||||
void (*op_mode_leave)(struct iwl_trans *iwl_trans);
|
||||
int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
|
||||
bool run_in_rfkill);
|
||||
void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
|
||||
|
@ -479,6 +498,7 @@ enum iwl_trans_state {
|
|||
* @ops - pointer to iwl_trans_ops
|
||||
* @op_mode - pointer to the op_mode
|
||||
* @cfg - pointer to the configuration
|
||||
* @status: a bit-mask of transport status flags
|
||||
* @dev - pointer to struct device * that represents the device
|
||||
* @hw_id: a u32 with the ID of the device / subdevice.
|
||||
* Set during transport allocation.
|
||||
|
@ -499,6 +519,7 @@ struct iwl_trans {
|
|||
struct iwl_op_mode *op_mode;
|
||||
const struct iwl_cfg *cfg;
|
||||
enum iwl_trans_state state;
|
||||
unsigned long status;
|
||||
|
||||
struct device *dev;
|
||||
u32 hw_rev;
|
||||
|
@ -540,15 +561,14 @@ static inline int iwl_trans_start_hw(struct iwl_trans *trans)
|
|||
return trans->ops->start_hw(trans);
|
||||
}
|
||||
|
||||
static inline void iwl_trans_stop_hw(struct iwl_trans *trans,
|
||||
bool op_mode_leaving)
|
||||
static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trans->ops->stop_hw(trans, op_mode_leaving);
|
||||
if (trans->ops->op_mode_leave)
|
||||
trans->ops->op_mode_leave(trans);
|
||||
|
||||
if (op_mode_leaving)
|
||||
trans->op_mode = NULL;
|
||||
trans->op_mode = NULL;
|
||||
|
||||
trans->state = IWL_TRANS_NO_FW;
|
||||
}
|
||||
|
@ -570,6 +590,7 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans,
|
|||
|
||||
WARN_ON_ONCE(!trans->rx_mpdu_cmd);
|
||||
|
||||
clear_bit(STATUS_FW_ERROR, &trans->status);
|
||||
return trans->ops->start_fw(trans, fw, run_in_rfkill);
|
||||
}
|
||||
|
||||
|
@ -601,6 +622,9 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
|
||||
return -EIO;
|
||||
|
||||
if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
|
||||
IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
|
||||
return -EIO;
|
||||
|
@ -640,6 +664,9 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
|
|||
static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
||||
struct iwl_device_cmd *dev_cmd, int queue)
|
||||
{
|
||||
if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
|
||||
return -EIO;
|
||||
|
||||
if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
|
||||
IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
|
||||
|
||||
|
@ -760,7 +787,8 @@ static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
|
|||
|
||||
static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
|
||||
{
|
||||
trans->ops->set_pmi(trans, state);
|
||||
if (trans->ops->set_pmi)
|
||||
trans->ops->set_pmi(trans, state);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -780,6 +808,16 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
|
|||
__release(nic_access);
|
||||
}
|
||||
|
||||
static inline void iwl_trans_fw_error(struct iwl_trans *trans)
|
||||
{
|
||||
if (WARN_ON_ONCE(!trans->op_mode))
|
||||
return;
|
||||
|
||||
/* prevent double restarts due to the same erroneous FW */
|
||||
if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
|
||||
iwl_op_mode_nic_error(trans->op_mode);
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* driver (transport) register/unregister functions
|
||||
******************************************************/
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
obj-$(CONFIG_IWLMVM) += iwlmvm.o
|
||||
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
|
||||
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 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
|
||||
|
||||
|
|
|
@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
|||
if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Update SF - Disable if needed. if this fails, SF might still be on
|
||||
* while many macs are bound, which is forbidden - so fail the binding.
|
||||
*/
|
||||
if (iwl_mvm_sf_update(mvm, vif, false))
|
||||
return -EINVAL;
|
||||
|
||||
return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
|
||||
}
|
||||
|
||||
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
|
||||
return -EINVAL;
|
||||
|
||||
return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
|
||||
ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
|
||||
|
||||
if (!ret)
|
||||
if (iwl_mvm_sf_update(mvm, vif, true))
|
||||
IWL_ERR(mvm, "Failed to update SF state\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,150 @@
|
|||
#include "mvm.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
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;
|
||||
case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
|
||||
IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
|
||||
dbgfs_pm->uapsd_misbehaving = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
enum iwl_dbgfs_pm_mask param;
|
||||
int val, ret;
|
||||
|
||||
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 if (!strncmp("uapsd_misbehaving=", buf, 18)) {
|
||||
if (sscanf(buf + 18, "%d", &val) != 1)
|
||||
return -EINVAL;
|
||||
param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
|
||||
} 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->mvm;
|
||||
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)
|
||||
|
@ -125,6 +269,201 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
|
|||
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
}
|
||||
|
||||
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 ieee80211_vif *vif, char *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
enum iwl_dbgfs_bf_mask param;
|
||||
int value, ret = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
|
||||
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
|
||||
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
|
||||
_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
|
||||
#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \
|
||||
if (!debugfs_create_file(#name, mode, parent, vif, \
|
||||
&iwl_dbgfs_##name##_ops)) \
|
||||
|
@ -132,6 +471,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
|
|||
} while (0)
|
||||
|
||||
MVM_DEBUGFS_READ_FILE_OPS(mac_params);
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
|
||||
|
||||
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
||||
{
|
||||
|
@ -155,9 +496,21 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
|||
return;
|
||||
}
|
||||
|
||||
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
|
||||
((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
|
||||
(vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
|
||||
mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)))
|
||||
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
|
||||
|
|
|
@ -85,6 +85,8 @@
|
|||
* PBW Snoozing enabled
|
||||
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
|
||||
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
|
||||
* @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
|
||||
* detection enablement
|
||||
*/
|
||||
enum iwl_power_flags {
|
||||
POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0),
|
||||
|
@ -94,6 +96,7 @@ enum iwl_power_flags {
|
|||
POWER_FLAGS_BT_SCO_ENA = BIT(8),
|
||||
POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
|
||||
POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
|
||||
POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12),
|
||||
};
|
||||
|
||||
#define IWL_POWER_VEC_SIZE 5
|
||||
|
@ -228,6 +231,19 @@ struct iwl_mac_power_cmd {
|
|||
u8 reserved;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
|
||||
* associated AP is identified as improperly implementing uAPSD protocol.
|
||||
* PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78
|
||||
* @sta_id: index of station in uCode's station table - associated AP ID in
|
||||
* this context.
|
||||
*/
|
||||
struct iwl_uapsd_misbehaving_ap_notif {
|
||||
__le32 sta_id;
|
||||
u8 mac_id;
|
||||
u8 reserved[3];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_beacon_filter_cmd
|
||||
* REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
|
||||
|
|
|
@ -138,7 +138,14 @@ enum iwl_sta_flags {
|
|||
|
||||
/**
|
||||
* enum iwl_sta_key_flag - key flags for the ADD_STA host command
|
||||
* @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
|
||||
* @STA_KEY_FLG_NO_ENC: no encryption
|
||||
* @STA_KEY_FLG_WEP: WEP encryption algorithm
|
||||
* @STA_KEY_FLG_CCM: CCMP encryption algorithm
|
||||
* @STA_KEY_FLG_TKIP: TKIP encryption algorithm
|
||||
* @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
|
||||
* @STA_KEY_FLG_CMAC: CMAC encryption algorithm
|
||||
* @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
|
||||
* @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
|
||||
* @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
|
||||
* station info array (1 - n 1X mode)
|
||||
* @STA_KEY_FLG_KEYID_MSK: the index of the key
|
||||
|
@ -152,6 +159,7 @@ enum iwl_sta_key_flag {
|
|||
STA_KEY_FLG_WEP = (1 << 0),
|
||||
STA_KEY_FLG_CCM = (2 << 0),
|
||||
STA_KEY_FLG_TKIP = (3 << 0),
|
||||
STA_KEY_FLG_EXT = (4 << 0),
|
||||
STA_KEY_FLG_CMAC = (6 << 0),
|
||||
STA_KEY_FLG_ENC_UNKNOWN = (7 << 0),
|
||||
STA_KEY_FLG_EN_MSK = (7 << 0),
|
||||
|
|
|
@ -132,6 +132,7 @@ enum iwl_tx_flags {
|
|||
#define TX_CMD_SEC_WEP 0x01
|
||||
#define TX_CMD_SEC_CCM 0x02
|
||||
#define TX_CMD_SEC_TKIP 0x03
|
||||
#define TX_CMD_SEC_EXT 0x04
|
||||
#define TX_CMD_SEC_MSK 0x07
|
||||
#define TX_CMD_SEC_WEP_KEY_IDX_POS 6
|
||||
#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0
|
||||
|
|
|
@ -141,6 +141,7 @@ enum {
|
|||
|
||||
/* Power - legacy power table command */
|
||||
POWER_TABLE_CMD = 0x77,
|
||||
PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
|
||||
|
||||
/* Thermal Throttling*/
|
||||
REPLY_THERMAL_MNG_BACKOFF = 0x7e,
|
||||
|
@ -183,6 +184,7 @@ enum {
|
|||
BT_PROFILE_NOTIFICATION = 0xce,
|
||||
BT_COEX_CI = 0x5d,
|
||||
|
||||
REPLY_SF_CFG_CMD = 0xd1,
|
||||
REPLY_BEACON_FILTERING_CMD = 0xd2,
|
||||
|
||||
REPLY_DEBUG_CMD = 0xf0,
|
||||
|
@ -1052,6 +1054,7 @@ enum iwl_mvm_rx_status {
|
|||
RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8),
|
||||
RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8),
|
||||
|
@ -1131,6 +1134,7 @@ struct iwl_set_calib_default_cmd {
|
|||
} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
|
||||
|
||||
#define MAX_PORT_ID_NUM 2
|
||||
#define MAX_MCAST_FILTERING_ADDRESSES 256
|
||||
|
||||
/**
|
||||
* struct iwl_mcast_filter_cmd - configure multicast filter.
|
||||
|
@ -1363,4 +1367,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
|
|||
struct mvm_statistics_general general;
|
||||
} __packed;
|
||||
|
||||
/***********************************
|
||||
* Smart Fifo API
|
||||
***********************************/
|
||||
/* Smart Fifo state */
|
||||
enum iwl_sf_state {
|
||||
SF_LONG_DELAY_ON = 0, /* should never be called by driver */
|
||||
SF_FULL_ON,
|
||||
SF_UNINIT,
|
||||
SF_INIT_OFF,
|
||||
SF_HW_NUM_STATES
|
||||
};
|
||||
|
||||
/* Smart Fifo possible scenario */
|
||||
enum iwl_sf_scenario {
|
||||
SF_SCENARIO_SINGLE_UNICAST,
|
||||
SF_SCENARIO_AGG_UNICAST,
|
||||
SF_SCENARIO_MULTICAST,
|
||||
SF_SCENARIO_BA_RESP,
|
||||
SF_SCENARIO_TX_RESP,
|
||||
SF_NUM_SCENARIO
|
||||
};
|
||||
|
||||
#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */
|
||||
#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
|
||||
|
||||
/* smart FIFO default values */
|
||||
#define SF_W_MARK_SISO 4096
|
||||
#define SF_W_MARK_MIMO2 8192
|
||||
#define SF_W_MARK_MIMO3 6144
|
||||
#define SF_W_MARK_LEGACY 4096
|
||||
#define SF_W_MARK_SCAN 4096
|
||||
|
||||
/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
|
||||
#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
|
||||
#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
|
||||
#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
|
||||
#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */
|
||||
#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */
|
||||
#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */
|
||||
#define SF_BA_IDLE_TIMER 320 /* 300 uSec */
|
||||
#define SF_BA_AGING_TIMER 2016 /* 2 mSec */
|
||||
#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */
|
||||
#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */
|
||||
|
||||
#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */
|
||||
|
||||
/**
|
||||
* Smart Fifo configuration command.
|
||||
* @state: smart fifo state, types listed in iwl_sf_sate.
|
||||
* @watermark: Minimum allowed availabe free space in RXF for transient state.
|
||||
* @long_delay_timeouts: aging and idle timer values for each scenario
|
||||
* in long delay state.
|
||||
* @full_on_timeouts: timer values for each scenario in full on state.
|
||||
*/
|
||||
struct iwl_sf_cfg_cmd {
|
||||
enum iwl_sf_state state;
|
||||
__le32 watermark[SF_TRANSIENT_STATES_NUMBER];
|
||||
__le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
|
||||
__le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
|
||||
} __packed; /* SF_CFG_API_S_VER_2 */
|
||||
|
||||
#endif /* __fw_api_h__ */
|
||||
|
|
|
@ -241,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
|
|||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (mvm->init_ucode_complete)
|
||||
if (WARN_ON_ONCE(mvm->init_ucode_complete))
|
||||
return 0;
|
||||
|
||||
iwl_init_notification_wait(&mvm->notif_wait,
|
||||
|
@ -287,7 +287,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
|
|||
IWL_DEBUG_RF_KILL(mvm,
|
||||
"jump over all phy activities due to RF kill\n");
|
||||
iwl_remove_notification(&mvm->notif_wait, &calib_wait);
|
||||
return 1;
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Send TX valid antennas before triggering calibrations */
|
||||
|
@ -319,9 +320,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
|
|||
error:
|
||||
iwl_remove_notification(&mvm->notif_wait, &calib_wait);
|
||||
out:
|
||||
if (!iwlmvm_mod_params.init_dbg) {
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
} else if (!mvm->nvm_data) {
|
||||
if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
|
||||
/* we want to debug INIT and we have no NVM - fake */
|
||||
mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
|
||||
sizeof(struct ieee80211_channel) +
|
||||
|
@ -370,11 +369,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
|||
ret = -ERFKILL;
|
||||
goto error;
|
||||
}
|
||||
/* should stop & start HW since that INIT image just loaded */
|
||||
iwl_trans_stop_hw(mvm->trans, false);
|
||||
ret = iwl_trans_start_hw(mvm->trans);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!iwlmvm_mod_params.init_dbg) {
|
||||
/*
|
||||
* should stop and start HW since that INIT
|
||||
* image just loaded
|
||||
*/
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
ret = iwl_trans_start_hw(mvm->trans);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (iwlmvm_mod_params.init_dbg)
|
||||
|
@ -386,6 +390,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
|||
goto error;
|
||||
}
|
||||
|
||||
ret = iwl_mvm_sf_update(mvm, NULL, false);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
|
||||
|
||||
ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
|
||||
if (ret)
|
||||
goto error;
|
||||
|
|
|
@ -261,6 +261,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
|||
|
||||
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
|
||||
|
||||
/* currently FW API supports only one optional cipher scheme */
|
||||
if (mvm->fw->cs && mvm->fw->cs->cipher) {
|
||||
mvm->hw->n_cipher_schemes = 1;
|
||||
mvm->hw->cipher_schemes = mvm->fw->cs;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
|
||||
mvm->trans->ops->d3_suspend &&
|
||||
|
@ -399,7 +405,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
|
|||
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
|
||||
{
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
iwl_trans_stop_hw(mvm->trans, false);
|
||||
|
||||
mvm->scan_status = IWL_MVM_SCAN_NONE;
|
||||
|
||||
|
@ -471,7 +476,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
|
|||
cancel_work_sync(&mvm->roc_done_wk);
|
||||
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
iwl_trans_stop_hw(mvm->trans, false);
|
||||
|
||||
iwl_mvm_async_handlers_purge(mvm);
|
||||
/* async_handlers_list is empty and will stay empty: HW is stopped */
|
||||
|
@ -488,17 +492,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
|
|||
cancel_work_sync(&mvm->async_handlers_wk);
|
||||
}
|
||||
|
||||
static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm *mvm = data;
|
||||
int ret;
|
||||
|
||||
ret = iwl_mvm_power_disable(mvm, vif);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "failed to disable power management\n");
|
||||
}
|
||||
|
||||
static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
|
@ -521,6 +514,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
s8 tx_power)
|
||||
{
|
||||
/* FW is in charge of regulatory enforcement */
|
||||
struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
|
||||
.mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
|
||||
.pwr_restriction = cpu_to_le16(tx_power),
|
||||
};
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
|
||||
sizeof(reduce_txpwr_cmd),
|
||||
&reduce_txpwr_cmd);
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
|
@ -541,26 +548,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* TODO: remove this temporary code.
|
||||
* Currently MVM FW supports power management only on single MAC.
|
||||
* If new interface added, disable PM on existing interface.
|
||||
* P2P device is a special case, since it is handled by FW similary to
|
||||
* scan. If P2P deviced is added, PM remains enabled on existing
|
||||
* interface.
|
||||
* Note: the method below does not count the new interface being added
|
||||
* at this moment.
|
||||
*/
|
||||
/* Counting number of interfaces is needed for legacy PM */
|
||||
if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
|
||||
mvm->vif_count++;
|
||||
if (mvm->vif_count > 1) {
|
||||
IWL_DEBUG_MAC80211(mvm,
|
||||
"Disable power on existing interfaces\n");
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_pm_disable_iterator, mvm);
|
||||
}
|
||||
|
||||
/*
|
||||
* The AP binding flow can be done only after the beacon
|
||||
|
@ -591,11 +581,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
goto out_release;
|
||||
|
||||
/*
|
||||
* Update power state on the new interface. Admittedly, based on
|
||||
* mac80211 logics this power update will disable power management
|
||||
*/
|
||||
iwl_mvm_power_update_mode(mvm, vif);
|
||||
iwl_mvm_power_disable(mvm, vif);
|
||||
|
||||
/* beacon filtering */
|
||||
ret = iwl_mvm_disable_beacon_filter(mvm, vif);
|
||||
|
@ -656,9 +642,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
|
|||
out_release:
|
||||
if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
|
||||
mvm->vif_count--;
|
||||
|
||||
/* TODO: remove this when legacy PM will be discarded */
|
||||
ieee80211_iterate_active_interfaces(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_update_iterator, mvm);
|
||||
|
||||
iwl_mvm_mac_ctxt_release(mvm, vif);
|
||||
out_unlock:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
@ -744,21 +733,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
|
|||
mvmvif->phy_ctxt = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: remove this temporary code.
|
||||
* Currently MVM FW supports power management only on single MAC.
|
||||
* Check if only one additional interface remains after removing
|
||||
* current one. Update power mode on the remaining interface.
|
||||
*/
|
||||
if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
|
||||
mvm->vif_count--;
|
||||
IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
|
||||
mvm->vif_count);
|
||||
if (mvm->vif_count == 1) {
|
||||
ieee80211_iterate_active_interfaces(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_update_iterator, mvm);
|
||||
}
|
||||
|
||||
/* TODO: remove this when legacy PM will be discarded */
|
||||
ieee80211_iterate_active_interfaces(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_update_iterator, mvm);
|
||||
|
||||
iwl_mvm_mac_ctxt_remove(mvm, vif);
|
||||
|
||||
|
@ -767,47 +748,116 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
|
|||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
s8 tx_power)
|
||||
{
|
||||
/* FW is in charge of regulatory enforcement */
|
||||
struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
|
||||
.mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
|
||||
.pwr_restriction = cpu_to_le16(tx_power),
|
||||
};
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
|
||||
sizeof(reduce_txpwr_cmd),
|
||||
&reduce_txpwr_cmd);
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iwl_mvm_mc_iter_data {
|
||||
struct iwl_mvm *mvm;
|
||||
int port_id;
|
||||
};
|
||||
|
||||
static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_mc_iter_data *data = _data;
|
||||
struct iwl_mvm *mvm = data->mvm;
|
||||
struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
|
||||
int ret, len;
|
||||
|
||||
/* if we don't have free ports, mcast frames will be dropped */
|
||||
if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
|
||||
return;
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION ||
|
||||
!vif->bss_conf.assoc)
|
||||
return;
|
||||
|
||||
cmd->port_id = data->port_id++;
|
||||
memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
|
||||
len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
|
||||
|
||||
ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, len, cmd);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
|
||||
}
|
||||
|
||||
static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct iwl_mvm_mc_iter_data iter_data = {
|
||||
.mvm = mvm,
|
||||
};
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
|
||||
return;
|
||||
|
||||
ieee80211_iterate_active_interfaces(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_mc_iface_iterator, &iter_data);
|
||||
}
|
||||
|
||||
static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
|
||||
struct netdev_hw_addr_list *mc_list)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct iwl_mcast_filter_cmd *cmd;
|
||||
struct netdev_hw_addr *addr;
|
||||
int addr_count = netdev_hw_addr_list_count(mc_list);
|
||||
bool pass_all = false;
|
||||
int len;
|
||||
|
||||
if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) {
|
||||
pass_all = true;
|
||||
addr_count = 0;
|
||||
}
|
||||
|
||||
len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
|
||||
cmd = kzalloc(len, GFP_ATOMIC);
|
||||
if (!cmd)
|
||||
return 0;
|
||||
|
||||
if (pass_all) {
|
||||
cmd->pass_all = 1;
|
||||
return (u64)(unsigned long)cmd;
|
||||
}
|
||||
|
||||
netdev_hw_addr_list_for_each(addr, mc_list) {
|
||||
IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
|
||||
cmd->count, addr->addr);
|
||||
memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
|
||||
addr->addr, ETH_ALEN);
|
||||
cmd->count++;
|
||||
}
|
||||
|
||||
return (u64)(unsigned long)cmd;
|
||||
}
|
||||
|
||||
static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
|
||||
unsigned int changed_flags,
|
||||
unsigned int *total_flags,
|
||||
u64 multicast)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
/* replace previous configuration */
|
||||
kfree(mvm->mcast_filter_cmd);
|
||||
mvm->mcast_filter_cmd = cmd;
|
||||
|
||||
if (!cmd)
|
||||
goto out;
|
||||
|
||||
iwl_mvm_recalc_multicast(mvm);
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
*total_flags = 0;
|
||||
}
|
||||
|
||||
static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mcast_filter_cmd mcast_filter_cmd = {
|
||||
.pass_all = 1,
|
||||
};
|
||||
|
||||
memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
|
||||
sizeof(mcast_filter_cmd),
|
||||
&mcast_filter_cmd);
|
||||
}
|
||||
|
||||
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *bss_conf,
|
||||
|
@ -828,7 +878,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
|||
IWL_ERR(mvm, "failed to update quotas\n");
|
||||
return;
|
||||
}
|
||||
iwl_mvm_configure_mcast_filter(mvm, vif);
|
||||
|
||||
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
|
||||
&mvm->status)) {
|
||||
|
@ -850,7 +899,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
|||
iwl_mvm_protect_session(mvm, vif, dur, dur,
|
||||
5 * dur);
|
||||
}
|
||||
|
||||
iwl_mvm_sf_update(mvm, vif, false);
|
||||
iwl_mvm_power_vif_assoc(mvm, vif);
|
||||
} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
|
||||
/*
|
||||
* If update fails - SF might be running in associated
|
||||
* mode while disassociated - which is forbidden.
|
||||
*/
|
||||
WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
|
||||
"Failed to update SF upon disassociation\n");
|
||||
|
||||
/* remove AP station now that the MAC is unassoc */
|
||||
ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
|
||||
if (ret)
|
||||
|
@ -862,6 +921,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
|||
IWL_ERR(mvm, "failed to update quotas\n");
|
||||
}
|
||||
|
||||
iwl_mvm_recalc_multicast(mvm);
|
||||
|
||||
/* reset rssi values */
|
||||
mvmvif->bf_data.ave_beacon_signal = 0;
|
||||
|
||||
|
@ -882,7 +943,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
|||
*/
|
||||
iwl_mvm_remove_time_event(mvm, mvmvif,
|
||||
&mvmvif->time_event_data);
|
||||
} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) {
|
||||
} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
|
||||
BSS_CHANGED_QOS)) {
|
||||
ret = iwl_mvm_power_update_mode(mvm, vif);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "failed to update power mode\n");
|
||||
|
@ -991,11 +1053,16 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
|
|||
struct ieee80211_bss_conf *bss_conf,
|
||||
u32 changes)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
|
||||
BSS_CHANGED_HT |
|
||||
BSS_CHANGED_BANDWIDTH;
|
||||
int ret;
|
||||
|
||||
/* Changes will be applied when the AP/IBSS is started */
|
||||
if (!mvmvif->ap_ibss_active)
|
||||
return;
|
||||
|
||||
if (changes & ht_change) {
|
||||
ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
|
||||
if (ret)
|
||||
|
@ -1222,6 +1289,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta, u32 changed)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION &&
|
||||
changed & IEEE80211_RC_NSS_CHANGED)
|
||||
iwl_mvm_sf_update(mvm, vif, false);
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, u16 ac,
|
||||
const struct ieee80211_tx_queue_params *params)
|
||||
|
@ -1344,7 +1422,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
|
|||
*/
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
/* currently FW supports only one optional cipher scheme */
|
||||
if (hw->n_cipher_schemes &&
|
||||
hw->cipher_schemes->cipher == key->cipher)
|
||||
key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
@ -1550,7 +1633,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
|
||||
ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
|
||||
ctx->rx_chains_static,
|
||||
ctx->rx_chains_dynamic);
|
||||
if (ret) {
|
||||
|
@ -1594,7 +1677,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
|
|||
return;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
|
||||
iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
|
||||
ctx->rx_chains_static,
|
||||
ctx->rx_chains_dynamic);
|
||||
iwl_mvm_bt_coex_vif_change(mvm);
|
||||
|
@ -1637,7 +1720,13 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* Setting the quota at this stage is only required for monitor
|
||||
* Power state must be updated before quotas,
|
||||
* otherwise fw will complain.
|
||||
*/
|
||||
mvm->bound_vif_cnt++;
|
||||
iwl_mvm_power_update_binding(mvm, vif, true);
|
||||
|
||||
/* Setting the quota at this stage is only required for monitor
|
||||
* interfaces. For the other types, the bss_info changed flow
|
||||
* will handle quota settings.
|
||||
*/
|
||||
|
@ -1652,6 +1741,8 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
|
||||
out_remove_binding:
|
||||
iwl_mvm_binding_remove_vif(mvm, vif);
|
||||
mvm->bound_vif_cnt--;
|
||||
iwl_mvm_power_update_binding(mvm, vif, false);
|
||||
out_unlock:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
if (ret)
|
||||
|
@ -1685,6 +1776,9 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
|||
iwl_mvm_binding_remove_vif(mvm, vif);
|
||||
out_unlock:
|
||||
mvmvif->phy_ctxt = NULL;
|
||||
mvm->bound_vif_cnt--;
|
||||
iwl_mvm_power_update_binding(mvm, vif, false);
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
|
@ -1779,6 +1873,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
|
|||
.add_interface = iwl_mvm_mac_add_interface,
|
||||
.remove_interface = iwl_mvm_mac_remove_interface,
|
||||
.config = iwl_mvm_mac_config,
|
||||
.prepare_multicast = iwl_mvm_prepare_multicast,
|
||||
.configure_filter = iwl_mvm_configure_filter,
|
||||
.bss_info_changed = iwl_mvm_bss_info_changed,
|
||||
.hw_scan = iwl_mvm_mac_hw_scan,
|
||||
|
@ -1788,6 +1883,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
|
|||
.sta_notify = iwl_mvm_mac_sta_notify,
|
||||
.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
|
||||
.set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
|
||||
.sta_rc_update = iwl_mvm_sta_rc_update,
|
||||
.conf_tx = iwl_mvm_mac_conf_tx,
|
||||
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
|
||||
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
|
||||
|
|
|
@ -163,6 +163,8 @@ struct iwl_mvm_power_ops {
|
|||
struct ieee80211_vif *vif);
|
||||
int (*power_update_device_mode)(struct iwl_mvm *mvm);
|
||||
int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
|
||||
void (*power_update_binding)(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif, bool assign);
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
char *buf, int bufsz);
|
||||
|
@ -181,6 +183,7 @@ enum iwl_dbgfs_pm_mask {
|
|||
MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
|
||||
MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
|
||||
MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
|
||||
MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9),
|
||||
};
|
||||
|
||||
struct iwl_dbgfs_pm {
|
||||
|
@ -193,6 +196,7 @@ struct iwl_dbgfs_pm {
|
|||
bool lprx_ena;
|
||||
u32 lprx_rssi_threshold;
|
||||
bool snooze_ena;
|
||||
bool uapsd_misbehaving;
|
||||
int mask;
|
||||
};
|
||||
|
||||
|
@ -269,8 +273,8 @@ struct iwl_mvm_vif_bf_data {
|
|||
* @bcast_sta: station used for broadcast packets. Used by the following
|
||||
* vifs: P2P_DEVICE, GO and AP.
|
||||
* @beacon_skb: the skb used to hold the AP/GO beacon template
|
||||
* @smps_requests: the requests of of differents parts of the driver, regard
|
||||
the desired smps mode.
|
||||
* @smps_requests: the SMPS requests of differents parts of the driver,
|
||||
* combined on update to yield the overall request to mac80211.
|
||||
*/
|
||||
struct iwl_mvm_vif {
|
||||
u16 id;
|
||||
|
@ -331,6 +335,11 @@ struct iwl_mvm_vif {
|
|||
#endif
|
||||
|
||||
enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
|
||||
|
||||
/* FW identified misbehaving AP */
|
||||
u8 uapsd_misbehaving_bssid[ETH_ALEN];
|
||||
|
||||
bool pm_prevented;
|
||||
};
|
||||
|
||||
static inline struct iwl_mvm_vif *
|
||||
|
@ -479,6 +488,7 @@ struct iwl_mvm {
|
|||
/* Scan status, cmd (pre-allocated) and auxiliary station */
|
||||
enum iwl_scan_status scan_status;
|
||||
struct iwl_scan_cmd *scan_cmd;
|
||||
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
|
||||
|
||||
/* rx chain antennas set through debugfs for the scan command */
|
||||
u8 scan_rx_ant;
|
||||
|
@ -489,6 +499,9 @@ struct iwl_mvm {
|
|||
u8 scan_last_antenna_idx; /* to toggle TX between antennas */
|
||||
u8 mgmt_last_antenna_idx;
|
||||
|
||||
/* last smart fifo state that was successfully sent to firmware */
|
||||
enum iwl_sf_state sf_state;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
struct dentry *debugfs_dir;
|
||||
u32 dbgfs_sram_offset, dbgfs_sram_len;
|
||||
|
@ -512,12 +525,6 @@ struct iwl_mvm {
|
|||
*/
|
||||
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
|
||||
|
||||
/*
|
||||
* This counter of created interfaces is referenced only in conjunction
|
||||
* with FW limitation related to power management. Currently PM is
|
||||
* supported only on a single interface.
|
||||
* IMPORTANT: this variable counts all interfaces except P2P device.
|
||||
*/
|
||||
u8 vif_count;
|
||||
|
||||
/* -1 for always, 0 for never, >0 for that many times */
|
||||
|
@ -560,6 +567,11 @@ struct iwl_mvm {
|
|||
u8 aux_queue;
|
||||
u8 first_agg_queue;
|
||||
u8 last_agg_queue;
|
||||
|
||||
u8 bound_vif_cnt;
|
||||
|
||||
/* Indicate if device power save is allowed */
|
||||
bool ps_prevented;
|
||||
};
|
||||
|
||||
/* Extract MVM priv from op_mode and _hw */
|
||||
|
@ -778,6 +790,19 @@ static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
bool assign)
|
||||
{
|
||||
if (mvm->pm_ops->power_update_binding)
|
||||
mvm->pm_ops->power_update_binding(mvm, vif, assign);
|
||||
}
|
||||
|
||||
void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
|
||||
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_DEBUGFS
|
||||
static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
|
@ -869,4 +894,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
|
|||
void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
|
||||
|
||||
/* smart fifo */
|
||||
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
bool added_vif);
|
||||
|
||||
#endif /* __IWL_MVM_H__ */
|
||||
|
|
|
@ -236,6 +236,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
|
|||
false),
|
||||
|
||||
RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
|
||||
RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
|
||||
iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
|
||||
};
|
||||
#undef RX_HANDLER
|
||||
#define CMD(x) [x] = #x
|
||||
|
@ -311,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
|
|||
CMD(REPLY_THERMAL_MNG_BACKOFF),
|
||||
CMD(MAC_PM_POWER_TABLE),
|
||||
CMD(BT_COEX_CI),
|
||||
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
|
||||
};
|
||||
#undef CMD
|
||||
|
||||
|
@ -341,7 +344,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
|
||||
op_mode = hw->priv;
|
||||
op_mode->ops = &iwl_mvm_ops;
|
||||
op_mode->trans = trans;
|
||||
|
||||
mvm = IWL_OP_MODE_GET_MVM(op_mode);
|
||||
mvm->dev = trans->dev;
|
||||
|
@ -359,6 +361,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
mvm->aux_queue = 11;
|
||||
mvm->first_agg_queue = 12;
|
||||
}
|
||||
mvm->sf_state = SF_UNINIT;
|
||||
|
||||
mutex_init(&mvm->mutex);
|
||||
spin_lock_init(&mvm->async_handlers_lock);
|
||||
|
@ -424,7 +427,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
* there is no need to unnecessarily power up the NIC at driver load
|
||||
*/
|
||||
if (iwlwifi_mod_params.nvm_file) {
|
||||
iwl_nvm_init(mvm);
|
||||
err = iwl_nvm_init(mvm);
|
||||
if (err)
|
||||
goto out_free;
|
||||
} else {
|
||||
err = iwl_trans_start_hw(mvm->trans);
|
||||
if (err)
|
||||
|
@ -432,16 +437,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
|
||||
mutex_lock(&mvm->mutex);
|
||||
err = iwl_run_init_mvm_ucode(mvm, true);
|
||||
iwl_trans_stop_device(trans);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
/* returns 0 if successful, 1 if success but in rfkill */
|
||||
if (err < 0 && !iwlmvm_mod_params.init_dbg) {
|
||||
IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Stop the hw after the ALIVE and NVM has been read */
|
||||
if (!iwlmvm_mod_params.init_dbg)
|
||||
iwl_trans_stop_hw(mvm->trans, false);
|
||||
}
|
||||
|
||||
scan_size = sizeof(struct iwl_scan_cmd) +
|
||||
|
@ -474,7 +476,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
iwl_phy_db_free(mvm->phy_db);
|
||||
kfree(mvm->scan_cmd);
|
||||
if (!iwlwifi_mod_params.nvm_file)
|
||||
iwl_trans_stop_hw(trans, true);
|
||||
iwl_trans_op_mode_leave(trans);
|
||||
ieee80211_free_hw(mvm->hw);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -491,12 +493,14 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
|
|||
ieee80211_unregister_hw(mvm->hw);
|
||||
|
||||
kfree(mvm->scan_cmd);
|
||||
kfree(mvm->mcast_filter_cmd);
|
||||
mvm->mcast_filter_cmd = NULL;
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
|
||||
kfree(mvm->d3_resume_sram);
|
||||
#endif
|
||||
|
||||
iwl_trans_stop_hw(mvm->trans, true);
|
||||
iwl_trans_op_mode_leave(mvm->trans);
|
||||
|
||||
iwl_phy_db_free(mvm->phy_db);
|
||||
mvm->phy_db = NULL;
|
||||
|
|
|
@ -186,6 +186,92 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
|
|||
}
|
||||
}
|
||||
|
||||
static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
enum ieee80211_ac_numbers ac;
|
||||
bool tid_found = false;
|
||||
|
||||
for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
|
||||
if (!mvmvif->queue_params[ac].uapsd)
|
||||
continue;
|
||||
|
||||
if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
|
||||
cmd->flags |=
|
||||
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
|
||||
|
||||
cmd->uapsd_ac_flags |= BIT(ac);
|
||||
|
||||
/* QNDP TID - the highest TID with no admission control */
|
||||
if (!tid_found && !mvmvif->queue_params[ac].acm) {
|
||||
tid_found = true;
|
||||
switch (ac) {
|
||||
case IEEE80211_AC_VO:
|
||||
cmd->qndp_tid = 6;
|
||||
break;
|
||||
case IEEE80211_AC_VI:
|
||||
cmd->qndp_tid = 5;
|
||||
break;
|
||||
case IEEE80211_AC_BE:
|
||||
cmd->qndp_tid = 0;
|
||||
break;
|
||||
case IEEE80211_AC_BK:
|
||||
cmd->qndp_tid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
|
||||
return;
|
||||
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
|
||||
|
||||
if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
|
||||
BIT(IEEE80211_AC_VI) |
|
||||
BIT(IEEE80211_AC_BE) |
|
||||
BIT(IEEE80211_AC_BK))) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
|
||||
cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
|
||||
cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
|
||||
cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
|
||||
cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
|
||||
}
|
||||
|
||||
cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
|
||||
|
||||
if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
|
||||
cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
|
||||
cmd->rx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
|
||||
} else {
|
||||
cmd->rx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
|
||||
}
|
||||
|
||||
if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
|
||||
cmd->heavy_tx_thld_packets =
|
||||
IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
|
||||
cmd->heavy_rx_thld_packets =
|
||||
IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
|
||||
} else {
|
||||
cmd->heavy_tx_thld_packets =
|
||||
IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
|
||||
cmd->heavy_rx_thld_packets =
|
||||
IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
|
||||
}
|
||||
cmd->heavy_tx_thld_percentage =
|
||||
IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
|
||||
cmd->heavy_rx_thld_percentage =
|
||||
IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
|
||||
}
|
||||
|
||||
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
|
@ -198,8 +284,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
bool radar_detect = false;
|
||||
struct iwl_mvm_vif *mvmvif __maybe_unused =
|
||||
iwl_mvm_vif_from_mac80211(vif);
|
||||
enum ieee80211_ac_numbers ac;
|
||||
bool tid_found = false;
|
||||
bool allow_uapsd = true;
|
||||
|
||||
cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
|
||||
mvmvif->color));
|
||||
|
@ -217,7 +302,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
|
||||
cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
|
||||
|
||||
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
|
||||
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
|
||||
mvm->ps_prevented)
|
||||
return;
|
||||
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
|
||||
|
@ -227,7 +313,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
mvmvif->dbgfs_pm.disable_power_off)
|
||||
cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
|
||||
#endif
|
||||
if (!vif->bss_conf.ps)
|
||||
if (!vif->bss_conf.ps || mvmvif->pm_prevented)
|
||||
return;
|
||||
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
|
||||
|
@ -269,81 +355,24 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
|
||||
}
|
||||
|
||||
for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
|
||||
if (!mvmvif->queue_params[ac].uapsd)
|
||||
continue;
|
||||
if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
|
||||
ETH_ALEN))
|
||||
allow_uapsd = false;
|
||||
|
||||
if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
|
||||
cmd->flags |=
|
||||
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
|
||||
if (vif->p2p &&
|
||||
!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
|
||||
allow_uapsd = false;
|
||||
/*
|
||||
* Avoid using uAPSD if P2P client is associated to GO that uses
|
||||
* opportunistic power save. This is due to current FW limitation.
|
||||
*/
|
||||
if (vif->p2p &&
|
||||
vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
|
||||
IEEE80211_P2P_OPPPS_ENABLE_BIT)
|
||||
allow_uapsd = false;
|
||||
|
||||
cmd->uapsd_ac_flags |= BIT(ac);
|
||||
|
||||
/* QNDP TID - the highest TID with no admission control */
|
||||
if (!tid_found && !mvmvif->queue_params[ac].acm) {
|
||||
tid_found = true;
|
||||
switch (ac) {
|
||||
case IEEE80211_AC_VO:
|
||||
cmd->qndp_tid = 6;
|
||||
break;
|
||||
case IEEE80211_AC_VI:
|
||||
cmd->qndp_tid = 5;
|
||||
break;
|
||||
case IEEE80211_AC_BE:
|
||||
cmd->qndp_tid = 0;
|
||||
break;
|
||||
case IEEE80211_AC_BK:
|
||||
cmd->qndp_tid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
|
||||
if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
|
||||
BIT(IEEE80211_AC_VI) |
|
||||
BIT(IEEE80211_AC_BE) |
|
||||
BIT(IEEE80211_AC_BK))) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
|
||||
cmd->snooze_interval =
|
||||
cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
|
||||
cmd->snooze_window =
|
||||
(mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
|
||||
cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
|
||||
cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
|
||||
}
|
||||
|
||||
cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
|
||||
|
||||
if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
|
||||
cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
|
||||
cmd->rx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
|
||||
} else {
|
||||
cmd->rx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout_uapsd =
|
||||
cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
|
||||
}
|
||||
|
||||
if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
|
||||
cmd->heavy_tx_thld_packets =
|
||||
IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
|
||||
cmd->heavy_rx_thld_packets =
|
||||
IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
|
||||
} else {
|
||||
cmd->heavy_tx_thld_packets =
|
||||
IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
|
||||
cmd->heavy_rx_thld_packets =
|
||||
IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
|
||||
}
|
||||
cmd->heavy_tx_thld_percentage =
|
||||
IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
|
||||
cmd->heavy_rx_thld_percentage =
|
||||
IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
|
||||
}
|
||||
if (allow_uapsd)
|
||||
iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
|
||||
|
@ -381,6 +410,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
cmd->flags &=
|
||||
cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
|
||||
}
|
||||
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
|
||||
u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
|
||||
if (mvmvif->dbgfs_pm.uapsd_misbehaving)
|
||||
cmd->flags |= cpu_to_le16(flag);
|
||||
else
|
||||
cmd->flags &= cpu_to_le16(flag);
|
||||
}
|
||||
#endif /* CONFIG_IWLWIFI_DEBUGFS */
|
||||
}
|
||||
|
||||
|
@ -391,18 +427,11 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
|
|||
bool ba_enable;
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* TODO: The following vif_count verification is temporary condition.
|
||||
* Avoid power mode update if more than one interface is currently
|
||||
* active. Remove this condition when FW will support power management
|
||||
* on multiple MACs.
|
||||
*/
|
||||
IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
|
||||
mvm->vif_count);
|
||||
if (mvm->vif_count > 1)
|
||||
if (vif->p2p &&
|
||||
!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))
|
||||
return 0;
|
||||
|
||||
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
|
||||
|
@ -446,7 +475,7 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
|
|||
sizeof(cmd), &cmd);
|
||||
}
|
||||
|
||||
static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
|
||||
static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
|
||||
{
|
||||
struct iwl_device_power_cmd cmd = {
|
||||
.flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
|
||||
|
@ -455,7 +484,8 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
|
|||
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
|
||||
return 0;
|
||||
|
||||
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
|
||||
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
|
||||
force_disable)
|
||||
cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
|
@ -472,6 +502,78 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
|
|||
&cmd);
|
||||
}
|
||||
|
||||
static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
|
||||
{
|
||||
return _iwl_mvm_power_update_device(mvm, false);
|
||||
}
|
||||
|
||||
void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
|
||||
ETH_ALEN))
|
||||
memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN);
|
||||
}
|
||||
|
||||
static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
u8 *ap_sta_id = _data;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
/* The ap_sta_id is not expected to change during current association
|
||||
* so no explicit protection is needed
|
||||
*/
|
||||
if (mvmvif->ap_sta_id == *ap_sta_id)
|
||||
memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
|
||||
ETH_ALEN);
|
||||
}
|
||||
|
||||
int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb,
|
||||
struct iwl_device_cmd *cmd)
|
||||
{
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
|
||||
u8 ap_sta_id = le32_to_cpu(notif->sta_id);
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = _data;
|
||||
int ret;
|
||||
|
||||
mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
|
||||
|
||||
ret = iwl_mvm_power_mac_update_mode(mvm, vif);
|
||||
WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
|
||||
}
|
||||
|
||||
static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
bool assign)
|
||||
{
|
||||
if (vif->type == NL80211_IFTYPE_MONITOR) {
|
||||
int ret = _iwl_mvm_power_update_device(mvm, assign);
|
||||
mvm->ps_prevented = assign;
|
||||
WARN_ONCE(ret, "Failed to update power device state\n");
|
||||
}
|
||||
|
||||
ieee80211_iterate_active_interfaces(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_binding_iterator,
|
||||
mvm);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif, char *buf,
|
||||
|
@ -494,70 +596,58 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
|
|||
pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
|
||||
le16_to_cpu(cmd.keep_alive_seconds));
|
||||
|
||||
if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
|
||||
(cmd.flags &
|
||||
cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
|
||||
1 : 0);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
|
||||
cmd.skip_dtim_periods);
|
||||
if (!(cmd.flags &
|
||||
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"rx_data_timeout = %d\n",
|
||||
le32_to_cpu(cmd.rx_data_timeout));
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"tx_data_timeout = %d\n",
|
||||
le32_to_cpu(cmd.tx_data_timeout));
|
||||
}
|
||||
if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"lprx_rssi_threshold = %d\n",
|
||||
cmd.lprx_rssi_threshold);
|
||||
if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
|
||||
pos +=
|
||||
scnprintf(buf+pos, bufsz-pos,
|
||||
"rx_data_timeout_uapsd = %d\n",
|
||||
le32_to_cpu(cmd.rx_data_timeout_uapsd));
|
||||
pos +=
|
||||
scnprintf(buf+pos, bufsz-pos,
|
||||
"tx_data_timeout_uapsd = %d\n",
|
||||
le32_to_cpu(cmd.tx_data_timeout_uapsd));
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n",
|
||||
cmd.qndp_tid);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"uapsd_ac_flags = 0x%x\n",
|
||||
cmd.uapsd_ac_flags);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"uapsd_max_sp = %d\n",
|
||||
cmd.uapsd_max_sp);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"heavy_tx_thld_packets = %d\n",
|
||||
cmd.heavy_tx_thld_packets);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"heavy_rx_thld_packets = %d\n",
|
||||
cmd.heavy_rx_thld_packets);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"heavy_tx_thld_percentage = %d\n",
|
||||
cmd.heavy_tx_thld_percentage);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"heavy_rx_thld_percentage = %d\n",
|
||||
cmd.heavy_rx_thld_percentage);
|
||||
pos +=
|
||||
scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n",
|
||||
(cmd.flags &
|
||||
cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ?
|
||||
1 : 0);
|
||||
}
|
||||
if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"snooze_interval = %d\n",
|
||||
cmd.snooze_interval);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"snooze_window = %d\n",
|
||||
cmd.snooze_window);
|
||||
}
|
||||
if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
|
||||
return pos;
|
||||
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
|
||||
(cmd.flags &
|
||||
cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
|
||||
cmd.skip_dtim_periods);
|
||||
if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
|
||||
le32_to_cpu(cmd.rx_data_timeout));
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
|
||||
le32_to_cpu(cmd.tx_data_timeout));
|
||||
}
|
||||
if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"lprx_rssi_threshold = %d\n",
|
||||
cmd.lprx_rssi_threshold);
|
||||
|
||||
if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
|
||||
return pos;
|
||||
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
|
||||
le32_to_cpu(cmd.rx_data_timeout_uapsd));
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
|
||||
le32_to_cpu(cmd.tx_data_timeout_uapsd));
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
|
||||
cmd.uapsd_ac_flags);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
|
||||
cmd.uapsd_max_sp);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
|
||||
cmd.heavy_tx_thld_packets);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
|
||||
cmd.heavy_rx_thld_packets);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
|
||||
cmd.heavy_tx_thld_percentage);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
|
||||
cmd.heavy_rx_thld_percentage);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
|
||||
(cmd.flags &
|
||||
cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
|
||||
1 : 0);
|
||||
|
||||
if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
|
||||
return pos;
|
||||
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
|
||||
cmd.snooze_interval);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
|
||||
cmd.snooze_window);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
@ -654,6 +744,7 @@ const struct iwl_mvm_power_ops pm_mac_ops = {
|
|||
.power_update_mode = iwl_mvm_power_mac_update_mode,
|
||||
.power_update_device_mode = iwl_mvm_power_update_device,
|
||||
.power_disable = iwl_mvm_power_mac_disable,
|
||||
.power_update_binding = _iwl_mvm_power_update_binding,
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
.power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
|
||||
#endif
|
||||
|
|
|
@ -42,9 +42,16 @@
|
|||
|
||||
#define RS_NAME "iwl-mvm-rs"
|
||||
|
||||
#define NUM_TRY_BEFORE_ANT_TOGGLE 1
|
||||
#define IWL_NUMBER_TRY 1
|
||||
#define IWL_HT_NUMBER_TRY 3
|
||||
#define NUM_TRY_BEFORE_ANT_TOGGLE 1
|
||||
#define RS_LEGACY_RETRIES_PER_RATE 1
|
||||
#define RS_HT_VHT_RETRIES_PER_RATE 2
|
||||
#define RS_HT_VHT_RETRIES_PER_RATE_TW 1
|
||||
#define RS_INITIAL_MIMO_NUM_RATES 3
|
||||
#define RS_INITIAL_SISO_NUM_RATES 3
|
||||
#define RS_INITIAL_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM
|
||||
#define RS_SECONDARY_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM
|
||||
#define RS_SECONDARY_SISO_NUM_RATES 3
|
||||
#define RS_SECONDARY_SISO_RETRIES 1
|
||||
|
||||
#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */
|
||||
#define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */
|
||||
|
@ -123,6 +130,12 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
|
|||
IWL_DECLARE_MCS_RATE(9), /* MCS 9 */
|
||||
};
|
||||
|
||||
enum rs_action {
|
||||
RS_ACTION_STAY = 0,
|
||||
RS_ACTION_DOWNSCALE = -1,
|
||||
RS_ACTION_UPSCALE = 1,
|
||||
};
|
||||
|
||||
enum rs_column_mode {
|
||||
RS_INVALID = 0,
|
||||
RS_LEGACY,
|
||||
|
@ -351,20 +364,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
struct sk_buff *skb,
|
||||
struct ieee80211_sta *sta,
|
||||
struct iwl_lq_sta *lq_sta);
|
||||
static void rs_fill_link_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_sta *sta,
|
||||
struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
|
||||
static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_sta *sta,
|
||||
struct iwl_lq_sta *lq_sta,
|
||||
const struct rs_rate *initial_rate);
|
||||
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);
|
||||
#else
|
||||
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
|
||||
u32 *rate_n_flags)
|
||||
{}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The following tables contain the expected throughput metrics for all rates
|
||||
*
|
||||
|
@ -504,30 +509,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
|
|||
return (ant_type & valid_antenna) == ant_type;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/**
|
||||
* Program the device to use fixed rate for frame transmit
|
||||
* This is for debugging/testing only
|
||||
* once the device start use fixed rate, we need to reload the module
|
||||
* to being back the normal operation.
|
||||
*/
|
||||
static void rs_program_fix_rate(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_sta)
|
||||
{
|
||||
lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
|
||||
lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
|
||||
lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
|
||||
|
||||
IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
|
||||
lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
|
||||
|
||||
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, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_data, u8 tid,
|
||||
struct ieee80211_sta *sta)
|
||||
|
@ -658,7 +639,7 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
|
|||
|
||||
/* Convert rs_rate object into ucode rate bitmask */
|
||||
static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
|
||||
struct rs_rate *rate)
|
||||
struct rs_rate *rate)
|
||||
{
|
||||
u32 ucode_rate = 0;
|
||||
int index = rate->index;
|
||||
|
@ -785,8 +766,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
|
|||
|
||||
/* 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 *ucode_rate,
|
||||
struct rs_rate *rate)
|
||||
static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate)
|
||||
{
|
||||
u8 new_ant_type;
|
||||
|
||||
|
@ -807,9 +787,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -883,65 +860,73 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
|
|||
return (high << 8) | low;
|
||||
}
|
||||
|
||||
static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate,
|
||||
u8 scale_index, u8 ht_possible)
|
||||
static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate)
|
||||
{
|
||||
s32 low;
|
||||
u16 rate_mask;
|
||||
return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate);
|
||||
}
|
||||
|
||||
/* Get the next supported lower rate in the current column.
|
||||
* Return true if bottom rate in the current column was reached
|
||||
*/
|
||||
static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate)
|
||||
{
|
||||
u8 low;
|
||||
u16 high_low;
|
||||
u8 switch_to_legacy = 0;
|
||||
u16 rate_mask;
|
||||
struct iwl_mvm *mvm = lq_sta->drv;
|
||||
|
||||
/* 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(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];
|
||||
rate_mask = rs_get_supported_rates(lq_sta, rate);
|
||||
high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
|
||||
rate->type);
|
||||
low = high_low & 0xff;
|
||||
|
||||
/* Bottom rate of column reached */
|
||||
if (low == IWL_RATE_INVALID)
|
||||
return true;
|
||||
|
||||
rate->index = low;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the next rate to use following a column downgrade */
|
||||
static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate)
|
||||
{
|
||||
struct iwl_mvm *mvm = lq_sta->drv;
|
||||
|
||||
if (is_legacy(rate)) {
|
||||
/* No column to downgrade from Legacy */
|
||||
return;
|
||||
} else if (is_siso(rate)) {
|
||||
/* Downgrade to Legacy if we were in SISO */
|
||||
if (lq_sta->band == IEEE80211_BAND_5GHZ)
|
||||
rate->type = LQ_LEGACY_A;
|
||||
else
|
||||
rate->type = LQ_LEGACY_G;
|
||||
|
||||
if (num_of_ant(rate->ant) > 1)
|
||||
rate->ant =
|
||||
first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
|
||||
|
||||
rate->bw = RATE_MCS_CHAN_WIDTH_20;
|
||||
rate->sgi = false;
|
||||
|
||||
WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
|
||||
rate->index > IWL_RATE_MCS_9_INDEX);
|
||||
|
||||
rate->index = rs_ht_to_legacy[rate->index];
|
||||
} else {
|
||||
/* Downgrade to SISO with same MCS if in MIMO */
|
||||
rate->type = is_vht_mimo2(rate) ?
|
||||
LQ_VHT_SISO : LQ_HT_SISO;
|
||||
}
|
||||
|
||||
rate_mask = rs_get_supported_rates(lq_sta, rate);
|
||||
|
||||
/* Mask with station rate restriction */
|
||||
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 &
|
||||
(lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
|
||||
else
|
||||
rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
|
||||
}
|
||||
if (num_of_ant(rate->ant) > 1)
|
||||
rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
|
||||
|
||||
/* If we switched from HT to legacy, check current rate */
|
||||
if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
|
||||
low = scale_index;
|
||||
goto out;
|
||||
}
|
||||
/* Relevant in both switching to SISO or Legacy */
|
||||
rate->sgi = false;
|
||||
|
||||
high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
|
||||
rate->type);
|
||||
low = high_low & 0xff;
|
||||
|
||||
if (low == IWL_RATE_INVALID)
|
||||
low = scale_index;
|
||||
|
||||
out:
|
||||
rate->index = low;
|
||||
return ucode_rate_from_rs_rate(lq_sta->drv, rate);
|
||||
if (!rs_rate_supported(lq_sta, rate))
|
||||
rs_get_lower_rate_in_column(lq_sta, rate);
|
||||
}
|
||||
|
||||
/* Simple function to compare two rate scale table types */
|
||||
|
@ -1137,14 +1122,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
|
|||
tmp_tbl = curr_tbl;
|
||||
else if (rs_rate_match(&rate, &other_tbl->rate))
|
||||
tmp_tbl = other_tbl;
|
||||
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:");
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
rs_collect_tx_data(tmp_tbl, rate.index, 1,
|
||||
i < retries ? 0 : legacy_success);
|
||||
}
|
||||
|
@ -1471,10 +1451,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
|
|||
struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate)
|
||||
{
|
||||
u32 ucode_rate;
|
||||
|
||||
ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
|
||||
rs_fill_link_cmd(mvm, sta, lq_sta, ucode_rate);
|
||||
rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
|
||||
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
|
||||
}
|
||||
|
||||
|
@ -1634,10 +1611,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
|
|||
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);
|
||||
|
||||
|
@ -1649,6 +1622,97 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
|
||||
struct iwl_scale_tbl_info *tbl,
|
||||
s32 sr, int low, int high,
|
||||
int current_tpt,
|
||||
int low_tpt, int high_tpt)
|
||||
{
|
||||
enum rs_action action = RS_ACTION_STAY;
|
||||
|
||||
/* Too many failures, decrease rate */
|
||||
if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"decrease rate because of low SR\n");
|
||||
action = RS_ACTION_DOWNSCALE;
|
||||
/* 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) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Good SR and no high rate measurement. "
|
||||
"Increase rate\n");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
} else if (low != IWL_RATE_INVALID) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Remain in current rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Both adjacent throughputs are measured, but neither one has better
|
||||
* throughput; we're using the best rate, don't change it!
|
||||
*/
|
||||
else if ((low_tpt != IWL_INVALID_VALUE) &&
|
||||
(high_tpt != IWL_INVALID_VALUE) &&
|
||||
(low_tpt < current_tpt) &&
|
||||
(high_tpt < current_tpt)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Both high and low are worse. "
|
||||
"Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
/* At least one adjacent rate's throughput is measured,
|
||||
* and may have better performance.
|
||||
*/
|
||||
else {
|
||||
/* Higher adjacent rate's throughput is measured */
|
||||
if (high_tpt != IWL_INVALID_VALUE) {
|
||||
/* 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");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
} else {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Higher rate isn't better OR "
|
||||
"no good SR. Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
/* Lower adjacent rate's throughput is measured */
|
||||
} else if (low_tpt != IWL_INVALID_VALUE) {
|
||||
/* Lower rate has better throughput */
|
||||
if (low_tpt > current_tpt) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate is better. "
|
||||
"Decrease rate\n");
|
||||
action = RS_ACTION_DOWNSCALE;
|
||||
} else if (sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate isn't better and "
|
||||
"good SR. Increase rate\n");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check; asked for decrease, but success rate or throughput
|
||||
* has been good at old rate. Don't change it.
|
||||
*/
|
||||
if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) &&
|
||||
((sr > IWL_RATE_HIGH_TH) ||
|
||||
(current_tpt > (100 * tbl->expected_tpt[low])))) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Sanity check failed. Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do rate scaling and search for new modulation mode.
|
||||
|
@ -1669,11 +1733,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
int low_tpt = IWL_INVALID_VALUE;
|
||||
int high_tpt = IWL_INVALID_VALUE;
|
||||
u32 fail_count;
|
||||
s8 scale_action = 0;
|
||||
enum rs_action scale_action = RS_ACTION_STAY;
|
||||
u16 rate_mask;
|
||||
u8 update_lq = 0;
|
||||
struct iwl_scale_tbl_info *tbl, *tbl1;
|
||||
u16 rate_scale_index_msk = 0;
|
||||
u8 active_tbl = 0;
|
||||
u8 done_search = 0;
|
||||
u16 high_low;
|
||||
|
@ -1690,8 +1753,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
info->flags & IEEE80211_TX_CTL_NO_ACK)
|
||||
return;
|
||||
|
||||
lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
|
||||
|
||||
tid = rs_get_tid(lq_sta, hdr);
|
||||
if ((tid != IWL_MAX_TID_COUNT) &&
|
||||
(lq_sta->tx_agg_tid_en & (1 << tid))) {
|
||||
|
@ -1730,33 +1791,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
/* rates available for this association, and for modulation mode */
|
||||
rate_mask = rs_get_supported_rates(lq_sta, rate);
|
||||
|
||||
/* mask with station rate restriction */
|
||||
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 &
|
||||
(lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
|
||||
else
|
||||
rate_scale_index_msk = (u16) (rate_mask &
|
||||
lq_sta->supp_rates);
|
||||
|
||||
} else {
|
||||
rate_scale_index_msk = rate_mask;
|
||||
}
|
||||
|
||||
if (!rate_scale_index_msk)
|
||||
rate_scale_index_msk = rate_mask;
|
||||
|
||||
if (!((BIT(index) & rate_scale_index_msk))) {
|
||||
if (!(BIT(index) & rate_mask)) {
|
||||
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*/
|
||||
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);
|
||||
tbl->rate.index = index;
|
||||
rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
|
||||
}
|
||||
return;
|
||||
|
@ -1847,7 +1888,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
tbl = &(lq_sta->lq_info[active_tbl]);
|
||||
|
||||
/* Revert to "active" rate and throughput info */
|
||||
index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
|
||||
index = tbl->rate.index;
|
||||
current_tpt = lq_sta->last_tpt;
|
||||
|
||||
/* Need to set up a new rate table in uCode */
|
||||
|
@ -1863,8 +1904,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,
|
||||
rate->type);
|
||||
high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
|
||||
low = high_low & 0xff;
|
||||
high = (high_low >> 8) & 0xff;
|
||||
|
||||
|
@ -1887,85 +1927,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
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 <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"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) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Good SR and no high rate measurement. "
|
||||
"Increase rate\n");
|
||||
scale_action = 1;
|
||||
} 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
|
||||
* throughput; we're using the best rate, don't change it! */
|
||||
else if ((low_tpt != IWL_INVALID_VALUE) &&
|
||||
(high_tpt != IWL_INVALID_VALUE) &&
|
||||
(low_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. */
|
||||
else {
|
||||
/* Higher adjacent rate's throughput is measured */
|
||||
if (high_tpt != IWL_INVALID_VALUE) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Lower adjacent rate's throughput is measured */
|
||||
} else if (low_tpt != IWL_INVALID_VALUE) {
|
||||
/* Lower rate has better throughput */
|
||||
if (low_tpt > current_tpt) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check; asked for decrease, but success rate or throughput
|
||||
* 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])))) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Sanity check failed. Maintain rate\n");
|
||||
scale_action = 0;
|
||||
}
|
||||
scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
|
||||
current_tpt, low_tpt, high_tpt);
|
||||
|
||||
/* Force a search in case BT doesn't like us being in MIMO */
|
||||
if (is_mimo(rate) &&
|
||||
|
@ -1977,7 +1940,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
switch (scale_action) {
|
||||
case -1:
|
||||
case RS_ACTION_DOWNSCALE:
|
||||
/* Decrease starting rate, update uCode's rate table */
|
||||
if (low != IWL_RATE_INVALID) {
|
||||
update_lq = 1;
|
||||
|
@ -1988,7 +1951,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
case RS_ACTION_UPSCALE:
|
||||
/* Increase starting rate, update uCode's rate table */
|
||||
if (high != IWL_RATE_INVALID) {
|
||||
update_lq = 1;
|
||||
|
@ -1999,7 +1962,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
break;
|
||||
case 0:
|
||||
case RS_ACTION_STAY:
|
||||
/* No change */
|
||||
default:
|
||||
break;
|
||||
|
@ -2053,11 +2016,11 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
rs_rate_scale_clear_window(&(tbl->win[i]));
|
||||
|
||||
/* Use new "search" start rate */
|
||||
index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
|
||||
index = tbl->rate.index;
|
||||
|
||||
rs_dump_rate(mvm, &tbl->rate,
|
||||
"Switch to SEARCH TABLE:");
|
||||
rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
|
||||
rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
|
||||
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
|
||||
} else {
|
||||
done_search = 1;
|
||||
|
@ -2095,8 +2058,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
out:
|
||||
tbl->rate.index = index;
|
||||
tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate);
|
||||
lq_sta->last_txrate_idx = index;
|
||||
}
|
||||
|
||||
|
@ -2123,7 +2084,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
struct iwl_scale_tbl_info *tbl;
|
||||
struct rs_rate *rate;
|
||||
int i;
|
||||
u32 ucode_rate;
|
||||
u8 active_tbl = 0;
|
||||
u8 valid_tx_ant;
|
||||
|
||||
|
@ -2154,9 +2114,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
else
|
||||
rate->type = LQ_LEGACY_G;
|
||||
|
||||
ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
|
||||
tbl->current_rate = ucode_rate;
|
||||
|
||||
WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
|
||||
if (rate->ant == ANT_A)
|
||||
tbl->column = RS_COLUMN_LEGACY_ANT_A;
|
||||
|
@ -2164,7 +2121,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
tbl->column = RS_COLUMN_LEGACY_ANT_B;
|
||||
|
||||
rs_set_expected_tpt_table(lq_sta, tbl);
|
||||
rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate);
|
||||
rs_fill_lq_cmd(NULL, NULL, lq_sta, rate);
|
||||
/* TODO restore station should remember the lq cmd */
|
||||
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
|
||||
}
|
||||
|
@ -2250,6 +2207,10 @@ 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)
|
||||
|
@ -2268,6 +2229,10 @@ 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)
|
||||
|
@ -2306,7 +2271,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
|||
rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
|
||||
|
||||
lq_sta->flush_timer = 0;
|
||||
lq_sta->supp_rates = sta->supp_rates[sband->band];
|
||||
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"LQ: *** rate scale station global init for station %d ***\n",
|
||||
|
@ -2395,113 +2359,165 @@ static void rs_rate_update(void *mvm_r,
|
|||
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)
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_cmd *lq_cmd,
|
||||
enum ieee80211_band band,
|
||||
u32 ucode_rate)
|
||||
{
|
||||
struct rs_rate rate;
|
||||
int index = 0;
|
||||
int repeat_rate = 0;
|
||||
u8 ant_toggle_cnt = 0;
|
||||
u8 use_ht_possible = 1;
|
||||
u8 valid_tx_ant = 0;
|
||||
struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
|
||||
int i;
|
||||
int num_rates = ARRAY_SIZE(lq_cmd->rs_table);
|
||||
__le32 ucode_rate_le32 = cpu_to_le32(ucode_rate);
|
||||
|
||||
/* Override starting rate (index 0) if needed for debug purposes */
|
||||
rs_dbgfs_set_mcs(lq_sta, &new_rate);
|
||||
for (i = 0; i < num_rates; i++)
|
||||
lq_cmd->rs_table[i] = ucode_rate_le32;
|
||||
|
||||
rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
|
||||
rs_rate_from_ucode_rate(ucode_rate, band, &rate);
|
||||
|
||||
/* How many times should we repeat the initial rate? */
|
||||
if (is_legacy(&rate)) {
|
||||
ant_toggle_cnt = 1;
|
||||
repeat_rate = IWL_NUMBER_TRY;
|
||||
} else {
|
||||
repeat_rate = min(IWL_HT_NUMBER_TRY,
|
||||
LINK_QUAL_AGG_DISABLE_START_DEF - 1);
|
||||
if (is_mimo(&rate))
|
||||
lq_cmd->mimo_delim = num_rates - 1;
|
||||
else
|
||||
lq_cmd->mimo_delim = 0;
|
||||
}
|
||||
#endif /* CONFIG_MAC80211_DEBUGFS */
|
||||
|
||||
static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_sta,
|
||||
struct rs_rate *rate,
|
||||
__le32 *rs_table, int *rs_table_index,
|
||||
int num_rates, int num_retries,
|
||||
u8 valid_tx_ant, bool toggle_ant)
|
||||
{
|
||||
int i, j;
|
||||
__le32 ucode_rate;
|
||||
bool bottom_reached = false;
|
||||
int prev_rate_idx = rate->index;
|
||||
int end = LINK_QUAL_MAX_RETRY_NUM;
|
||||
int index = *rs_table_index;
|
||||
|
||||
for (i = 0; i < num_rates && index < end; i++) {
|
||||
ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate));
|
||||
for (j = 0; j < num_retries && index < end; j++, index++)
|
||||
rs_table[index] = ucode_rate;
|
||||
|
||||
if (toggle_ant)
|
||||
rs_toggle_antenna(valid_tx_ant, rate);
|
||||
|
||||
prev_rate_idx = rate->index;
|
||||
bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
|
||||
if (bottom_reached && !is_legacy(rate))
|
||||
break;
|
||||
}
|
||||
|
||||
lq_cmd->mimo_delim = is_mimo(&rate) ? 1 : 0;
|
||||
if (!bottom_reached)
|
||||
rate->index = prev_rate_idx;
|
||||
|
||||
/* Fill 1st table entry (index 0) */
|
||||
lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
|
||||
*rs_table_index = index;
|
||||
}
|
||||
|
||||
if (num_of_ant(rate.ant) == 1)
|
||||
lq_cmd->single_stream_ant_msk = rate.ant;
|
||||
/* otherwise we don't modify the existing value */
|
||||
/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
|
||||
* column the rate table should look like this:
|
||||
*
|
||||
* rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
|
||||
* rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
|
||||
* rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
|
||||
* rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
|
||||
* rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
|
||||
* rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
|
||||
* rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
|
||||
* rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
|
||||
* rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
|
||||
* rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps
|
||||
* rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps
|
||||
* rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps
|
||||
* rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps
|
||||
* rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps
|
||||
* rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps
|
||||
* rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
|
||||
*/
|
||||
static void rs_build_rates_table(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_sta,
|
||||
const struct rs_rate *initial_rate)
|
||||
{
|
||||
struct rs_rate rate;
|
||||
int num_rates, num_retries, index = 0;
|
||||
u8 valid_tx_ant = 0;
|
||||
struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
|
||||
bool toggle_ant = false;
|
||||
|
||||
memcpy(&rate, initial_rate, sizeof(struct rs_rate));
|
||||
|
||||
index++;
|
||||
repeat_rate--;
|
||||
if (mvm)
|
||||
valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
|
||||
|
||||
/* Fill rest of rate table */
|
||||
while (index < LINK_QUAL_MAX_RETRY_NUM) {
|
||||
/* Repeat initial/next rate.
|
||||
* 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(&rate)) {
|
||||
if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
|
||||
ant_toggle_cnt++;
|
||||
else if (mvm &&
|
||||
rs_toggle_antenna(valid_tx_ant,
|
||||
&new_rate, &rate))
|
||||
ant_toggle_cnt = 1;
|
||||
}
|
||||
|
||||
/* Override next rate if needed for debug purposes */
|
||||
rs_dbgfs_set_mcs(lq_sta, &new_rate);
|
||||
|
||||
/* Fill next table entry */
|
||||
lq_cmd->rs_table[index] =
|
||||
cpu_to_le32(new_rate);
|
||||
repeat_rate--;
|
||||
index++;
|
||||
}
|
||||
|
||||
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(&rate))
|
||||
lq_cmd->mimo_delim = index;
|
||||
|
||||
/* Get next rate */
|
||||
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(&rate)) {
|
||||
if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
|
||||
ant_toggle_cnt++;
|
||||
else if (mvm &&
|
||||
rs_toggle_antenna(valid_tx_ant,
|
||||
&new_rate, &rate))
|
||||
ant_toggle_cnt = 1;
|
||||
|
||||
repeat_rate = IWL_NUMBER_TRY;
|
||||
} else {
|
||||
repeat_rate = IWL_HT_NUMBER_TRY;
|
||||
}
|
||||
|
||||
/* Don't allow HT rates after next pass.
|
||||
* rs_get_lower_rate() will change type to LQ_LEGACY_A
|
||||
* or LQ_LEGACY_G.
|
||||
*/
|
||||
use_ht_possible = 0;
|
||||
|
||||
/* Override next rate if needed for debug purposes */
|
||||
rs_dbgfs_set_mcs(lq_sta, &new_rate);
|
||||
|
||||
/* Fill next table entry */
|
||||
lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
|
||||
|
||||
index++;
|
||||
repeat_rate--;
|
||||
if (is_siso(&rate)) {
|
||||
num_rates = RS_INITIAL_SISO_NUM_RATES;
|
||||
num_retries = RS_HT_VHT_RETRIES_PER_RATE;
|
||||
} else if (is_mimo(&rate)) {
|
||||
num_rates = RS_INITIAL_MIMO_NUM_RATES;
|
||||
num_retries = RS_HT_VHT_RETRIES_PER_RATE;
|
||||
} else {
|
||||
num_rates = RS_INITIAL_LEGACY_NUM_RATES;
|
||||
num_retries = RS_LEGACY_RETRIES_PER_RATE;
|
||||
toggle_ant = true;
|
||||
}
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
rs_get_lower_rate_down_column(lq_sta, &rate);
|
||||
|
||||
if (is_siso(&rate)) {
|
||||
num_rates = RS_SECONDARY_SISO_NUM_RATES;
|
||||
num_retries = RS_SECONDARY_SISO_RETRIES;
|
||||
} else if (is_legacy(&rate)) {
|
||||
num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
|
||||
num_retries = RS_LEGACY_RETRIES_PER_RATE;
|
||||
} else {
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
toggle_ant = true;
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
rs_get_lower_rate_down_column(lq_sta, &rate);
|
||||
|
||||
num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
|
||||
num_retries = RS_LEGACY_RETRIES_PER_RATE;
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
}
|
||||
|
||||
static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_sta *sta,
|
||||
struct iwl_lq_sta *lq_sta,
|
||||
const struct rs_rate *initial_rate)
|
||||
{
|
||||
struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
|
||||
u8 ant = initial_rate->ant;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
if (lq_sta->dbg_fixed_rate) {
|
||||
rs_build_rates_table_from_fixed(mvm, lq_cmd,
|
||||
lq_sta->band,
|
||||
lq_sta->dbg_fixed_rate);
|
||||
ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
|
||||
RATE_MCS_ANT_POS;
|
||||
} else
|
||||
#endif
|
||||
rs_build_rates_table(mvm, lq_sta, initial_rate);
|
||||
|
||||
if (num_of_ant(ant) == 1)
|
||||
lq_cmd->single_stream_ant_msk = ant;
|
||||
|
||||
lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
|
||||
lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
|
||||
|
||||
|
@ -2534,31 +2550,6 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
|
||||
u32 *rate_n_flags)
|
||||
{
|
||||
struct iwl_mvm *mvm;
|
||||
u8 valid_tx_ant;
|
||||
u8 ant_sel_tx;
|
||||
|
||||
mvm = lq_sta->drv;
|
||||
valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
|
||||
if (lq_sta->dbg_fixed_rate) {
|
||||
ant_sel_tx =
|
||||
((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
|
||||
>> RATE_MCS_ANT_POS);
|
||||
if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
|
||||
*rate_n_flags = lq_sta->dbg_fixed_rate;
|
||||
} else {
|
||||
lq_sta->dbg_fixed_rate = 0;
|
||||
IWL_ERR(mvm,
|
||||
"Invalid antenna selection 0x%X, Valid is 0x%X\n",
|
||||
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)
|
||||
{
|
||||
|
||||
|
@ -2612,6 +2603,31 @@ static int rs_pretty_print_rate(char *buf, const u32 rate)
|
|||
(rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Program the device to use fixed rate for frame transmit
|
||||
* This is for debugging/testing only
|
||||
* once the device start use fixed rate, we need to reload the module
|
||||
* to being back the normal operation.
|
||||
*/
|
||||
static void rs_program_fix_rate(struct iwl_mvm *mvm,
|
||||
struct iwl_lq_sta *lq_sta)
|
||||
{
|
||||
lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */
|
||||
lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
|
||||
lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */
|
||||
|
||||
IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
|
||||
lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
|
||||
|
||||
if (lq_sta->dbg_fixed_rate) {
|
||||
struct rs_rate rate;
|
||||
rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
|
||||
lq_sta->band, &rate);
|
||||
rs_fill_lq_cmd(NULL, NULL, lq_sta, &rate);
|
||||
iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
|
@ -2702,12 +2718,10 @@ 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++) {
|
||||
u32 rate = le32_to_cpu(lq_sta->lq.rs_table[i]);
|
||||
desc += sprintf(buff+desc,
|
||||
" rate[%d] 0x%X ",
|
||||
i, rate);
|
||||
u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
|
||||
|
||||
desc += rs_pretty_print_rate(buff+desc, rate);
|
||||
desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
|
||||
desc += rs_pretty_print_rate(buff+desc, r);
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
|
||||
|
@ -2741,14 +2755,14 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
|
|||
rate = &tbl->rate;
|
||||
desc += sprintf(buff+desc,
|
||||
"%s type=%d SGI=%d BW=%s DUP=0\n"
|
||||
"rate=0x%X\n",
|
||||
"index=%d\n",
|
||||
lq_sta->active_tbl == i ? "*" : "x",
|
||||
rate->type,
|
||||
rate->sgi,
|
||||
is_ht20(rate) ? "20Mhz" :
|
||||
is_ht40(rate) ? "40Mhz" :
|
||||
is_ht80(rate) ? "80Mhz" : "ERR",
|
||||
tbl->current_rate);
|
||||
rate->index);
|
||||
for (j = 0; j < IWL_RATE_COUNT; j++) {
|
||||
desc += sprintf(buff+desc,
|
||||
"counter=%d success=%d %%=%d\n",
|
||||
|
|
|
@ -278,7 +278,6 @@ struct iwl_scale_tbl_info {
|
|||
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 */
|
||||
};
|
||||
|
||||
|
@ -315,7 +314,6 @@ struct iwl_lq_sta {
|
|||
enum ieee80211_band band;
|
||||
|
||||
/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
|
||||
u32 supp_rates;
|
||||
u16 active_legacy_rate;
|
||||
u16 active_siso_rate;
|
||||
u16 active_mimo2_rate;
|
||||
|
|
|
@ -251,6 +251,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
|
|||
stats->flag |= RX_FLAG_DECRYPTED;
|
||||
return 0;
|
||||
|
||||
case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
|
||||
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
|
||||
return -1;
|
||||
stats->flag |= RX_FLAG_DECRYPTED;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* 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) 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) 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"
|
||||
|
||||
/* For counting bound interfaces */
|
||||
struct iwl_mvm_active_iface_iterator_data {
|
||||
struct ieee80211_vif *ignore_vif;
|
||||
u8 sta_vif_ap_sta_id;
|
||||
enum iwl_sf_state sta_vif_state;
|
||||
int num_active_macs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Count bound interfaces which are not p2p, besides data->ignore_vif.
|
||||
* data->station_vif will point to one bound vif of type station, if exists.
|
||||
*/
|
||||
static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_active_iface_iterator_data *data = _data;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
|
||||
vif->type == NL80211_IFTYPE_P2P_DEVICE)
|
||||
return;
|
||||
|
||||
data->num_active_macs++;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION) {
|
||||
data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
|
||||
if (vif->bss_conf.assoc)
|
||||
data->sta_vif_state = SF_FULL_ON;
|
||||
else
|
||||
data->sta_vif_state = SF_INIT_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Aging and idle timeouts for the different possible scenarios
|
||||
* in SF_FULL_ON state.
|
||||
*/
|
||||
static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
|
||||
{
|
||||
cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
|
||||
cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
|
||||
},
|
||||
{
|
||||
cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
|
||||
cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
|
||||
},
|
||||
{
|
||||
cpu_to_le32(SF_MCAST_AGING_TIMER),
|
||||
cpu_to_le32(SF_MCAST_IDLE_TIMER)
|
||||
},
|
||||
{
|
||||
cpu_to_le32(SF_BA_AGING_TIMER),
|
||||
cpu_to_le32(SF_BA_IDLE_TIMER)
|
||||
},
|
||||
{
|
||||
cpu_to_le32(SF_TX_RE_AGING_TIMER),
|
||||
cpu_to_le32(SF_TX_RE_IDLE_TIMER)
|
||||
},
|
||||
};
|
||||
|
||||
static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
int i, j, watermark;
|
||||
|
||||
sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
|
||||
|
||||
/*
|
||||
* If we are in association flow - check antenna configuration
|
||||
* capabilities of the AP station, and choose the watermark accordingly.
|
||||
*/
|
||||
if (sta) {
|
||||
if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
|
||||
switch (sta->rx_nss) {
|
||||
case 1:
|
||||
watermark = SF_W_MARK_SISO;
|
||||
break;
|
||||
case 2:
|
||||
watermark = SF_W_MARK_MIMO2;
|
||||
break;
|
||||
default:
|
||||
watermark = SF_W_MARK_MIMO3;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
watermark = SF_W_MARK_LEGACY;
|
||||
}
|
||||
/* default watermark value for unassociated mode. */
|
||||
} else {
|
||||
watermark = SF_W_MARK_MIMO2;
|
||||
}
|
||||
sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
|
||||
|
||||
for (i = 0; i < SF_NUM_SCENARIO; i++) {
|
||||
for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
|
||||
sf_cmd->long_delay_timeouts[i][j] =
|
||||
cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
|
||||
}
|
||||
}
|
||||
BUILD_BUG_ON(sizeof(sf_full_timeout) !=
|
||||
sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
|
||||
|
||||
memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
|
||||
sizeof(sf_full_timeout));
|
||||
}
|
||||
|
||||
static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
|
||||
enum iwl_sf_state new_state)
|
||||
{
|
||||
struct iwl_sf_cfg_cmd sf_cmd = {
|
||||
.state = new_state,
|
||||
};
|
||||
struct ieee80211_sta *sta;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* If an associated AP sta changed its antenna configuration, the state
|
||||
* will remain FULL_ON but SF parameters need to be reconsidered.
|
||||
*/
|
||||
if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
|
||||
return 0;
|
||||
|
||||
switch (new_state) {
|
||||
case SF_UNINIT:
|
||||
break;
|
||||
case SF_FULL_ON:
|
||||
if (sta_id == IWL_MVM_STATION_COUNT) {
|
||||
IWL_ERR(mvm,
|
||||
"No station: Cannot switch SF to FULL_ON\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rcu_read_lock();
|
||||
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
|
||||
if (IS_ERR_OR_NULL(sta)) {
|
||||
IWL_ERR(mvm, "Invalid station id\n");
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
iwl_mvm_fill_sf_command(&sf_cmd, sta);
|
||||
rcu_read_unlock();
|
||||
break;
|
||||
case SF_INIT_OFF:
|
||||
iwl_mvm_fill_sf_command(&sf_cmd, NULL);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
|
||||
new_state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
|
||||
sizeof(sf_cmd), &sf_cmd);
|
||||
if (!ret)
|
||||
mvm->sf_state = new_state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update Smart fifo:
|
||||
* Count bound interfaces that are not to be removed, ignoring p2p devices,
|
||||
* and set new state accordingly.
|
||||
*/
|
||||
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
|
||||
bool remove_vif)
|
||||
{
|
||||
enum iwl_sf_state new_state;
|
||||
u8 sta_id = IWL_MVM_STATION_COUNT;
|
||||
struct iwl_mvm_vif *mvmvif = NULL;
|
||||
struct iwl_mvm_active_iface_iterator_data data = {
|
||||
.ignore_vif = changed_vif,
|
||||
.sta_vif_state = SF_UNINIT,
|
||||
.sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
|
||||
};
|
||||
|
||||
if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ignore the call if we are in HW Restart flow, or if the handled
|
||||
* vif is a p2p device.
|
||||
*/
|
||||
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
|
||||
(changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
|
||||
return 0;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_bound_iface_iterator,
|
||||
&data);
|
||||
|
||||
/* If changed_vif exists and is not to be removed, add to the count */
|
||||
if (changed_vif && !remove_vif)
|
||||
data.num_active_macs++;
|
||||
|
||||
switch (data.num_active_macs) {
|
||||
case 0:
|
||||
/* If there are no active macs - change state to SF_INIT_OFF */
|
||||
new_state = SF_INIT_OFF;
|
||||
break;
|
||||
case 1:
|
||||
if (remove_vif) {
|
||||
/* The one active mac left is of type station
|
||||
* and we filled the relevant data during iteration
|
||||
*/
|
||||
new_state = data.sta_vif_state;
|
||||
sta_id = data.sta_vif_ap_sta_id;
|
||||
} else {
|
||||
if (WARN_ON(!changed_vif))
|
||||
return -EINVAL;
|
||||
if (changed_vif->type != NL80211_IFTYPE_STATION) {
|
||||
new_state = SF_UNINIT;
|
||||
} else if (changed_vif->bss_conf.assoc) {
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
|
||||
sta_id = mvmvif->ap_sta_id;
|
||||
new_state = SF_FULL_ON;
|
||||
} else {
|
||||
new_state = SF_INIT_OFF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* If there are multiple active macs - change to SF_UNINIT */
|
||||
new_state = SF_UNINIT;
|
||||
}
|
||||
return iwl_mvm_sf_config(mvm, sta_id, new_state);
|
||||
}
|
|
@ -939,19 +939,6 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
|
||||
sta->addr, tid);
|
||||
|
||||
if (mvm->cfg->ht_params->use_rts_for_aggregation) {
|
||||
/*
|
||||
* switch to RTS/CTS if it is the prefer protection
|
||||
* method for HT traffic
|
||||
* this function also sends the LQ command
|
||||
*/
|
||||
return iwl_mvm_tx_protection(mvm, mvmsta, true);
|
||||
/*
|
||||
* TODO: remove the TLC_RTS flag when we tear down the last
|
||||
* AGG session (agg_tids_count in DVM)
|
||||
*/
|
||||
}
|
||||
|
||||
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
|
||||
}
|
||||
|
||||
|
@ -1130,8 +1117,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
|
|||
memcpy(cmd.key, keyconf->key, keyconf->keylen);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
|
||||
memcpy(cmd.key, keyconf->key, keyconf->keylen);
|
||||
}
|
||||
|
||||
if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
|
||||
|
@ -1295,8 +1282,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
|
|||
0, NULL, CMD_SYNC);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
|
||||
ret = -EINVAL;
|
||||
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
|
||||
sta_id, 0, NULL, CMD_SYNC);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
|
|
@ -340,7 +340,7 @@ static void check_exit_ctkill(struct work_struct *work)
|
|||
|
||||
iwl_trans_start_hw(mvm->trans);
|
||||
temp = check_nic_temperature(mvm);
|
||||
iwl_trans_stop_hw(mvm->trans, false);
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
|
||||
if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
|
||||
IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
|
||||
|
|
|
@ -253,8 +253,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
|
|||
memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
|
||||
break;
|
||||
tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -518,6 +518,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
int i;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
/* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
|
||||
if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
|
||||
return;
|
||||
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
mvmvif->smps_requests[req_type] = smps_request;
|
||||
for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
|
||||
|
|
|
@ -256,7 +256,6 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
|
|||
* @hw_base: pci hardware address support
|
||||
* @ucode_write_complete: indicates that the ucode has been copied.
|
||||
* @ucode_write_waitq: wait queue for uCode load
|
||||
* @status - transport specific status flags
|
||||
* @cmd_queue - command queue number
|
||||
* @rx_buf_size_8k: 8 kB RX buffer size
|
||||
* @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
|
||||
|
@ -296,7 +295,6 @@ struct iwl_trans_pcie {
|
|||
wait_queue_head_t ucode_write_waitq;
|
||||
wait_queue_head_t wait_command_queue;
|
||||
|
||||
unsigned long status;
|
||||
u8 cmd_queue;
|
||||
u8 cmd_fifo;
|
||||
u8 n_no_reclaim_cmds;
|
||||
|
@ -315,24 +313,6 @@ struct iwl_trans_pcie {
|
|||
spinlock_t reg_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_pcie_status: status of the PCIe transport
|
||||
* @STATUS_HCMD_ACTIVE: a SYNC command is being processed
|
||||
* @STATUS_DEVICE_ENABLED: APM is enabled
|
||||
* @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
|
||||
* @STATUS_INT_ENABLED: interrupts are enabled
|
||||
* @STATUS_RFKILL: the HW RFkill switch is in KILL position
|
||||
* @STATUS_FW_ERROR: the fw is in error state
|
||||
*/
|
||||
enum iwl_pcie_status {
|
||||
STATUS_HCMD_ACTIVE,
|
||||
STATUS_DEVICE_ENABLED,
|
||||
STATUS_TPOWER_PMI,
|
||||
STATUS_INT_ENABLED,
|
||||
STATUS_RFKILL,
|
||||
STATUS_FW_ERROR,
|
||||
};
|
||||
|
||||
#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
|
||||
((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
|
||||
|
||||
|
@ -399,8 +379,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans);
|
|||
******************************************************/
|
||||
static inline void iwl_disable_interrupts(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
|
||||
clear_bit(STATUS_INT_ENABLED, &trans->status);
|
||||
|
||||
/* disable interrupts from uCode/NIC to host */
|
||||
iwl_write32(trans, CSR_INT_MASK, 0x00000000);
|
||||
|
@ -417,7 +396,7 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans)
|
|||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
|
||||
set_bit(STATUS_INT_ENABLED, &trans_pcie->status);
|
||||
set_bit(STATUS_INT_ENABLED, &trans->status);
|
||||
iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
|
||||
}
|
||||
|
||||
|
@ -477,12 +456,4 @@ 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__ */
|
||||
|
|
|
@ -162,11 +162,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
|
|||
rxq->write_actual = (rxq->write & ~0x7);
|
||||
iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
|
||||
} else {
|
||||
struct iwl_trans_pcie *trans_pcie =
|
||||
IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
/* If power-saving is in use, make sure device is awake */
|
||||
if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
|
||||
if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
|
||||
reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
|
||||
|
||||
if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
|
||||
|
@ -222,7 +219,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
|
|||
* stopped, we cannot access the HW (in particular not prph).
|
||||
* So don't try to restock if the APM has been already stopped.
|
||||
*/
|
||||
if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status))
|
||||
if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&rxq->lock, flags);
|
||||
|
@ -791,7 +788,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
|
|||
APMS_CLK_VAL_MRB_FUNC_MODE) ||
|
||||
(iwl_read_prph(trans, APMG_PS_CTRL_REG) &
|
||||
APMG_PS_CTRL_VAL_RESET_REQ))) {
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
iwl_op_mode_wimax_active(trans->op_mode);
|
||||
wake_up(&trans_pcie->wait_command_queue);
|
||||
return;
|
||||
|
@ -800,14 +797,14 @@ 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_nic_error(trans);
|
||||
/* The STATUS_FW_ERROR bit is set in this function. This must happen
|
||||
* before we wake up the command caller, to ensure a proper cleanup. */
|
||||
iwl_trans_fw_error(trans);
|
||||
local_bh_enable();
|
||||
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
wake_up(&trans_pcie->wait_command_queue);
|
||||
}
|
||||
|
||||
irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
|
||||
|
@ -894,14 +891,14 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
|
|||
|
||||
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
|
||||
if (hw_rfkill) {
|
||||
set_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
|
||||
&trans_pcie->status))
|
||||
set_bit(STATUS_RFKILL, &trans->status);
|
||||
if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
|
||||
&trans->status))
|
||||
IWL_DEBUG_RF_KILL(trans,
|
||||
"Rfkill while SYNC HCMD in flight\n");
|
||||
wake_up(&trans_pcie->wait_command_queue);
|
||||
} else {
|
||||
clear_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
clear_bit(STATUS_RFKILL, &trans->status);
|
||||
}
|
||||
|
||||
handled |= CSR_INT_BIT_RF_KILL;
|
||||
|
@ -1005,7 +1002,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
|
|||
|
||||
/* Re-enable all interrupts */
|
||||
/* only Re-enable if disabled by irq */
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status))
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans->status))
|
||||
iwl_enable_interrupts(trans);
|
||||
/* Re-enable RF_KILL if it occurred */
|
||||
else if (handled & CSR_INT_BIT_RF_KILL)
|
||||
|
@ -1160,7 +1157,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
|
|||
* the handler can be scheduled because of a previous
|
||||
* interrupt.
|
||||
*/
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans->status) &&
|
||||
!trans_pcie->inta)
|
||||
iwl_enable_interrupts(trans);
|
||||
return IRQ_NONE;
|
||||
|
@ -1290,7 +1287,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
|
|||
/* re-enable interrupts here since we don't have anything to service.
|
||||
* only Re-enable if disabled by irq.
|
||||
*/
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
|
||||
if (test_bit(STATUS_INT_ENABLED, &trans->status) &&
|
||||
!trans_pcie->inta)
|
||||
iwl_enable_interrupts(trans);
|
||||
|
||||
|
|
|
@ -150,7 +150,6 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
|
|||
*/
|
||||
static int iwl_pcie_apm_init(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
int ret = 0;
|
||||
IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
|
||||
|
||||
|
@ -223,7 +222,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
|
|||
/* Clear the interrupt in APMG if the NIC is in RFKILL */
|
||||
iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
|
||||
|
||||
set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
|
||||
set_bit(STATUS_DEVICE_ENABLED, &trans->status);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
@ -249,10 +248,9 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
|
|||
|
||||
static void iwl_pcie_apm_stop(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
|
||||
|
||||
clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
|
||||
clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
|
||||
|
||||
/* Stop device's DMA activity */
|
||||
iwl_pcie_apm_stop_master(trans);
|
||||
|
@ -582,7 +580,6 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
|
|||
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
|
||||
const struct fw_img *fw, bool run_in_rfkill)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
int ret;
|
||||
bool hw_rfkill;
|
||||
|
||||
|
@ -592,16 +589,14 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
clear_bit(STATUS_FW_ERROR, &trans_pcie->status);
|
||||
|
||||
iwl_enable_rfkill_int(trans);
|
||||
|
||||
/* If platform's RF_KILL switch is NOT set to KILL */
|
||||
hw_rfkill = iwl_is_rfkill_set(trans);
|
||||
if (hw_rfkill)
|
||||
set_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
set_bit(STATUS_RFKILL, &trans->status);
|
||||
else
|
||||
clear_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
clear_bit(STATUS_RFKILL, &trans->status);
|
||||
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
|
||||
if (hw_rfkill && !run_in_rfkill)
|
||||
return -ERFKILL;
|
||||
|
@ -641,6 +636,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
unsigned long flags;
|
||||
bool hw_rfkill;
|
||||
|
||||
/* tell the device to stop sending interrupts */
|
||||
spin_lock_irqsave(&trans_pcie->irq_lock, flags);
|
||||
|
@ -657,7 +653,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|||
* restart. So don't process again if the device is
|
||||
* already dead.
|
||||
*/
|
||||
if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) {
|
||||
if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
|
||||
iwl_pcie_tx_stop(trans);
|
||||
iwl_pcie_rx_stop(trans);
|
||||
|
||||
|
@ -681,17 +677,34 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|||
iwl_disable_interrupts(trans);
|
||||
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
|
||||
|
||||
iwl_enable_rfkill_int(trans);
|
||||
|
||||
/* stop and reset the on-board processor */
|
||||
iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
|
||||
|
||||
/* clear all status bits */
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
|
||||
clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
|
||||
clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
|
||||
clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
|
||||
clear_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
clear_bit(STATUS_INT_ENABLED, &trans->status);
|
||||
clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
|
||||
clear_bit(STATUS_TPOWER_PMI, &trans->status);
|
||||
clear_bit(STATUS_RFKILL, &trans->status);
|
||||
|
||||
/*
|
||||
* Even if we stop the HW, we still want the RF kill
|
||||
* interrupt
|
||||
*/
|
||||
iwl_enable_rfkill_int(trans);
|
||||
|
||||
/*
|
||||
* Check again since the RF kill state may have changed while
|
||||
* all the interrupts were disabled, in this case we couldn't
|
||||
* receive the RF kill interrupt and update the state in the
|
||||
* op_mode.
|
||||
*/
|
||||
hw_rfkill = iwl_is_rfkill_set(trans);
|
||||
if (hw_rfkill)
|
||||
set_bit(STATUS_RFKILL, &trans->status);
|
||||
else
|
||||
clear_bit(STATUS_RFKILL, &trans->status);
|
||||
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
|
||||
}
|
||||
|
||||
static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
|
||||
|
@ -776,7 +789,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
|
|||
|
||||
static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
bool hw_rfkill;
|
||||
int err;
|
||||
|
||||
|
@ -798,21 +810,20 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
|
|||
|
||||
hw_rfkill = iwl_is_rfkill_set(trans);
|
||||
if (hw_rfkill)
|
||||
set_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
set_bit(STATUS_RFKILL, &trans->status);
|
||||
else
|
||||
clear_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
clear_bit(STATUS_RFKILL, &trans->status);
|
||||
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
|
||||
bool op_mode_leaving)
|
||||
static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
bool hw_rfkill;
|
||||
unsigned long flags;
|
||||
|
||||
/* disable interrupts - don't enable HW RF kill interrupt */
|
||||
spin_lock_irqsave(&trans_pcie->irq_lock, flags);
|
||||
iwl_disable_interrupts(trans);
|
||||
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
|
||||
|
@ -824,27 +835,6 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
|
|||
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
|
||||
|
||||
iwl_pcie_disable_ict(trans);
|
||||
|
||||
if (!op_mode_leaving) {
|
||||
/*
|
||||
* Even if we stop the HW, we still want the RF kill
|
||||
* interrupt
|
||||
*/
|
||||
iwl_enable_rfkill_int(trans);
|
||||
|
||||
/*
|
||||
* Check again since the RF kill state may have changed while
|
||||
* all the interrupts were disabled, in this case we couldn't
|
||||
* receive the RF kill interrupt and update the state in the
|
||||
* op_mode.
|
||||
*/
|
||||
hw_rfkill = iwl_is_rfkill_set(trans);
|
||||
if (hw_rfkill)
|
||||
set_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
else
|
||||
clear_bit(STATUS_RFKILL, &trans_pcie->status);
|
||||
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
|
||||
|
@ -928,12 +918,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
|
|||
|
||||
static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
if (state)
|
||||
set_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
|
||||
set_bit(STATUS_TPOWER_PMI, &trans->status);
|
||||
else
|
||||
clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
|
||||
clear_bit(STATUS_TPOWER_PMI, &trans->status);
|
||||
}
|
||||
|
||||
static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
|
||||
|
@ -1457,7 +1445,7 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
|
|||
|
||||
static const struct iwl_trans_ops trans_ops_pcie = {
|
||||
.start_hw = iwl_trans_pcie_start_hw,
|
||||
.stop_hw = iwl_trans_pcie_stop_hw,
|
||||
.op_mode_leave = iwl_trans_pcie_op_mode_leave,
|
||||
.fw_alive = iwl_trans_pcie_fw_alive,
|
||||
.start_fw = iwl_trans_pcie_start_fw,
|
||||
.stop_device = iwl_trans_pcie_stop_device,
|
||||
|
|
|
@ -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_nic_error(trans);
|
||||
iwl_trans_fw_error(trans);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -300,10 +300,8 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
|
|||
iwl_write32(trans, HBUS_TARG_WRPTR,
|
||||
txq->q.write_ptr | (txq_id << 8));
|
||||
} else {
|
||||
struct iwl_trans_pcie *trans_pcie =
|
||||
IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
/* if we're trying to save power */
|
||||
if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
|
||||
if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
|
||||
/* wake up nic if it's powered down ...
|
||||
* uCode will wake up, and interrupt us again, so next
|
||||
* time we'll skip this part. */
|
||||
|
@ -1023,7 +1021,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_nic_error(trans);
|
||||
iwl_trans_fw_error(trans);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1449,12 +1447,12 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
|
|||
iwl_pcie_cmdq_reclaim(trans, txq_id, index);
|
||||
|
||||
if (!(meta->flags & CMD_ASYNC)) {
|
||||
if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) {
|
||||
if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
|
||||
IWL_WARN(trans,
|
||||
"HCMD_ACTIVE already clear for command %s\n",
|
||||
get_cmd_string(trans_pcie, cmd->hdr.cmd));
|
||||
}
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
|
||||
get_cmd_string(trans_pcie, cmd->hdr.cmd));
|
||||
wake_up(&trans_pcie->wait_command_queue);
|
||||
|
@ -1499,8 +1497,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
|
||||
get_cmd_string(trans_pcie, cmd->id));
|
||||
|
||||
if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE,
|
||||
&trans_pcie->status),
|
||||
if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
|
||||
&trans->status),
|
||||
"Command %s: a command is already active!\n",
|
||||
get_cmd_string(trans_pcie, cmd->id)))
|
||||
return -EIO;
|
||||
|
@ -1511,7 +1509,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
|
||||
if (cmd_idx < 0) {
|
||||
ret = cmd_idx;
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
IWL_ERR(trans,
|
||||
"Error sending %s: enqueue_hcmd failed: %d\n",
|
||||
get_cmd_string(trans_pcie, cmd->id), ret);
|
||||
|
@ -1523,8 +1521,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
|
||||
timeout -= COMMAND_POKE_TIMEOUT;
|
||||
ret = wait_event_timeout(trans_pcie->wait_command_queue,
|
||||
!test_bit(STATUS_HCMD_ACTIVE,
|
||||
&trans_pcie->status),
|
||||
!test_bit(STATUS_SYNC_HCMD_ACTIVE,
|
||||
&trans->status),
|
||||
COMMAND_POKE_TIMEOUT);
|
||||
if (ret)
|
||||
break;
|
||||
|
@ -1552,17 +1550,17 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
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);
|
||||
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
||||
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
|
||||
get_cmd_string(trans_pcie, cmd->id));
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
iwl_nic_error(trans);
|
||||
iwl_trans_fw_error(trans);
|
||||
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) {
|
||||
if (test_bit(STATUS_FW_ERROR, &trans->status)) {
|
||||
IWL_ERR(trans, "FW error in SYNC CMD %s\n",
|
||||
get_cmd_string(trans_pcie, cmd->id));
|
||||
dump_stack();
|
||||
|
@ -1571,7 +1569,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
}
|
||||
|
||||
if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
|
||||
test_bit(STATUS_RFKILL, &trans_pcie->status)) {
|
||||
test_bit(STATUS_RFKILL, &trans->status)) {
|
||||
IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
|
||||
ret = -ERFKILL;
|
||||
goto cancel;
|
||||
|
@ -1608,13 +1606,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
|
||||
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
|
||||
return -EIO;
|
||||
|
||||
if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
|
||||
test_bit(STATUS_RFKILL, &trans_pcie->status)) {
|
||||
test_bit(STATUS_RFKILL, &trans->status)) {
|
||||
IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
|
||||
cmd->id);
|
||||
return -ERFKILL;
|
||||
|
|
Loading…
Reference in New Issue