Merge git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next

This commit is contained in:
John W. Linville 2014-02-12 15:21:07 -05:00
commit e9c6531677
52 changed files with 2777 additions and 1088 deletions

View file

@ -68,6 +68,19 @@ config IWLWIFI_OPMODE_MODULAR
comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
depends on IWLWIFI && IWLDVM=n && IWLMVM=n
config IWLWIFI_BCAST_FILTERING
bool "Enable broadcast filtering"
depends on IWLMVM
help
Say Y here to enable default bcast filtering configuration.
Enabling broadcast filtering will drop any incoming wireless
broadcast frames, except some very specific predefined
patterns (e.g. incoming arp requests).
If unsure, don't enable this option, as some programs might
expect incoming broadcasts for their normal operations.
menu "Debugging Options"
depends on IWLWIFI
@ -111,6 +124,7 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
Enable use of experimental ucode for testing and debugging.
config IWLWIFI_DEVICE_TRACING
bool "iwlwifi device access tracing"
depends on IWLWIFI
depends on EVENT_TRACING

View file

@ -8,7 +8,7 @@ iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o
iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o
iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o
iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o
iwlwifi-objs += $(iwlwifi-m)

View file

@ -176,46 +176,46 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
* (2.4 GHz) band.
*/
static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
};
static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */
{0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */
{0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */
{0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
};
static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
{0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
{0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
{0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
};
static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
{0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
{0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
{0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
};
static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
{0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
{0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
{0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
};
static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
{0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
{0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
{0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
};
static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */
{0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */
{0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */
@ -1111,7 +1111,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl)
{
/* Used to choose among HT tables */
s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
/* Check for invalid LQ type */
if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
@ -1173,9 +1173,8 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
&(lq_sta->lq_info[lq_sta->active_tbl]);
s32 active_sr = active_tbl->win[index].success_ratio;
s32 active_tpt = active_tbl->expected_tpt[index];
/* expected "search" throughput */
s32 *tpt_tbl = tbl->expected_tpt;
const u16 *tpt_tbl = tbl->expected_tpt;
s32 new_rate, high, low, start_hi;
u16 high_low;

View file

@ -315,7 +315,7 @@ struct iwl_scale_tbl_info {
u8 is_dup; /* 1 = duplicated data streams */
u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
u8 max_search; /* maximun number of tables we can search */
s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
const u16 *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 */
};

View file

@ -71,8 +71,8 @@
#define IWL3160_UCODE_API_MAX 8
/* Oldest version we won't warn about */
#define IWL7260_UCODE_API_OK 7
#define IWL3160_UCODE_API_OK 7
#define IWL7260_UCODE_API_OK 8
#define IWL3160_UCODE_API_OK 8
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 7
@ -95,6 +95,8 @@
#define IWL7265_FW_PRE "iwlwifi-7265-"
#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_7000 0
static const struct iwl_base_params iwl7000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
@ -120,7 +122,8 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl7000_base_params, \
.led_mode = IWL_LED_RF_STATE
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000
const struct iwl_cfg iwl7260_2ac_cfg = {
@ -194,6 +197,17 @@ const struct iwl_cfg iwl3160_n_cfg = {
.host_interrupt_operation_mode = true,
};
static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = {
{.pwr = 1600, .backoff = 0},
{.pwr = 1300, .backoff = 467},
{.pwr = 900, .backoff = 1900},
{.pwr = 800, .backoff = 2630},
{.pwr = 700, .backoff = 3720},
{.pwr = 600, .backoff = 5550},
{.pwr = 500, .backoff = 9350},
{0},
};
const struct iwl_cfg iwl7265_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7265",
.fw_name_pre = IWL7265_FW_PRE,
@ -201,6 +215,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
const struct iwl_cfg iwl7265_2n_cfg = {
@ -210,6 +225,7 @@ const struct iwl_cfg iwl7265_2n_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
const struct iwl_cfg iwl7265_n_cfg = {
@ -219,6 +235,7 @@ const struct iwl_cfg iwl7265_n_cfg = {
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));

View file

@ -0,0 +1,132 @@
/******************************************************************************
*
* 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) 2014 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) 2014 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 <linux/module.h>
#include <linux/stringify.h>
#include "iwl-config.h"
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
#define IWL8000_UCODE_API_MAX 8
/* Oldest version we won't warn about */
#define IWL8000_UCODE_API_OK 8
/* Lowest firmware API version supported */
#define IWL8000_UCODE_API_MIN 8
/* NVM versions */
#define IWL8000_NVM_VERSION 0x0a1d
#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */
#define IWL8000_FW_PRE "iwlwifi-8000-"
#define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_8000 10
static const struct iwl_base_params iwl8000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
.pll_cfg_val = 0,
.shadow_ram_support = true,
.led_compensation = 57,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.max_event_log_size = 512,
.shadow_reg_enable = true,
.pcie_l1_allowed = true,
};
static const struct iwl_ht_params iwl8000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
};
#define IWL_DEVICE_8000 \
.ucode_api_max = IWL8000_UCODE_API_MAX, \
.ucode_api_ok = IWL8000_UCODE_API_OK, \
.ucode_api_min = IWL8000_UCODE_API_MIN, \
.device_family = IWL_DEVICE_FAMILY_8000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
.base_params = &iwl8000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000
const struct iwl_cfg iwl8260_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 8260",
.fw_name_pre = IWL8000_FW_PRE,
IWL_DEVICE_8000,
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
};
const struct iwl_cfg iwl8260_n_cfg = {
.name = "Intel(R) Dual Band Wireless-AC 8260",
.fw_name_pre = IWL8000_FW_PRE,
IWL_DEVICE_8000,
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
};
MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));

View file

@ -84,6 +84,7 @@ enum iwl_device_family {
IWL_DEVICE_FAMILY_6050,
IWL_DEVICE_FAMILY_6150,
IWL_DEVICE_FAMILY_7000,
IWL_DEVICE_FAMILY_8000,
};
/*
@ -192,6 +193,15 @@ struct iwl_eeprom_params {
bool enhanced_txpower;
};
/* Tx-backoff power threshold
* @pwr: The power limit in mw
* @backoff: The tx-backoff in uSec
*/
struct iwl_pwr_tx_backoff {
u32 pwr;
u32 backoff;
};
/**
* struct iwl_cfg
* @name: Offical name of the device
@ -217,6 +227,9 @@ struct iwl_eeprom_params {
* @high_temp: Is this NIC is designated to be in high temperature.
* @host_interrupt_operation_mode: device needs host interrupt operation
* mode set
* @d0i3: device uses d0i3 instead of d3
* @nvm_hw_section_num: the ID of the HW NVM section
* @pwr_tx_backoffs: translation table between power limits and backoffs
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
@ -247,6 +260,9 @@ struct iwl_cfg {
const bool internal_wimax_coex;
const bool host_interrupt_operation_mode;
bool high_temp;
bool d0i3;
u8 nvm_hw_section_num;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
};
/*
@ -307,6 +323,8 @@ extern const struct iwl_cfg iwl3160_n_cfg;
extern const struct iwl_cfg iwl7265_2ac_cfg;
extern const struct iwl_cfg iwl7265_2n_cfg;
extern const struct iwl_cfg iwl7265_n_cfg;
extern const struct iwl_cfg iwl8260_2ac_cfg;
extern const struct iwl_cfg iwl8260_n_cfg;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */

View file

@ -395,38 +395,6 @@
#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
/* SECURE boot registers */
#define CSR_SECURE_BOOT_CONFIG_ADDR (0x100)
enum secure_boot_config_reg {
CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
};
#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR (0x100)
#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR (0x100)
enum secure_boot_status_reg {
CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000003,
CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002,
CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004,
CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008,
CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010,
};
#define CSR_UCODE_LOAD_STATUS_ADDR (0x100)
enum secure_load_status_reg {
CSR_CPU_STATUS_LOADING_STARTED = 0x00000001,
CSR_CPU_STATUS_LOADING_COMPLETED = 0x00000002,
CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
};
#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100)
#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100)
#define CSR_SECURE_TIME_OUT (100)
#define FH_TCSR_0_REG0 (0x1D00)
/*
* HBUS (Host-side Bus)
*

View file

@ -126,6 +126,7 @@ do { \
/* 0x00000F00 - 0x00000100 */
#define IWL_DL_POWER 0x00000100
#define IWL_DL_TEMP 0x00000200
#define IWL_DL_RPM 0x00000400
#define IWL_DL_SCAN 0x00000800
/* 0x0000F000 - 0x00001000 */
#define IWL_DL_ASSOC 0x00001000
@ -189,5 +190,6 @@ do { \
#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a)
#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
#endif

View file

@ -128,7 +128,7 @@ struct iwl_drv {
const struct iwl_cfg *cfg;
int fw_index; /* firmware we're trying to load */
char firmware_name[25]; /* name of firmware file to load */
char firmware_name[32]; /* name of firmware file to load */
struct completion request_firmware_complete;
@ -237,7 +237,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
return -ENOENT;
}
sprintf(drv->firmware_name, "%s%s%s", name_pre, tag, ".ucode");
snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
name_pre, tag);
IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
(drv->fw_index == UCODE_EXPERIMENTAL_INDEX)

View file

@ -95,6 +95,8 @@
* @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
* @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
* @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
*/
enum iwl_ucode_tlv_flag {
IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
@ -119,6 +121,8 @@ enum iwl_ucode_tlv_flag {
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),
IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
};
/* The default calibrate table size if not specified by firmware file */
@ -160,8 +164,7 @@ enum iwl_ucode_sec {
* For 16.0 uCode and above, there is no differentiation between sections,
* just an offset to the HW address.
*/
#define IWL_UCODE_SECTION_MAX 6
#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU (IWL_UCODE_SECTION_MAX/2)
#define IWL_UCODE_SECTION_MAX 12
struct iwl_ucode_capabilities {
u32 max_probe_length;

View file

@ -130,6 +130,21 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
}
IWL_EXPORT_SYMBOL(iwl_write_prph);
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout)
{
int t = 0;
do {
if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
} while (t < timeout);
return -ETIMEDOUT;
}
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
{
unsigned long flags;

View file

@ -72,6 +72,8 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout);
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
u32 bits, u32 mask);

View file

@ -397,11 +397,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
tx_chains, rx_chains);
data->calib_version = 255; /* TODO:
this value will prevent some checks from
failing, we need to check if this
field is still needed, and if it does,
where is it in the NVM*/
data->calib_version = 255;
return data;
}

View file

@ -131,6 +131,8 @@ struct iwl_cfg;
* @nic_config: configure NIC, called before firmware is started.
* May sleep
* @wimax_active: invoked when WiMax becomes active. May sleep
* @enter_d0i3: configure the fw to enter d0i3. May sleep.
* @exit_d0i3: configure the fw to exit d0i3. May sleep.
*/
struct iwl_op_mode_ops {
struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@ -148,6 +150,8 @@ struct iwl_op_mode_ops {
void (*cmd_queue_full)(struct iwl_op_mode *op_mode);
void (*nic_config)(struct iwl_op_mode *op_mode);
void (*wimax_active)(struct iwl_op_mode *op_mode);
int (*enter_d0i3)(struct iwl_op_mode *op_mode);
int (*exit_d0i3)(struct iwl_op_mode *op_mode);
};
int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
@ -155,7 +159,7 @@ void iwl_opmode_deregister(const char *name);
/**
* struct iwl_op_mode - operational mode
* @ops - pointer to its own ops
* @ops: pointer to its own ops
*
* This holds an implementation of the mac80211 / fw API.
*/
@ -226,4 +230,22 @@ static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
op_mode->ops->wimax_active(op_mode);
}
static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode)
{
might_sleep();
if (!op_mode->ops->enter_d0i3)
return 0;
return op_mode->ops->enter_d0i3(op_mode);
}
static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
{
might_sleep();
if (!op_mode->ops->exit_d0i3)
return 0;
return op_mode->ops->exit_d0i3(op_mode);
}
#endif /* __iwl_op_mode_h__ */

View file

@ -72,7 +72,7 @@
#include "iwl-trans.h"
#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
#define IWL_NUM_PAPD_CH_GROUPS 4
#define IWL_NUM_PAPD_CH_GROUPS 7
#define IWL_NUM_TXP_CH_GROUPS 9
struct iwl_phy_db_entry {
@ -383,7 +383,7 @@ static int iwl_phy_db_send_all_channel_groups(
if (!entry)
return -EINVAL;
if (WARN_ON_ONCE(!entry->size))
if (!entry->size)
continue;
/* Send the requested PHY DB section */

View file

@ -105,6 +105,13 @@
/* Device NMI register */
#define DEVICE_SET_NMI_REG 0x00a01c30
/*
* Device reset for family 8000
* write to bit 24 in order to reset the CPU
*/
#define RELEASE_CPU_RESET (0x300C)
#define RELEASE_CPU_RESET_BIT BIT(24)
/*****************************************************************************
* 7000/3000 series SHR DTS addresses *
*****************************************************************************/
@ -281,4 +288,43 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
#define OSC_CLK (0xa04068)
#define OSC_CLK_FORCE_CONTROL (0x8)
/* SECURE boot registers */
#define LMPM_SECURE_BOOT_CONFIG_ADDR (0x100)
enum secure_boot_config_reg {
LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
};
#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR (0x1E30)
#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR (0x1E34)
enum secure_boot_status_reg {
LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000001,
LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002,
LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004,
LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008,
LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010,
LMPM_SECURE_BOOT_STATUS_SUCCESS = 0x00000003,
};
#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
enum secure_load_status_reg {
LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001,
LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003,
LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007,
LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8,
LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00,
};
#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38)
#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C)
#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78)
#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C)
#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000)
#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000)
#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
#define LMPM_SECURE_TIME_OUT (100)
#endif /* __iwl_prph_h__ */

View file

@ -193,12 +193,23 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
* @CMD_ASYNC: Return right away and don't wait for the response
* @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
* response. The caller needs to call iwl_free_resp when done.
* @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
* command queue, but after other high priority commands. valid only
* with CMD_ASYNC.
* @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
* @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
* @CMD_WAKE_UP_TRANS: The command response should wake up the trans
* (i.e. mark it as non-idle).
*/
enum CMD_MODE {
CMD_SYNC = 0,
CMD_ASYNC = BIT(0),
CMD_WANT_SKB = BIT(1),
CMD_SEND_IN_RFKILL = BIT(2),
CMD_HIGH_PRIO = BIT(3),
CMD_SEND_IN_IDLE = BIT(4),
CMD_MAKE_TRANS_IDLE = BIT(5),
CMD_WAKE_UP_TRANS = BIT(6),
};
#define DEF_CMD_PAYLOAD_SIZE 320
@ -335,6 +346,9 @@ enum iwl_d3_status {
* @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
* @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands
* are sent
* @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
*/
enum iwl_trans_status {
STATUS_SYNC_HCMD_ACTIVE,
@ -343,6 +357,8 @@ enum iwl_trans_status {
STATUS_INT_ENABLED,
STATUS_RFKILL,
STATUS_FW_ERROR,
STATUS_TRANS_GOING_IDLE,
STATUS_TRANS_IDLE,
};
/**
@ -443,6 +459,11 @@ struct iwl_trans;
* @release_nic_access: let the NIC go to sleep. The "flags" parameter
* must be the same one that was sent before to the grab_nic_access.
* @set_bits_mask - set SRAM register according to value and mask.
* @ref: grab a reference to the transport/FW layers, disallowing
* certain low power states
* @unref: release a reference previously taken with @ref. Note that
* initially the reference count is 1, making an initial @unref
* necessary to allow low power states.
*/
struct iwl_trans_ops {
@ -489,6 +510,8 @@ struct iwl_trans_ops {
unsigned long *flags);
void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
u32 value);
void (*ref)(struct iwl_trans *trans);
void (*unref)(struct iwl_trans *trans);
};
/**
@ -523,6 +546,7 @@ enum iwl_trans_state {
* starting the firmware, used for tracing
* @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
* start of the 802.11 header in the @rx_mpdu_cmd
* @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
*/
struct iwl_trans {
const struct iwl_trans_ops *ops;
@ -551,6 +575,8 @@ struct iwl_trans {
struct lockdep_map sync_cmd_lockdep_map;
#endif
u64 dflt_pwr_limit;
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
char trans_specific[0] __aligned(sizeof(void *));
@ -627,6 +653,18 @@ static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
return trans->ops->d3_resume(trans, status, test);
}
static inline void iwl_trans_ref(struct iwl_trans *trans)
{
if (trans->ops->ref)
trans->ops->ref(trans);
}
static inline void iwl_trans_unref(struct iwl_trans *trans)
{
if (trans->ops->unref)
trans->ops->unref(trans);
}
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{

View file

@ -2,7 +2,7 @@ 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 sf.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o power_legacy.o bt-coex.o
iwlmvm-y += power.o bt-coex.o
iwlmvm-y += led.o tt.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o

View file

@ -378,7 +378,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
flags = iwlwifi_mod_params.bt_coex_active ?
BT_COEX_NW : BT_COEX_DISABLE;
flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE;
bt_cmd->flags = cpu_to_le32(flags);
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
@ -399,6 +398,9 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
BT_VALID_TXRX_MAX_FREQ_0 |
BT_VALID_SYNC_TO_SCO);
if (IWL_MVM_BT_COEX_SYNC2SCO)
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
if (mvm->cfg->bt_shared_single_ant)
memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
sizeof(iwl_single_shared_ant));
@ -489,8 +491,7 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
return ret;
}
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
bool enable)
int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
{
struct iwl_bt_coex_cmd *bt_cmd;
/* Send ASYNC since this can be sent from an atomic context */
@ -500,25 +501,16 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
.dataflags = { IWL_HCMD_DFL_DUP, },
.flags = CMD_ASYNC,
};
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
int ret;
if (sta_id == IWL_MVM_STATION_COUNT)
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
if (!mvmsta)
return 0;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
/* This can happen if the station has been removed right now */
if (IS_ERR_OR_NULL(sta))
return 0;
mvmsta = iwl_mvm_sta_from_mac80211(sta);
/* nothing to do */
if (mvmsta->bt_reduced_txpower == enable)
if (mvmsta->bt_reduced_txpower_dbg ||
mvmsta->bt_reduced_txpower == enable)
return 0;
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
@ -552,6 +544,7 @@ struct iwl_bt_iterator_data {
bool reduced_tx_power;
struct ieee80211_chanctx_conf *primary;
struct ieee80211_chanctx_conf *secondary;
bool primary_ll;
};
static inline
@ -577,72 +570,113 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
struct iwl_mvm *mvm = data->mvm;
struct ieee80211_chanctx_conf *chanctx_conf;
enum ieee80211_smps_mode smps_mode;
u32 bt_activity_grading;
int ave_rssi;
lockdep_assert_held(&mvm->mutex);
if (vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP)
return;
switch (vif->type) {
case NL80211_IFTYPE_STATION:
/* default smps_mode for BSS / P2P client is AUTOMATIC */
smps_mode = IEEE80211_SMPS_AUTOMATIC;
data->num_bss_ifaces++;
smps_mode = IEEE80211_SMPS_AUTOMATIC;
/*
* Count unassoc BSSes, relax SMSP constraints
* and disable reduced Tx Power
*/
if (!vif->bss_conf.assoc) {
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
smps_mode);
if (iwl_mvm_bt_coex_reduced_txp(mvm,
mvmvif->ap_sta_id,
false))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
return;
}
break;
case NL80211_IFTYPE_AP:
/* default smps_mode for AP / GO is OFF */
smps_mode = IEEE80211_SMPS_OFF;
if (!mvmvif->ap_ibss_active) {
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
smps_mode);
return;
}
/* the Ack / Cts kill mask must be default if AP / GO */
data->reduced_tx_power = false;
break;
default:
return;
}
chanctx_conf = rcu_dereference(vif->chanctx_conf);
/* If channel context is invalid or not on 2.4GHz .. */
if ((!chanctx_conf ||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
/* ... and it is an associated STATION, relax constraints */
if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc)
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
smps_mode);
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
/* ... relax constraints and disable rssi events */
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
smps_mode);
if (vif->type == NL80211_IFTYPE_STATION)
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
return;
}
/* SoftAP / GO will always be primary */
bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
if (bt_activity_grading >= BT_HIGH_TRAFFIC)
smps_mode = IEEE80211_SMPS_STATIC;
else if (bt_activity_grading >= BT_LOW_TRAFFIC)
smps_mode = vif->type == NL80211_IFTYPE_AP ?
IEEE80211_SMPS_OFF :
IEEE80211_SMPS_DYNAMIC;
IWL_DEBUG_COEX(data->mvm,
"mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
mvmvif->id, data->notif->bt_status, bt_activity_grading,
smps_mode);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
/* low latency is always primary */
if (iwl_mvm_vif_low_latency(mvmvif)) {
data->primary_ll = true;
data->secondary = data->primary;
data->primary = chanctx_conf;
}
if (vif->type == NL80211_IFTYPE_AP) {
if (!mvmvif->ap_ibss_active)
return;
/* the Ack / Cts kill mask must be default if AP / GO */
data->reduced_tx_power = false;
if (chanctx_conf == data->primary)
return;
/* downgrade the current primary no matter what its type is */
data->secondary = data->primary;
data->primary = chanctx_conf;
if (!data->primary_ll) {
/*
* downgrade the current primary no matter what its
* type is.
*/
data->secondary = data->primary;
data->primary = chanctx_conf;
} else {
/* there is low latency vif - we will be secondary */
data->secondary = chanctx_conf;
}
return;
}
data->num_bss_ifaces++;
/* we are now a STA / P2P Client, and take associated ones only */
if (!vif->bss_conf.assoc)
return;
/* STA / P2P Client, try to be primary if first vif */
/*
* STA / P2P Client, try to be primary if first vif. If we are in low
* latency mode, we are already in primary and just don't do much
*/
if (!data->primary || data->primary == chanctx_conf)
data->primary = chanctx_conf;
else if (!data->secondary)
/* if secondary is not NULL, it might be a GO */
data->secondary = chanctx_conf;
if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC)
smps_mode = IEEE80211_SMPS_STATIC;
else if (le32_to_cpu(data->notif->bt_activity_grading) >=
BT_LOW_TRAFFIC)
smps_mode = IEEE80211_SMPS_DYNAMIC;
IWL_DEBUG_COEX(data->mvm,
"mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
mvmvif->id, data->notif->bt_status,
data->notif->bt_activity_grading, smps_mode);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
/* don't reduce the Tx power if in loose scheme */
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
mvm->cfg->bt_shared_single_ant) {

View file

@ -78,5 +78,9 @@
#define IWL_MVM_PS_SNOOZE_INTERVAL 25
#define IWL_MVM_PS_SNOOZE_WINDOW 50
#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
#define IWL_MVM_LOWLAT_SINGLE_BINDING_MAXDUR 24 /* TU */
#define IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR 24 /* TU */
#define IWL_MVM_BT_COEX_SYNC2SCO 1
#endif /* __MVM_CONSTANTS_H */

View file

@ -963,7 +963,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
};
int ret, i;
int len __maybe_unused;
u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
if (!wowlan) {
/*
@ -980,8 +979,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
old_aux_sta_id = mvm->aux_sta.sta_id;
/* see if there's only a single BSS vif and it's associated */
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@ -1066,16 +1063,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
iwl_trans_stop_device(mvm->trans);
/*
* The D3 firmware still hardcodes the AP station ID for the
* BSS we're associated with as 0. Store the real STA ID here
* and assign 0. When we leave this function, we'll restore
* the original value for the resume code.
*/
old_ap_sta_id = mvm_ap_sta->sta_id;
mvm_ap_sta->sta_id = 0;
mvmvif->ap_sta_id = 0;
/*
* Set the HW restart bit -- this is mostly true as we're
* going to load new firmware and reprogram that, though
@ -1096,16 +1083,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
mvm->ptk_ivlen = 0;
mvm->ptk_icvlen = 0;
/*
* The D3 firmware still hardcodes the AP station ID for the
* BSS we're associated with as 0. As a result, we have to move
* the auxiliary station to ID 1 so the ID 0 remains free for
* the AP station for later.
* We set the sta_id to 1 here, and reset it to its previous
* value (that we stored above) later.
*/
mvm->aux_sta.sta_id = 1;
ret = iwl_mvm_load_d3_fw(mvm);
if (ret)
goto out;
@ -1191,11 +1168,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (ret)
goto out;
ret = iwl_mvm_power_update_device_mode(mvm);
ret = iwl_mvm_power_update_device(mvm);
if (ret)
goto out;
ret = iwl_mvm_power_update_mode(mvm, vif);
ret = iwl_mvm_power_update_mac(mvm, vif);
if (ret)
goto out;
@ -1222,10 +1199,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
iwl_trans_d3_suspend(mvm->trans, test);
out:
mvm->aux_sta.sta_id = old_aux_sta_id;
mvm_ap_sta->sta_id = old_ap_sta_id;
mvmvif->ap_sta_id = old_ap_sta_id;
if (ret < 0)
ieee80211_restart_hw(mvm->hw);
out_noreset:

View file

@ -185,7 +185,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
mutex_lock(&mvm->mutex);
iwl_dbgfs_update_pm(mvm, vif, param, val);
ret = iwl_mvm_power_update_mode(mvm, vif);
ret = iwl_mvm_power_update_mac(mvm, vif);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@ -202,7 +202,7 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
int bufsz = sizeof(buf);
int pos;
pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@ -225,6 +225,29 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
ap_sta_id = mvmvif->ap_sta_id;
switch (ieee80211_vif_type_p2p(vif)) {
case NL80211_IFTYPE_ADHOC:
pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
break;
case NL80211_IFTYPE_STATION:
pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
break;
case NL80211_IFTYPE_AP:
pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
break;
case NL80211_IFTYPE_P2P_CLIENT:
pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
break;
case NL80211_IFTYPE_P2P_GO:
pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
break;
case NL80211_IFTYPE_P2P_DEVICE:
pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
break;
default:
break;
}
pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
mvmvif->id, mvmvif->color);
pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
@ -249,9 +272,10 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
pos += scnprintf(buf+pos, bufsz-pos,
"ap_sta_id %d - reduced Tx power %d\n",
"ap_sta_id %d - reduced Tx power %d force %d\n",
ap_sta_id,
mvm_sta->bt_reduced_txpower);
mvm_sta->bt_reduced_txpower,
mvm_sta->bt_reduced_txpower_dbg);
}
}
@ -269,6 +293,36 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
static ssize_t iwl_dbgfs_reduced_txp_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;
struct iwl_mvm_sta *mvmsta;
bool reduced_tx_power;
int ret;
if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
return -ENOTCONN;
if (strtobool(buf, &reduced_tx_power) != 0)
return -EINVAL;
mutex_lock(&mvm->mutex);
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
mvmsta->bt_reduced_txpower_dbg = false;
ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
reduced_tx_power);
if (!ret)
mvmsta->bt_reduced_txpower_dbg = true;
mutex_unlock(&mvm->mutex);
return ret ? : count;
}
static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
enum iwl_dbgfs_bf_mask param, int value)
{
@ -403,9 +457,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
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);
ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
else
ret = iwl_mvm_enable_beacon_filter(mvm, vif);
ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@ -460,6 +514,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
static ssize_t iwl_dbgfs_low_latency_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;
u8 value;
int ret;
ret = kstrtou8(buf, 0, &value);
if (ret)
return ret;
if (value > 1)
return -EINVAL;
mutex_lock(&mvm->mutex);
iwl_mvm_update_low_latency(mvm, vif, value);
mutex_unlock(&mvm->mutex);
return count;
}
static ssize_t iwl_dbgfs_low_latency_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[3];
buf[0] = mvmvif->low_latency ? '1' : '0';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
}
#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) \
@ -473,6 +562,8 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
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);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10);
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
@ -496,15 +587,18 @@ 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 &&
if ((mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) &&
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);
MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
S_IRUSR | S_IWUSR);
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
mvmvif == mvm->bf_allowed_vif)

View file

@ -90,7 +90,7 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta;
int sta_id, drain, ret;
if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
@ -105,13 +105,12 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
mutex_lock(&mvm->mutex);
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(sta))
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
if (!mvmsta)
ret = -ENOENT;
else
ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
count;
ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
mutex_unlock(&mvm->mutex);
@ -251,7 +250,7 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
}
mutex_lock(&mvm->mutex);
ret = iwl_mvm_power_update_device_mode(mvm);
ret = iwl_mvm_power_update_device(mvm);
mutex_unlock(&mvm->mutex);
return ret ?: count;
@ -600,6 +599,187 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
return count;
}
#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_bcast_filter_cmd cmd;
const struct iwl_fw_bcast_filter *filter;
char *buf;
int bufsz = 1024;
int i, j, pos = 0;
ssize_t ret;
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&mvm->mutex);
if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
ADD_TEXT("None\n");
mutex_unlock(&mvm->mutex);
goto out;
}
mutex_unlock(&mvm->mutex);
for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
filter = &cmd.filters[i];
ADD_TEXT("Filter [%d]:\n", i);
ADD_TEXT("\tDiscard=%d\n", filter->discard);
ADD_TEXT("\tFrame Type: %s\n",
filter->frame_type ? "IPv4" : "Generic");
for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
const struct iwl_fw_bcast_filter_attr *attr;
attr = &filter->attrs[j];
if (!attr->mask)
break;
ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
j, attr->offset,
attr->offset_type ? "IP End" :
"Payload Start",
be32_to_cpu(attr->mask),
be32_to_cpu(attr->val),
le16_to_cpu(attr->reserved1));
}
}
out:
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return ret;
}
static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
int pos, next_pos;
struct iwl_fw_bcast_filter filter = {};
struct iwl_bcast_filter_cmd cmd;
u32 filter_id, attr_id, mask, value;
int err = 0;
if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
&filter.frame_type, &pos) != 3)
return -EINVAL;
if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
return -EINVAL;
for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
attr_id++) {
struct iwl_fw_bcast_filter_attr *attr =
&filter.attrs[attr_id];
if (pos >= count)
break;
if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
&attr->offset, &attr->offset_type,
&mask, &value, &next_pos) != 4)
return -EINVAL;
attr->mask = cpu_to_be32(mask);
attr->val = cpu_to_be32(value);
if (mask)
filter.num_attrs++;
pos += next_pos;
}
mutex_lock(&mvm->mutex);
memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
&filter, sizeof(filter));
/* send updated bcast filtering configuration */
if (mvm->dbgfs_bcast_filtering.override &&
iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
mutex_unlock(&mvm->mutex);
return err ?: count;
}
static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
struct iwl_bcast_filter_cmd cmd;
char *buf;
int bufsz = 1024;
int i, pos = 0;
ssize_t ret;
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&mvm->mutex);
if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
ADD_TEXT("None\n");
mutex_unlock(&mvm->mutex);
goto out;
}
mutex_unlock(&mvm->mutex);
for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
i, mac->default_discard, mac->attached_filters);
}
out:
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return ret;
}
static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
char *buf, size_t count,
loff_t *ppos)
{
struct iwl_bcast_filter_cmd cmd;
struct iwl_fw_bcast_mac mac = {};
u32 mac_id, attached_filters;
int err = 0;
if (!mvm->bcast_filters)
return -ENOENT;
if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
&attached_filters) != 3)
return -EINVAL;
if (mac_id >= ARRAY_SIZE(cmd.macs) ||
mac.default_discard > 1 ||
attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
return -EINVAL;
mac.attached_filters = cpu_to_le16(attached_filters);
mutex_lock(&mvm->mutex);
memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
&mac, sizeof(mac));
/* send updated bcast filtering configuration */
if (mvm->dbgfs_bcast_filtering.override &&
iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
mutex_unlock(&mvm->mutex);
return err ?: count;
}
#endif
#ifdef CONFIG_PM_SLEEP
static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
@ -658,15 +838,74 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
}
#endif
#define PRINT_MVM_REF(ref) do { \
if (test_bit(ref, mvm->ref_bitmap)) \
pos += scnprintf(buf + pos, bufsz - pos, \
"\t(0x%lx) %s\n", \
BIT(ref), #ref); \
} while (0)
static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
int pos = 0;
char buf[256];
const size_t bufsz = sizeof(buf);
pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
mvm->ref_bitmap[0]);
PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
PRINT_MVM_REF(IWL_MVM_REF_SCAN);
PRINT_MVM_REF(IWL_MVM_REF_ROC);
PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
PRINT_MVM_REF(IWL_MVM_REF_USER);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
unsigned long value;
int ret;
bool taken;
ret = kstrtoul(buf, 10, &value);
if (ret < 0)
return ret;
mutex_lock(&mvm->mutex);
taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap);
if (value == 1 && !taken)
iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
else if (value == 0 && taken)
iwl_mvm_unref(mvm, IWL_MVM_REF_USER);
else
ret = -EINVAL;
mutex_unlock(&mvm->mutex);
if (ret < 0)
return ret;
return count;
}
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \
if (!debugfs_create_file(#name, mode, parent, mvm, \
#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
if (!debugfs_create_file(alias, mode, parent, mvm, \
&iwl_dbgfs_##name##_ops)) \
goto err; \
} while (0)
#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
/* Device wide debugfs entries */
MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
@ -680,6 +919,12 @@ MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
#endif
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
@ -687,6 +932,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
{
struct dentry *bcast_dir __maybe_unused;
char buf[100];
mvm->debugfs_dir = dbgfs_dir;
@ -705,6 +951,27 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
bcast_dir = debugfs_create_dir("bcast_filtering",
mvm->debugfs_dir);
if (!bcast_dir)
goto err;
if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
bcast_dir,
&mvm->dbgfs_bcast_filtering.override))
goto err;
MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
bcast_dir, S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
bcast_dir, S_IWUSR | S_IRUSR);
}
#endif
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);

View file

@ -70,37 +70,24 @@
/**
* enum iwl_bt_coex_flags - flags for BT_COEX command
* @BT_CH_PRIMARY_EN:
* @BT_CH_SECONDARY_EN:
* @BT_NOTIF_COEX_OFF:
* @BT_COEX_MODE_POS:
* @BT_COEX_MODE_MSK:
* @BT_COEX_DISABLE:
* @BT_COEX_2W:
* @BT_COEX_3W:
* @BT_COEX_NW:
* @BT_USE_DEFAULTS:
* @BT_SYNC_2_BT_DISABLE:
* @BT_COEX_CORUNNING_TBL_EN:
* @BT_COEX_SYNC2SCO:
*
* The COEX_MODE must be set for each command. Even if it is not changed.
*/
enum iwl_bt_coex_flags {
BT_CH_PRIMARY_EN = BIT(0),
BT_CH_SECONDARY_EN = BIT(1),
BT_NOTIF_COEX_OFF = BIT(2),
BT_COEX_MODE_POS = 3,
BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
BT_USE_DEFAULTS = BIT(6),
BT_SYNC_2_BT_DISABLE = BIT(7),
BT_COEX_CORUNNING_TBL_EN = BIT(8),
BT_COEX_MPLUT_TBL_EN = BIT(9),
/* Bit 10 is reserved */
BT_COEX_WF_PRIO_BOOST_CHECK_EN = BIT(11),
BT_COEX_SYNC2SCO = BIT(7),
};
/*

View file

@ -231,8 +231,12 @@ enum iwl_wowlan_wakeup_filters {
IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8),
IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9),
IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10),
/* BIT(11) reserved */
IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11),
IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12),
IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13),
IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14),
IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15),
IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
struct iwl_wowlan_config_cmd {

View file

@ -301,54 +301,65 @@ struct iwl_beacon_filter_cmd {
/* Beacon filtering and beacon abort */
#define IWL_BF_ENERGY_DELTA_DEFAULT 5
#define IWL_BF_ENERGY_DELTA_D0I3 20
#define IWL_BF_ENERGY_DELTA_MAX 255
#define IWL_BF_ENERGY_DELTA_MIN 0
#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20
#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
#define IWL_BF_ROAMING_STATE_DEFAULT 72
#define IWL_BF_ROAMING_STATE_D0I3 72
#define IWL_BF_ROAMING_STATE_MAX 255
#define IWL_BF_ROAMING_STATE_MIN 0
#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
#define IWL_BF_TEMP_THRESHOLD_D0I3 112
#define IWL_BF_TEMP_THRESHOLD_MAX 255
#define IWL_BF_TEMP_THRESHOLD_MIN 0
#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
#define IWL_BF_TEMP_FAST_FILTER_D0I3 1
#define IWL_BF_TEMP_FAST_FILTER_MAX 255
#define IWL_BF_TEMP_FAST_FILTER_MIN 0
#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
#define IWL_BF_TEMP_SLOW_FILTER_D0I3 5
#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
#define IWL_BF_DEBUG_FLAG_DEFAULT 0
#define IWL_BF_DEBUG_FLAG_D0I3 0
#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
#define IWL_BF_ESCAPE_TIMER_D0I3 1024
#define IWL_BF_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0
#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
#define IWL_BA_ESCAPE_TIMER_D0I3 6
#define IWL_BA_ESCAPE_TIMER_D3 9
#define IWL_BA_ESCAPE_TIMER_MAX 1024
#define IWL_BA_ESCAPE_TIMER_MIN 0
#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
#define IWL_BF_CMD_CONFIG_DEFAULTS \
.bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT), \
.bf_roaming_energy_delta = \
cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT), \
.bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT), \
.bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT), \
.bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \
.bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \
.bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT), \
.bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \
.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
#define IWL_BF_CMD_CONFIG(mode) \
.bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \
.bf_roaming_energy_delta = \
cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \
.bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \
.bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \
.bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \
.bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \
.bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \
.bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \
.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode)
#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT)
#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3)
#endif

View file

@ -199,11 +199,14 @@ enum iwl_sta_modify_flag {
* @STA_SLEEP_STATE_AWAKE:
* @STA_SLEEP_STATE_PS_POLL:
* @STA_SLEEP_STATE_UAPSD:
* @STA_SLEEP_STATE_MOREDATA: set more-data bit on
* (last) released frame
*/
enum iwl_sta_sleep_flag {
STA_SLEEP_STATE_AWAKE = 0,
STA_SLEEP_STATE_PS_POLL = BIT(0),
STA_SLEEP_STATE_UAPSD = BIT(1),
STA_SLEEP_STATE_AWAKE = 0,
STA_SLEEP_STATE_PS_POLL = BIT(0),
STA_SLEEP_STATE_UAPSD = BIT(1),
STA_SLEEP_STATE_MOREDATA = BIT(2),
};
/* STA ID and color bits definitions */
@ -318,13 +321,15 @@ struct iwl_mvm_add_sta_cmd_v5 {
} __packed; /* ADD_STA_CMD_API_S_VER_5 */
/**
* struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station
* VER_6 of this command is quite similar to VER_5 except
* struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station
* VER_7 of this command is quite similar to VER_5 except
* exclusion of all fields related to the security key installation.
* It only differs from VER_6 by the "awake_acs" field that is
* reserved and ignored in VER_6.
*/
struct iwl_mvm_add_sta_cmd_v6 {
struct iwl_mvm_add_sta_cmd_v7 {
u8 add_modify;
u8 reserved1;
u8 awake_acs;
__le16 tid_disable_tx;
__le32 mac_id_n_color;
u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */
@ -342,7 +347,7 @@ struct iwl_mvm_add_sta_cmd_v6 {
__le16 assoc_id;
__le16 beamform_flags;
__le32 tfd_queue_msk;
} __packed; /* ADD_STA_CMD_API_S_VER_6 */
} __packed; /* ADD_STA_CMD_API_S_VER_7 */
/**
* struct iwl_mvm_add_sta_key_cmd - add/modify sta key
@ -432,5 +437,15 @@ struct iwl_mvm_wep_key_cmd {
struct iwl_mvm_wep_key wep_key[0];
} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
/**
* struct iwl_mvm_eosp_notification - EOSP notification from firmware
* @remain_frame_count: # of frames remaining, non-zero if SP was cut
* short by GO absence
* @sta_id: station ID
*/
struct iwl_mvm_eosp_notification {
__le32 remain_frame_count;
__le32 sta_id;
} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */
#endif /* __fw_api_sta_h__ */

View file

@ -163,6 +163,7 @@ enum {
TX_ANT_CONFIGURATION_CMD = 0x98,
BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f,
/* RF-KILL commands and notifications */
@ -190,6 +191,7 @@ enum {
REPLY_DEBUG_CMD = 0xf0,
DEBUG_LOG_MSG = 0xf7,
BCAST_FILTER_CMD = 0xcf,
MCAST_FILTER_CMD = 0xd0,
/* D3 commands/notifications */
@ -197,6 +199,7 @@ enum {
PROT_OFFLOAD_CONFIG_CMD = 0xd4,
OFFLOADS_QUERY_CMD = 0xd5,
REMOTE_WAKE_CONFIG_CMD = 0xd6,
D0I3_END_CMD = 0xed,
/* for WoWLAN in particular */
WOWLAN_PATTERNS = 0xe0,
@ -303,6 +306,7 @@ struct iwl_phy_cfg_cmd {
#define PHY_CFG_RX_CHAIN_B BIT(13)
#define PHY_CFG_RX_CHAIN_C BIT(14)
#define NVM_MAX_NUM_SECTIONS 11
/* Target of the NVM_ACCESS_CMD */
enum {
@ -313,14 +317,9 @@ enum {
/* Section types for NVM_ACCESS_CMD */
enum {
NVM_SECTION_TYPE_HW = 0,
NVM_SECTION_TYPE_SW,
NVM_SECTION_TYPE_PAPD,
NVM_SECTION_TYPE_BT,
NVM_SECTION_TYPE_CALIBRATION,
NVM_SECTION_TYPE_PRODUCTION,
NVM_SECTION_TYPE_POST_FCS_CALIB,
NVM_NUM_OF_SECTIONS,
NVM_SECTION_TYPE_SW = 1,
NVM_SECTION_TYPE_CALIBRATION = 4,
NVM_SECTION_TYPE_PRODUCTION = 5,
};
/**
@ -412,6 +411,35 @@ struct mvm_alive_resp {
__le32 scd_base_ptr; /* SRAM address for SCD */
} __packed; /* ALIVE_RES_API_S_VER_1 */
struct mvm_alive_resp_ver2 {
__le16 status;
__le16 flags;
u8 ucode_minor;
u8 ucode_major;
__le16 id;
u8 api_minor;
u8 api_major;
u8 ver_subtype;
u8 ver_type;
u8 mac;
u8 opt;
__le16 reserved2;
__le32 timestamp;
__le32 error_event_table_ptr; /* SRAM address for error log */
__le32 log_event_table_ptr; /* SRAM address for LMAC event log */
__le32 cpu_register_ptr;
__le32 dbgm_config_ptr;
__le32 alive_counter_ptr;
__le32 scd_base_ptr; /* SRAM address for SCD */
__le32 st_fwrd_addr; /* pointer to Store and forward */
__le32 st_fwrd_size;
u8 umac_minor; /* UMAC version: minor */
u8 umac_major; /* UMAC version: major */
__le16 umac_id; /* UMAC version: id */
__le32 error_info_addr; /* SRAM address for UMAC error log */
__le32 dbg_print_buff_addr;
} __packed; /* ALIVE_RES_API_S_VER_2 */
/* Error response/notification */
enum {
FW_ERR_UNKNOWN_CMD = 0x0,
@ -1159,6 +1187,90 @@ struct iwl_mcast_filter_cmd {
u8 addr_list[0];
} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
#define MAX_BCAST_FILTERS 8
#define MAX_BCAST_FILTER_ATTRS 2
/**
* enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet
* @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start.
* @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e.
* start of ip payload).
*/
enum iwl_mvm_bcast_filter_attr_offset {
BCAST_FILTER_OFFSET_PAYLOAD_START = 0,
BCAST_FILTER_OFFSET_IP_END = 1,
};
/**
* struct iwl_fw_bcast_filter_attr - broadcast filter attribute
* @offset_type: &enum iwl_mvm_bcast_filter_attr_offset.
* @offset: starting offset of this pattern.
* @val: value to match - big endian (MSB is the first
* byte to match from offset pos).
* @mask: mask to match (big endian).
*/
struct iwl_fw_bcast_filter_attr {
u8 offset_type;
u8 offset;
__le16 reserved1;
__be32 val;
__be32 mask;
} __packed; /* BCAST_FILTER_ATT_S_VER_1 */
/**
* enum iwl_mvm_bcast_filter_frame_type - filter frame type
* @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames.
* @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames
*/
enum iwl_mvm_bcast_filter_frame_type {
BCAST_FILTER_FRAME_TYPE_ALL = 0,
BCAST_FILTER_FRAME_TYPE_IPV4 = 1,
};
/**
* struct iwl_fw_bcast_filter - broadcast filter
* @discard: discard frame (1) or let it pass (0).
* @frame_type: &enum iwl_mvm_bcast_filter_frame_type.
* @num_attrs: number of valid attributes in this filter.
* @attrs: attributes of this filter. a filter is considered matched
* only when all its attributes are matched (i.e. AND relationship)
*/
struct iwl_fw_bcast_filter {
u8 discard;
u8 frame_type;
u8 num_attrs;
u8 reserved1;
struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
} __packed; /* BCAST_FILTER_S_VER_1 */
/**
* struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
* @default_discard: default action for this mac (discard (1) / pass (0)).
* @attached_filters: bitmap of relevant filters for this mac.
*/
struct iwl_fw_bcast_mac {
u8 default_discard;
u8 reserved1;
__le16 attached_filters;
} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */
/**
* struct iwl_bcast_filter_cmd - broadcast filtering configuration
* @disable: enable (0) / disable (1)
* @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS)
* @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER)
* @filters: broadcast filters
* @macs: broadcast filtering configuration per-mac
*/
struct iwl_bcast_filter_cmd {
u8 disable;
u8 max_bcast_filters;
u8 max_macs;
u8 reserved1;
struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS];
struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER];
} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */
struct mvm_statistics_dbg {
__le32 burst_check;
__le32 burst_count;

View file

@ -110,18 +110,46 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_mvm_alive_data *alive_data = data;
struct mvm_alive_resp *palive;
struct mvm_alive_resp_ver2 *palive2;
palive = (void *)pkt->data;
if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
palive = (void *)pkt->data;
mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
mvm->support_umac_log = false;
mvm->error_event_table =
le32_to_cpu(palive->error_event_table_ptr);
mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm,
"Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type,
palive->ver_subtype, palive->flags);
alive_data->valid = le16_to_cpu(palive->status) ==
IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm,
"Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type,
palive->ver_subtype, palive->flags);
} else {
palive2 = (void *)pkt->data;
mvm->support_umac_log = true;
mvm->error_event_table =
le32_to_cpu(palive2->error_event_table_ptr);
mvm->log_event_table =
le32_to_cpu(palive2->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
mvm->umac_error_event_table =
le32_to_cpu(palive2->error_info_addr);
alive_data->valid = le16_to_cpu(palive2->status) ==
IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm,
"Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive2->status), palive2->ver_type,
palive2->ver_subtype, palive2->flags);
IWL_DEBUG_FW(mvm,
"UMAC version: Major - 0x%x, Minor - 0x%x\n",
palive2->umac_major, palive2->umac_minor);
}
return true;
}
@ -439,10 +467,23 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
goto error;
}
ret = iwl_mvm_power_update_device_mode(mvm);
/* Initialize tx backoffs to the minimal possible */
iwl_mvm_tt_tx_backoff(mvm, 0);
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
ret = iwl_power_legacy_set_cam_mode(mvm);
if (ret)
goto error;
}
ret = iwl_mvm_power_update_device(mvm);
if (ret)
goto error;
/* allow FW/transport low power modes if not during restart */
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:

View file

@ -90,6 +90,7 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
{
struct iwl_mvm_mac_iface_iterator_data *data = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 min_bi;
/* Skip the interface for which we are trying to assign a tsf_id */
if (vif == data->vif)
@ -114,42 +115,57 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
switch (data->vif->type) {
case NL80211_IFTYPE_STATION:
/*
* The new interface is client, so if the existing one
* we're iterating is an AP, and both interfaces have the
* same beacon interval, the same TSF should be used to
* avoid drift between the new client and existing AP,
* the existing AP will get drift updates from the new
* client context in this case
* The new interface is a client, so if the one we're iterating
* is an AP, and the beacon interval of the AP is a multiple or
* divisor of the beacon interval of the client, the same TSF
* should be used to avoid drift between the new client and
* existing AP. The existing AP will get drift updates from the
* new client context in this case.
*/
if (vif->type == NL80211_IFTYPE_AP) {
if (data->preferred_tsf == NUM_TSF_IDS &&
test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
(vif->bss_conf.beacon_int ==
data->vif->bss_conf.beacon_int)) {
data->preferred_tsf = mvmvif->tsf_id;
return;
}
if (vif->type != NL80211_IFTYPE_AP ||
data->preferred_tsf != NUM_TSF_IDS ||
!test_bit(mvmvif->tsf_id, data->available_tsf_ids))
break;
min_bi = min(data->vif->bss_conf.beacon_int,
vif->bss_conf.beacon_int);
if (!min_bi)
break;
if ((data->vif->bss_conf.beacon_int -
vif->bss_conf.beacon_int) % min_bi == 0) {
data->preferred_tsf = mvmvif->tsf_id;
return;
}
break;
case NL80211_IFTYPE_AP:
/*
* The new interface is AP/GO, so in case both interfaces
* have the same beacon interval, it should get drift
* updates from an existing client or use the same
* TSF as an existing GO. There's no drift between
* TSFs internally but if they used different TSFs
* then a new client MAC could update one of them
* and cause drift that way.
* The new interface is AP/GO, so if its beacon interval is a
* multiple or a divisor of the beacon interval of an existing
* interface, it should get drift updates from an existing
* client or use the same TSF as an existing GO. There's no
* drift between TSFs internally but if they used different
* TSFs then a new client MAC could update one of them and
* cause drift that way.
*/
if (vif->type == NL80211_IFTYPE_STATION ||
vif->type == NL80211_IFTYPE_AP) {
if (data->preferred_tsf == NUM_TSF_IDS &&
test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
(vif->bss_conf.beacon_int ==
data->vif->bss_conf.beacon_int)) {
data->preferred_tsf = mvmvif->tsf_id;
return;
}
if ((vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_STATION) ||
data->preferred_tsf != NUM_TSF_IDS ||
!test_bit(mvmvif->tsf_id, data->available_tsf_ids))
break;
min_bi = min(data->vif->bss_conf.beacon_int,
vif->bss_conf.beacon_int);
if (!min_bi)
break;
if ((data->vif->bss_conf.beacon_int -
vif->bss_conf.beacon_int) % min_bi == 0) {
data->preferred_tsf = mvmvif->tsf_id;
return;
}
break;
default:

View file

@ -66,6 +66,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <net/mac80211.h>
#include <net/tcp.h>
@ -128,6 +129,117 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
};
#endif
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
/*
* Use the reserved field to indicate magic values.
* these values will only be used internally by the driver,
* and won't make it to the fw (reserved will be 0).
* BC_FILTER_MAGIC_IP - configure the val of this attribute to
* be the vif's ip address. in case there is not a single
* ip address (0, or more than 1), this attribute will
* be skipped.
* BC_FILTER_MAGIC_MAC - set the val of this attribute to
* the LSB bytes of the vif's mac address
*/
enum {
BC_FILTER_MAGIC_NONE = 0,
BC_FILTER_MAGIC_IP,
BC_FILTER_MAGIC_MAC,
};
static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
{
/* arp */
.discard = 0,
.frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
.attrs = {
{
/* frame type - arp, hw type - ethernet */
.offset_type =
BCAST_FILTER_OFFSET_PAYLOAD_START,
.offset = sizeof(rfc1042_header),
.val = cpu_to_be32(0x08060001),
.mask = cpu_to_be32(0xffffffff),
},
{
/* arp dest ip */
.offset_type =
BCAST_FILTER_OFFSET_PAYLOAD_START,
.offset = sizeof(rfc1042_header) + 2 +
sizeof(struct arphdr) +
ETH_ALEN + sizeof(__be32) +
ETH_ALEN,
.mask = cpu_to_be32(0xffffffff),
/* mark it as special field */
.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
},
},
},
{
/* dhcp offer bcast */
.discard = 0,
.frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
.attrs = {
{
/* udp dest port - 68 (bootp client)*/
.offset_type = BCAST_FILTER_OFFSET_IP_END,
.offset = offsetof(struct udphdr, dest),
.val = cpu_to_be32(0x00440000),
.mask = cpu_to_be32(0xffff0000),
},
{
/* dhcp - lsb bytes of client hw address */
.offset_type = BCAST_FILTER_OFFSET_IP_END,
.offset = 38,
.mask = cpu_to_be32(0xffffffff),
/* mark it as special field */
.reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
},
},
},
/* last filter must be empty */
{},
};
#endif
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
if (!mvm->trans->cfg->d0i3)
return;
IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
iwl_trans_ref(mvm->trans);
}
void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
if (!mvm->trans->cfg->d0i3)
return;
IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
iwl_trans_unref(mvm->trans);
}
static void
iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
{
int i;
if (!mvm->trans->cfg->d0i3)
return;
for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
if (ref == i)
continue;
IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
clear_bit(i, mvm->ref_bitmap);
iwl_trans_unref(mvm->trans);
}
}
static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
{
int i;
@ -203,6 +315,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(iwl_mvm_iface_combinations);
@ -289,6 +404,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
}
#endif
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
/* assign default bcast filtering configuration */
mvm->bcast_filters = iwl_mvm_default_bcast_filters;
#endif
ret = iwl_mvm_leds_init(mvm);
if (ret)
return ret;
@ -305,6 +425,9 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_sta *sta = control->sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
if (iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
@ -315,8 +438,16 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
!test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
goto drop;
if (control->sta) {
if (iwl_mvm_tx_skb(mvm, skb, control->sta))
/* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
ieee80211_is_mgmt(hdr->frame_control) &&
!ieee80211_is_deauth(hdr->frame_control) &&
!ieee80211_is_disassoc(hdr->frame_control) &&
!ieee80211_is_action(hdr->frame_control)))
sta = NULL;
if (sta) {
if (iwl_mvm_tx_skb(mvm, skb, sta))
goto drop;
return;
}
@ -416,6 +547,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
iwl_mvm_cleanup_iterator, mvm);
mvm->p2p_device_vif = NULL;
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
iwl_mvm_reset_phy_ctxts(mvm);
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
@ -423,6 +555,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
ieee80211_wake_queues(mvm->hw);
/* cleanup all stale references (scan, roc), but keep the
* ucode_down ref until reconfig is complete */
iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
mvm->vif_count = 0;
mvm->rx_ba_sessions = 0;
}
@ -457,6 +593,9 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
ret);
/* allow transport/FW low power modes */
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
mutex_unlock(&mvm->mutex);
}
@ -464,9 +603,14 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
flush_work(&mvm->d0i3_exit_work);
flush_work(&mvm->async_handlers_wk);
mutex_lock(&mvm->mutex);
/* disallow low power states when the FW is down */
iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
/* async_handlers_wk is now blocked */
/*
@ -492,14 +636,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
cancel_work_sync(&mvm->async_handlers_wk);
}
static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = data;
iwl_mvm_power_update_mode(mvm, vif);
}
static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
{
u16 i;
@ -567,7 +703,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
vif->type == NL80211_IFTYPE_ADHOC) {
u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
qmask);
qmask,
ieee80211_vif_type_p2p(vif));
if (ret) {
IWL_ERR(mvm, "Failed to allocate bcast sta\n");
goto out_release;
@ -581,10 +718,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
if (ret)
goto out_release;
iwl_mvm_power_disable(mvm, vif);
ret = iwl_mvm_power_update_mac(mvm, vif);
if (ret)
goto out_release;
/* beacon filtering */
ret = iwl_mvm_disable_beacon_filter(mvm, vif);
ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
if (ret)
goto out_remove_mac;
@ -643,11 +782,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
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);
@ -736,11 +870,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
if (mvm->vif_count && 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_power_update_mac(mvm, vif);
iwl_mvm_mac_ctxt_remove(mvm, vif);
out_release:
@ -858,6 +988,156 @@ out:
*total_flags = 0;
}
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
struct iwl_bcast_iter_data {
struct iwl_mvm *mvm;
struct iwl_bcast_filter_cmd *cmd;
u8 current_filter;
};
static void
iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
const struct iwl_fw_bcast_filter *in_filter,
struct iwl_fw_bcast_filter *out_filter)
{
struct iwl_fw_bcast_filter_attr *attr;
int i;
memcpy(out_filter, in_filter, sizeof(*out_filter));
for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
attr = &out_filter->attrs[i];
if (!attr->mask)
break;
switch (attr->reserved1) {
case cpu_to_le16(BC_FILTER_MAGIC_IP):
if (vif->bss_conf.arp_addr_cnt != 1) {
attr->mask = 0;
continue;
}
attr->val = vif->bss_conf.arp_addr_list[0];
break;
case cpu_to_le16(BC_FILTER_MAGIC_MAC):
attr->val = *(__be32 *)&vif->addr[2];
break;
default:
break;
}
attr->reserved1 = 0;
out_filter->num_attrs++;
}
}
static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_bcast_iter_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
struct iwl_bcast_filter_cmd *cmd = data->cmd;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_fw_bcast_mac *bcast_mac;
int i;
if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
return;
bcast_mac = &cmd->macs[mvmvif->id];
/* enable filtering only for associated stations */
if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
return;
bcast_mac->default_discard = 1;
/* copy all configured filters */
for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
/*
* Make sure we don't exceed our filters limit.
* if there is still a valid filter to be configured,
* be on the safe side and just allow bcast for this mac.
*/
if (WARN_ON_ONCE(data->current_filter >=
ARRAY_SIZE(cmd->filters))) {
bcast_mac->default_discard = 0;
bcast_mac->attached_filters = 0;
break;
}
iwl_mvm_set_bcast_filter(vif,
&mvm->bcast_filters[i],
&cmd->filters[data->current_filter]);
/* skip current filter if it contains no attributes */
if (!cmd->filters[data->current_filter].num_attrs)
continue;
/* attach the filter to current mac */
bcast_mac->attached_filters |=
cpu_to_le16(BIT(data->current_filter));
data->current_filter++;
}
}
bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
struct iwl_bcast_filter_cmd *cmd)
{
struct iwl_bcast_iter_data iter_data = {
.mvm = mvm,
.cmd = cmd,
};
memset(cmd, 0, sizeof(*cmd));
cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
cmd->max_macs = ARRAY_SIZE(cmd->macs);
#ifdef CONFIG_IWLWIFI_DEBUGFS
/* use debugfs filters/macs if override is configured */
if (mvm->dbgfs_bcast_filtering.override) {
memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
sizeof(cmd->filters));
memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
sizeof(cmd->macs));
return true;
}
#endif
/* if no filters are configured, do nothing */
if (!mvm->bcast_filters)
return false;
/* configure and attach these filters for each associated sta vif */
ieee80211_iterate_active_interfaces(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_bcast_filter_iterator, &iter_data);
return true;
}
static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
struct iwl_bcast_filter_cmd cmd;
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
return 0;
if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
return 0;
return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
}
#else
static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
return 0;
}
#endif
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@ -910,6 +1190,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
iwl_mvm_sf_update(mvm, vif, false);
iwl_mvm_power_vif_assoc(mvm, vif);
if (vif->p2p)
iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
/*
* If update fails - SF might be running in associated
@ -922,27 +1204,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
if (ret)
IWL_ERR(mvm, "failed to remove AP station\n");
if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
/* remove quota for this interface */
ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
IWL_ERR(mvm, "failed to update quotas\n");
if (vif->p2p)
iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
}
iwl_mvm_recalc_multicast(mvm);
iwl_mvm_configure_bcast_filter(mvm, vif);
/* reset rssi values */
mvmvif->bf_data.ave_beacon_signal = 0;
if (!(mvm->fw->ucode_capa.flags &
IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
/* Workaround for FW bug, otherwise FW disables device
* power save upon disassociation
*/
ret = iwl_mvm_power_update_mode(mvm, vif);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
}
iwl_mvm_bt_coex_vif_change(mvm);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
IEEE80211_SMPS_AUTOMATIC);
@ -955,7 +1235,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
&mvmvif->time_event_data);
} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
BSS_CHANGED_QOS)) {
ret = iwl_mvm_power_update_mode(mvm, vif);
ret = iwl_mvm_power_update_mac(mvm, vif);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
}
@ -969,10 +1249,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
/* reset cqm events tracking */
mvmvif->bf_data.last_cqm_event = 0;
ret = iwl_mvm_update_beacon_filter(mvm, vif);
ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
if (ret)
IWL_ERR(mvm, "failed to update CQM thresholds\n");
}
if (changes & BSS_CHANGED_ARP_FILTER) {
IWL_DEBUG_MAC80211(mvm, "arp filter changed");
iwl_mvm_configure_bcast_filter(mvm, vif);
}
}
static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
@ -1006,8 +1291,6 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (ret)
goto out_remove;
mvmvif->ap_ibss_active = true;
/* Send the bcast station. At this stage the TBTT and DTIM time events
* are added and applied to the scheduler */
ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
@ -1019,7 +1302,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
/* power updated needs to be done before quotas */
mvm->bound_vif_cnt++;
iwl_mvm_power_update_binding(mvm, vif, true);
iwl_mvm_power_update_mac(mvm, vif);
ret = iwl_mvm_update_quotas(mvm, vif);
if (ret)
@ -1029,6 +1312,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
if (vif->p2p && mvm->p2p_device_vif)
iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
iwl_mvm_bt_coex_vif_change(mvm);
mutex_unlock(&mvm->mutex);
@ -1036,7 +1321,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
out_quota_failed:
mvm->bound_vif_cnt--;
iwl_mvm_power_update_binding(mvm, vif, false);
iwl_mvm_power_update_mac(mvm, vif);
mvmvif->ap_ibss_active = false;
iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
out_unbind:
@ -1062,6 +1347,8 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
iwl_mvm_bt_coex_vif_change(mvm);
iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
if (vif->p2p && mvm->p2p_device_vif)
iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
@ -1071,7 +1358,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
iwl_mvm_binding_remove_vif(mvm, vif);
mvm->bound_vif_cnt--;
iwl_mvm_power_update_binding(mvm, vif, false);
iwl_mvm_power_update_mac(mvm, vif);
iwl_mvm_mac_ctxt_remove(mvm, vif);
@ -1085,26 +1372,20 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
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)
IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
}
if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
BSS_CHANGED_BANDWIDTH) &&
iwl_mvm_mac_ctxt_changed(mvm, vif))
IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
/* Need to send a new beacon template to the FW */
if (changes & BSS_CHANGED_BEACON) {
if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
IWL_WARN(mvm, "Failed updating beacon data\n");
}
if (changes & BSS_CHANGED_BEACON &&
iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
IWL_WARN(mvm, "Failed updating beacon data\n");
}
static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@ -1137,6 +1418,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
struct cfg80211_scan_request *req)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_notification_wait wait_scan_done;
static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
int ret;
if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
@ -1144,11 +1427,38 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
if (mvm->scan_status == IWL_MVM_SCAN_NONE)
ret = iwl_mvm_scan_request(mvm, vif, req);
else
switch (mvm->scan_status) {
case IWL_MVM_SCAN_SCHED:
iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
scan_done_notif,
ARRAY_SIZE(scan_done_notif),
NULL, NULL);
iwl_mvm_sched_scan_stop(mvm);
ret = iwl_wait_notification(&mvm->notif_wait,
&wait_scan_done, HZ);
if (ret) {
ret = -EBUSY;
goto out;
}
/* iwl_mvm_rx_scan_offload_complete_notif() will be called
* soon but will not reset the scan status as it won't be
* IWL_MVM_SCAN_SCHED any more since we queue the next scan
* immediately (below)
*/
break;
case IWL_MVM_SCAN_NONE:
break;
default:
ret = -EBUSY;
goto out;
}
iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
ret = iwl_mvm_scan_request(mvm, vif, req);
if (ret)
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
out:
mutex_unlock(&mvm->mutex);
return ret;
@ -1168,20 +1478,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
static void
iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tid,
struct ieee80211_sta *sta, u16 tids,
int num_frames,
enum ieee80211_frame_release_type reason,
bool more_data)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
/* TODO: how do we tell the fw to send frames for a specific TID */
/* Called when we need to transmit (a) frame(s) from mac80211 */
/*
* The fw will send EOSP notification when the last frame will be
* transmitted.
*/
iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames);
iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
tids, more_data, false);
}
static void
iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, u16 tids,
int num_frames,
enum ieee80211_frame_release_type reason,
bool more_data)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
/* Called when we need to transmit (a) frame(s) from agg queue */
iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
tids, more_data, true);
}
static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
@ -1191,11 +1513,25 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
int tid;
switch (cmd) {
case STA_NOTIFY_SLEEP:
if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
ieee80211_sta_block_awake(hw, sta, true);
spin_lock_bh(&mvmsta->lock);
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
struct iwl_mvm_tid_data *tid_data;
tid_data = &mvmsta->tid_data[tid];
if (tid_data->state != IWL_AGG_ON &&
tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
continue;
if (iwl_mvm_tid_queued(tid_data) == 0)
continue;
ieee80211_sta_set_buffered(sta, tid, true);
}
spin_unlock_bh(&mvmsta->lock);
/*
* The fw updates the STA to be asleep. Tx packets on the Tx
* queues to this station will not be transmitted. The fw will
@ -1286,12 +1622,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
/* enable beacon filtering */
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
ret = 0;
} else if (old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
/* disable beacon filtering */
WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC));
ret = 0;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
@ -1756,7 +2092,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
* otherwise fw will complain.
*/
mvm->bound_vif_cnt++;
iwl_mvm_power_update_binding(mvm, vif, true);
iwl_mvm_power_update_mac(mvm, vif);
/* Setting the quota at this stage is only required for monitor
* interfaces. For the other types, the bss_info changed flow
@ -1774,7 +2110,7 @@ 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);
iwl_mvm_power_update_mac(mvm, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
if (ret)
@ -1807,7 +2143,7 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
iwl_mvm_binding_remove_vif(mvm, vif);
mvm->bound_vif_cnt--;
iwl_mvm_power_update_binding(mvm, vif, false);
iwl_mvm_power_update_mac(mvm, vif);
out_unlock:
mvmvif->phy_ctxt = NULL;
@ -1874,8 +2210,9 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
return -EINVAL;
if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
return iwl_mvm_enable_beacon_filter(mvm, vif);
return iwl_mvm_disable_beacon_filter(mvm, vif);
return iwl_mvm_enable_beacon_filter(mvm, vif,
CMD_SYNC);
return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
}
return -EOPNOTSUPP;
@ -1914,6 +2251,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
.sta_state = iwl_mvm_mac_sta_state,
.sta_notify = iwl_mvm_mac_sta_notify,
.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
.release_buffered_frames = iwl_mvm_mac_release_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,

View file

@ -92,7 +92,6 @@ enum iwl_mvm_tx_fifo {
};
extern struct ieee80211_ops iwl_mvm_hw_ops;
extern const struct iwl_mvm_power_ops pm_legacy_ops;
extern const struct iwl_mvm_power_ops pm_mac_ops;
/**
@ -159,20 +158,6 @@ enum iwl_power_scheme {
IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2
struct iwl_mvm_power_ops {
int (*power_update_mode)(struct iwl_mvm *mvm,
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);
#endif
};
#ifdef CONFIG_IWLWIFI_DEBUGFS
enum iwl_dbgfs_pm_mask {
MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
@ -239,6 +224,17 @@ enum iwl_mvm_smps_type_request {
NUM_IWL_MVM_SMPS_REQ,
};
enum iwl_mvm_ref_type {
IWL_MVM_REF_UCODE_DOWN,
IWL_MVM_REF_SCAN,
IWL_MVM_REF_ROC,
IWL_MVM_REF_P2P_CLIENT,
IWL_MVM_REF_AP_IBSS,
IWL_MVM_REF_USER,
IWL_MVM_REF_COUNT,
};
/**
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
@ -269,7 +265,9 @@ struct iwl_mvm_vif_bf_data {
* @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
* should get quota etc.
* @monitor_active: indicates that monitor context is configured, and that the
* interface should get quota etc.
* interface should get quota etc.
* @low_latency: indicates that this interface is in low-latency mode
* (VMACLowLatencyMode)
* @queue_params: QoS params for this MAC
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
@ -285,6 +283,7 @@ struct iwl_mvm_vif {
bool uploaded;
bool ap_ibss_active;
bool monitor_active;
bool low_latency;
struct iwl_mvm_vif_bf_data bf_data;
u32 ap_beacon_time;
@ -333,14 +332,13 @@ struct iwl_mvm_vif {
struct dentry *dbgfs_slink;
struct iwl_dbgfs_pm dbgfs_pm;
struct iwl_dbgfs_bf dbgfs_bf;
struct iwl_mac_power_cmd mac_pwr_cmd;
#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 *
@ -415,6 +413,7 @@ struct iwl_tt_params {
* @ct_kill_exit: worker to exit thermal kill
* @dynamic_smps: Is thermal throttling enabled dynamic_smps?
* @tx_backoff: The current thremal throttling tx backoff in uSec.
* @min_backoff: The minimal tx backoff due to power restrictions
* @params: Parameters to configure the thermal throttling algorithm.
* @throttle: Is thermal throttling is active?
*/
@ -422,6 +421,7 @@ struct iwl_mvm_tt_mgmt {
struct delayed_work ct_kill_exit;
bool dynamic_smps;
u32 tx_backoff;
u32 min_backoff;
const struct iwl_tt_params *params;
bool throttle;
};
@ -457,6 +457,8 @@ struct iwl_mvm {
bool init_ucode_complete;
u32 error_event_table;
u32 log_event_table;
u32 umac_error_event_table;
bool support_umac_log;
u32 ampdu_ref;
@ -470,7 +472,7 @@ struct iwl_mvm {
struct iwl_nvm_data *nvm_data;
/* NVM sections */
struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
/* EEPROM MAC addresses */
struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
@ -494,6 +496,17 @@ struct iwl_mvm {
/* rx chain antennas set through debugfs for the scan command */
u8 scan_rx_ant;
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
/* broadcast filters to configure for each associated station */
const struct iwl_fw_bcast_filter *bcast_filters;
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct {
u32 override; /* u32 for debugfs_create_bool */
struct iwl_bcast_filter_cmd cmd;
} dbgfs_bcast_filtering;
#endif
#endif
/* Internal station */
struct iwl_mvm_int_sta aux_sta;
@ -526,6 +539,9 @@ struct iwl_mvm {
*/
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
/* A bitmap of reference types taken by the driver. */
unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
u8 vif_count;
/* -1 for always, 0 for never, >0 for that many times */
@ -548,6 +564,10 @@ struct iwl_mvm {
#endif
#endif
/* d0i3 */
u8 d0i3_ap_sta_id;
struct work_struct d0i3_exit_work;
/* BT-Coex */
u8 bt_kill_msk;
struct iwl_bt_coex_profile_notif last_bt_notif;
@ -557,8 +577,6 @@ struct iwl_mvm {
struct iwl_mvm_tt_mgmt thermal_throttle;
s32 temperature; /* Celsius */
const struct iwl_mvm_power_ops *pm_ops;
#ifdef CONFIG_NL80211_TESTMODE
u32 noa_duration;
struct ieee80211_vif *noa_vif;
@ -572,7 +590,9 @@ struct iwl_mvm {
u8 bound_vif_cnt;
/* Indicate if device power save is allowed */
bool ps_prevented;
bool ps_disabled;
/* Indicate if device power management is allowed */
bool pm_disabled;
};
/* Extract MVM priv from op_mode and _hw */
@ -595,6 +615,24 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
}
static inline struct iwl_mvm_sta *
iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
{
struct ieee80211_sta *sta;
if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
return NULL;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
/* This can happen if the station has been removed right now */
if (IS_ERR_OR_NULL(sta))
return NULL;
return iwl_mvm_sta_from_mac80211(sta);
}
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
@ -661,6 +699,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm);
int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
struct iwl_bcast_filter_cmd *cmd);
/*
* FW notifications / CMD responses handlers
@ -773,48 +813,19 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
/* rate scaling */
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init);
/* power managment */
static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
return mvm->pm_ops->power_update_mode(mvm, vif);
}
/* power management */
int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);
static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
return mvm->pm_ops->power_disable(mvm, vif);
}
static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
{
if (mvm->pm_ops->power_update_device_mode)
return mvm->pm_ops->power_update_device_mode(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);
}
int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
char *buf, int bufsz);
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,
char *buf, int bufsz)
{
return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
}
#endif
int iwl_mvm_leds_init(struct iwl_mvm *mvm);
void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@ -841,6 +852,10 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
}
#endif
/* D0i3 */
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
/* BT Coex */
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
@ -854,6 +869,7 @@ u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
enum iwl_bt_kill_msk {
BT_KILL_MSK_DEFAULT,
@ -875,25 +891,51 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
struct iwl_beacon_filter_cmd *cmd)
{}
#endif
int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool enable, u32 flags);
int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
struct ieee80211_vif *vif,
u32 flags);
int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
struct iwl_beacon_filter_cmd *cmd);
struct ieee80211_vif *vif,
u32 flags);
int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, bool enable);
int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
struct ieee80211_vif *vif,
bool force,
u32 flags);
/* SMPS */
void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum iwl_mvm_smps_type_request req_type,
enum ieee80211_smps_mode smps_request);
/* Low latency */
int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool value);
/* get VMACLowLatencyMode */
static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
{
/*
* should this consider associated/active/... state?
*
* Normally low-latency should only be active on interfaces
* that are active, but at least with debugfs it can also be
* enabled on interfaces that aren't active. However, when
* interface aren't active then they aren't added into the
* binding, so this has no real impact. For now, just return
* the current desired low-latency state.
*/
return mvmvif->low_latency;
}
/* Thermal management and CT-kill */
void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);

View file

@ -67,14 +67,6 @@
#include "iwl-eeprom-read.h"
#include "iwl-nvm-parse.h"
/* list of NVM sections we are allowed/need to read */
static const int nvm_to_read[] = {
NVM_SECTION_TYPE_HW,
NVM_SECTION_TYPE_SW,
NVM_SECTION_TYPE_CALIBRATION,
NVM_SECTION_TYPE_PRODUCTION,
};
/* Default NVM size to read */
#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
#define IWL_MAX_NVM_SECTION_SIZE 7000
@ -240,7 +232,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
/* Checking for required sections */
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
!mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
IWL_ERR(mvm, "Can't parse empty NVM sections\n");
return NULL;
}
@ -248,7 +240,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
if (WARN_ON(!mvm->cfg))
return NULL;
hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
@ -367,7 +359,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
break;
}
if (WARN(section_id >= NVM_NUM_OF_SECTIONS,
if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
"Invalid NVM section ID %d\n", section_id)) {
ret = -EINVAL;
break;
@ -415,6 +407,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
int ret, i, section;
u8 *nvm_buffer, *temp;
if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
return -EINVAL;
/* load external NVM if configured */
if (iwlwifi_mod_params.nvm_file) {
/* move to External NVM flow */
@ -422,6 +417,14 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
if (ret)
return ret;
} else {
/* list of NVM sections we are allowed/need to read */
int nvm_to_read[] = {
mvm->cfg->nvm_hw_section_num,
NVM_SECTION_TYPE_SW,
NVM_SECTION_TYPE_CALIBRATION,
NVM_SECTION_TYPE_PRODUCTION,
};
/* Read From FW NVM */
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
@ -446,10 +449,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
#ifdef CONFIG_IWLWIFI_DEBUGFS
switch (section) {
case NVM_SECTION_TYPE_HW:
mvm->nvm_hw_blob.data = temp;
mvm->nvm_hw_blob.size = ret;
break;
case NVM_SECTION_TYPE_SW:
mvm->nvm_sw_blob.data = temp;
mvm->nvm_sw_blob.size = ret;
@ -463,6 +462,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
mvm->nvm_prod_blob.size = ret;
break;
default:
if (section == mvm->cfg->nvm_hw_section_num) {
mvm->nvm_hw_blob.data = temp;
mvm->nvm_hw_blob.size = ret;
break;
}
WARN(1, "section: %d", section);
}
#endif

View file

@ -185,9 +185,10 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
* (PCIe power is lost before PERST# is asserted), causing ME FW
* to lose ownership and not being able to obtain it back.
*/
iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
}
struct iwl_rx_handlers {
@ -222,10 +223,12 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
iwl_mvm_rx_scan_offload_complete_notif, false),
iwl_mvm_rx_scan_offload_complete_notif, true),
RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
false),
@ -284,9 +287,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_NOTIFICATION),
CMD(EOSP_NOTIFICATION),
CMD(REDUCE_TX_POWER_CMD),
CMD(TX_ANT_CONFIGURATION_CMD),
CMD(D3_CONFIG_CMD),
CMD(D0I3_END_CMD),
CMD(PROT_OFFLOAD_CONFIG_CMD),
CMD(OFFLOADS_QUERY_CMD),
CMD(REMOTE_WAKE_CONFIG_CMD),
@ -309,6 +314,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(BT_PROFILE_NOTIFICATION),
CMD(BT_CONFIG),
CMD(MCAST_FILTER_CMD),
CMD(BCAST_FILTER_CMD),
CMD(REPLY_SF_CFG_CMD),
CMD(REPLY_BEACON_FILTERING_CMD),
CMD(REPLY_THERMAL_MNG_BACKOFF),
@ -320,6 +326,24 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
/* this forward declaration can avoid to export the function */
static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
static void iwl_mvm_d0i3_exit_work(struct work_struct *wk);
static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg)
{
const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs;
if (!pwr_tx_backoff)
return 0;
while (pwr_tx_backoff->pwr) {
if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr)
return pwr_tx_backoff->backoff;
pwr_tx_backoff++;
}
return 0;
}
static struct iwl_op_mode *
iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
@ -333,6 +357,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
TX_CMD,
};
int err, scan_size;
u32 min_backoff;
/*
* We use IWL_MVM_STATION_COUNT to check the validity of the station
* index all over the driver - check that its value corresponds to the
* array size.
*/
BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT);
/********************************
* 1. Allocating and configuring HW data
@ -373,6 +405,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
@ -421,7 +454,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
mvm->cfg->name, mvm->trans->hw_rev);
iwl_mvm_tt_initialize(mvm);
min_backoff = calc_min_backoff(trans, cfg);
iwl_mvm_tt_initialize(mvm, min_backoff);
/*
* If the NVM exists in an external file,
@ -462,13 +496,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (err)
goto out_unregister;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)
mvm->pm_ops = &pm_mac_ops;
else
mvm->pm_ops = &pm_legacy_ops;
memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
/* rpm starts with a taken ref. only set the appropriate bit here. */
set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
return op_mode;
out_unregister:
@ -508,7 +540,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
mvm->phy_db = NULL;
iwl_free_nvm_data(mvm->nvm_data);
for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
ieee80211_free_hw(mvm->hw);
@ -702,6 +734,29 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
{
iwl_abort_notification_waits(&mvm->notif_wait);
/*
* This is a bit racy, but worst case we tell mac80211 about
* a stopped/aborted scan when that was already done which
* is not a problem. It is necessary to abort any os scan
* here because mac80211 requires having the scan cleared
* before restarting.
* We'll reset the scan_status to NONE in restart cleanup in
* the next start() call from mac80211. If restart isn't called
* (no fw restart) scan status will stay busy.
*/
switch (mvm->scan_status) {
case IWL_MVM_SCAN_NONE:
break;
case IWL_MVM_SCAN_OS:
ieee80211_scan_completed(mvm->hw, true);
break;
case IWL_MVM_SCAN_SCHED:
/* Sched scan will be restarted by mac80211 in restart_hw. */
if (!mvm->restart_fw)
ieee80211_sched_scan_stopped(mvm->hw);
break;
}
/*
* If we're restarting already, don't cycle restarts.
* If INIT fw asserted, it will likely fail again.
@ -733,25 +788,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
schedule_work(&reprobe->work);
} else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
/*
* This is a bit racy, but worst case we tell mac80211 about
* a stopped/aborted (sched) scan when that was already done
* which is not a problem. It is necessary to abort any scan
* here because mac80211 requires having the scan cleared
* before restarting.
* We'll reset the scan_status to NONE in restart cleanup in
* the next start() call from mac80211.
*/
switch (mvm->scan_status) {
case IWL_MVM_SCAN_NONE:
break;
case IWL_MVM_SCAN_OS:
ieee80211_scan_completed(mvm->hw, true);
break;
case IWL_MVM_SCAN_SCHED:
ieee80211_sched_scan_stopped(mvm->hw);
break;
}
/* don't let the transport/FW power down */
iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
if (mvm->restart_fw > 0)
mvm->restart_fw--;
@ -778,6 +816,163 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
iwl_mvm_nic_restart(mvm);
}
struct iwl_d0i3_iter_data {
struct iwl_mvm *mvm;
u8 ap_sta_id;
u8 vif_count;
};
static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_d0i3_iter_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr);
if (vif->type != NL80211_IFTYPE_STATION ||
!vif->bss_conf.assoc)
return;
iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
/*
* on init/association, mvm already configures POWER_TABLE_CMD
* and REPLY_MCAST_FILTER_CMD, so currently don't
* reconfigure them (we might want to use different
* params later on, though).
*/
data->ap_sta_id = mvmvif->ap_sta_id;
data->vif_count++;
}
static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE;
int ret;
struct iwl_d0i3_iter_data d0i3_iter_data = {
.mvm = mvm,
};
struct iwl_wowlan_config_cmd wowlan_config_cmd = {
.wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
IWL_WOWLAN_WAKEUP_BEACON_MISS |
IWL_WOWLAN_WAKEUP_LINK_CHANGE |
IWL_WOWLAN_WAKEUP_BCN_FILTERING),
};
struct iwl_d3_manager_config d3_cfg_cmd = {
.min_sleep_time = cpu_to_le32(1000),
};
IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_enter_d0i3_iterator,
&d0i3_iter_data);
if (d0i3_iter_data.vif_count == 1) {
mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id;
} else {
WARN_ON_ONCE(d0i3_iter_data.vif_count > 1);
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
}
ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags,
sizeof(wowlan_config_cmd),
&wowlan_config_cmd);
if (ret)
return ret;
return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD,
flags | CMD_MAKE_TRANS_IDLE,
sizeof(d3_cfg_cmd), &d3_cfg_cmd);
}
static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = _data;
u32 flags = CMD_ASYNC | CMD_HIGH_PRIO;
IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr);
if (vif->type != NL80211_IFTYPE_STATION ||
!vif->bss_conf.assoc)
return;
iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
}
static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
ieee80211_connection_loss(vif);
}
static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
{
struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
struct iwl_host_cmd get_status_cmd = {
.id = WOWLAN_GET_STATUSES,
.flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
};
struct iwl_wowlan_status_v6 *status;
int ret;
u32 disconnection_reasons, wakeup_reasons;
mutex_lock(&mvm->mutex);
ret = iwl_mvm_send_cmd(mvm, &get_status_cmd);
if (ret)
goto out;
if (!get_status_cmd.resp_pkt)
goto out;
status = (void *)get_status_cmd.resp_pkt->data;
wakeup_reasons = le32_to_cpu(status->wakeup_reasons);
IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
disconnection_reasons =
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
if (wakeup_reasons & disconnection_reasons)
ieee80211_iterate_active_interfaces(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_d0i3_disconnect_iter, mvm);
iwl_free_resp(&get_status_cmd);
out:
mutex_unlock(&mvm->mutex);
}
static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
CMD_WAKE_UP_TRANS;
int ret;
IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
if (ret)
goto out;
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_exit_d0i3_iterator,
mvm);
out:
schedule_work(&mvm->d0i3_exit_work);
return ret;
}
static const struct iwl_op_mode_ops iwl_mvm_ops = {
.start = iwl_op_mode_mvm_start,
.stop = iwl_op_mode_mvm_stop,
@ -789,4 +984,6 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = {
.nic_error = iwl_mvm_nic_error,
.cmd_queue_full = iwl_mvm_cmd_queue_full,
.nic_config = iwl_mvm_nic_config,
.enter_d0i3 = iwl_mvm_enter_d0i3,
.exit_d0i3 = iwl_mvm_exit_d0i3,
};

View file

@ -74,39 +74,36 @@
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
static
int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
struct iwl_beacon_filter_cmd *cmd)
struct iwl_beacon_filter_cmd *cmd,
u32 flags)
{
int ret;
IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
le32_to_cpu(cmd->ba_enable_beacon_abort));
IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
le32_to_cpu(cmd->ba_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
le32_to_cpu(cmd->bf_debug_flag));
IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
le32_to_cpu(cmd->bf_enable_beacon_filter));
IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
le32_to_cpu(cmd->bf_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
le32_to_cpu(cmd->bf_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
le32_to_cpu(cmd->bf_roaming_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
le32_to_cpu(cmd->bf_roaming_state));
IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
le32_to_cpu(cmd->bf_temp_threshold));
IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
le32_to_cpu(cmd->bf_temp_fast_filter));
IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
le32_to_cpu(cmd->bf_temp_slow_filter));
ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
sizeof(struct iwl_beacon_filter_cmd), cmd);
if (!ret) {
IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
le32_to_cpu(cmd->ba_enable_beacon_abort));
IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
le32_to_cpu(cmd->ba_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
le32_to_cpu(cmd->bf_debug_flag));
IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
le32_to_cpu(cmd->bf_enable_beacon_filter));
IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
le32_to_cpu(cmd->bf_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
le32_to_cpu(cmd->bf_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
le32_to_cpu(cmd->bf_roaming_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
le32_to_cpu(cmd->bf_roaming_state));
IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
le32_to_cpu(cmd->bf_temp_threshold));
IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
le32_to_cpu(cmd->bf_temp_fast_filter));
IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
le32_to_cpu(cmd->bf_temp_slow_filter));
}
return ret;
return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
sizeof(struct iwl_beacon_filter_cmd), cmd);
}
static
@ -145,7 +142,7 @@ int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
mvmvif->bf_data.ba_enabled = enable;
iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
}
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
@ -301,8 +298,7 @@ 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 ||
mvm->ps_prevented)
if (mvm->ps_disabled)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@ -312,7 +308,8 @@ 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 || mvmvif->pm_prevented)
if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
mvm->pm_disabled)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@ -419,11 +416,9 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
int ret;
bool ba_enable;
struct iwl_mac_power_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION)
@ -435,56 +430,30 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
sizeof(cmd), &cmd);
if (ret)
return ret;
ba_enable = !!(cmd.flags &
cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
}
static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
struct iwl_mac_power_cmd cmd = {};
struct iwl_mvm_vif *mvmvif __maybe_unused =
iwl_mvm_vif_from_mac80211(vif);
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color));
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
mvmvif->dbgfs_pm.disable_power_off)
cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
#endif
iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
sizeof(cmd), &cmd);
}
static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
{
struct iwl_device_power_cmd cmd = {
.flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
};
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
return 0;
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 ||
force_disable)
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
mvm->ps_disabled = true;
if (mvm->ps_disabled)
cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
#ifdef CONFIG_IWLWIFI_DEBUGFS
@ -501,11 +470,6 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
&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);
@ -544,44 +508,137 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
return 0;
}
static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
struct iwl_power_constraint {
struct ieee80211_vif *bf_vif;
struct ieee80211_vif *bss_vif;
bool pm_disabled;
bool ps_disabled;
};
static void iwl_mvm_power_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;
struct iwl_power_constraint *power_iterator = _data;
mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
switch (ieee80211_vif_type_p2p(vif)) {
case NL80211_IFTYPE_P2P_DEVICE:
break;
ret = iwl_mvm_power_mac_update_mode(mvm, vif);
WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_AP:
/* no BSS power mgmt if we have an active AP */
if (mvmvif->ap_ibss_active)
power_iterator->pm_disabled = true;
break;
case NL80211_IFTYPE_MONITOR:
/* no BSS power mgmt and no device power save */
power_iterator->pm_disabled = true;
power_iterator->ps_disabled = true;
break;
case NL80211_IFTYPE_P2P_CLIENT:
/* no BSS power mgmt if we have a P2P client*/
power_iterator->pm_disabled = true;
break;
case NL80211_IFTYPE_STATION:
/* we should have only one BSS vif */
WARN_ON(power_iterator->bss_vif);
power_iterator->bss_vif = vif;
if (mvmvif->bf_data.bf_enabled &&
!WARN_ON(power_iterator->bf_vif))
power_iterator->bf_vif = vif;
break;
default:
break;
}
}
static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool assign)
static void
iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
struct iwl_power_constraint *constraint)
{
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");
lockdep_assert_held(&mvm->mutex);
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
constraint->pm_disabled = true;
constraint->ps_disabled = true;
}
ieee80211_iterate_active_interfaces(mvm->hw,
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_power_binding_iterator,
mvm);
iwl_mvm_power_iterator, constraint);
/* TODO: remove this and determine this variable in the iterator */
if (mvm->bound_vif_cnt > 1)
constraint->pm_disabled = true;
}
int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_power_constraint constraint = {};
bool ba_enable;
int ret;
lockdep_assert_held(&mvm->mutex);
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
return 0;
iwl_mvm_power_get_global_constraint(mvm, &constraint);
mvm->ps_disabled = constraint.ps_disabled;
mvm->pm_disabled = constraint.pm_disabled;
/* don't update device power state unless we add / remove monitor */
if (vif->type == NL80211_IFTYPE_MONITOR) {
ret = iwl_mvm_power_update_device(mvm);
if (ret)
return ret;
}
ret = iwl_mvm_power_send_cmd(mvm, vif);
if (ret)
return ret;
if (constraint.bss_vif && vif != constraint.bss_vif) {
ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
if (ret)
return ret;
}
if (!constraint.bf_vif)
return 0;
vif = constraint.bf_vif;
mvmvif = iwl_mvm_vif_from_mac80211(vif);
ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, char *buf,
int bufsz)
int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, char *buf,
int bufsz)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mac_power_cmd cmd = {};
int pos = 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
if (WARN_ON(!(mvm->fw->ucode_capa.flags &
IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)))
return 0;
mutex_lock(&mvm->mutex);
memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
mutex_unlock(&mvm->mutex);
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
@ -685,32 +742,46 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
}
#endif
int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_beacon_filter_cmd *cmd,
u32 cmd_flags,
bool d0i3)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_beacon_filter_cmd cmd = {
IWL_BF_CMD_CONFIG_DEFAULTS,
.bf_enable_beacon_filter = cpu_to_le32(1),
};
int ret;
if (mvmvif != mvm->bf_allowed_vif ||
vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);
if (!d0i3)
iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
if (!ret)
/* don't change bf_enabled in case of temporary d0i3 configuration */
if (!ret && !d0i3)
mvmvif->bf_data.bf_enabled = true;
return ret;
}
int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 flags)
{
struct iwl_beacon_filter_cmd cmd = {
IWL_BF_CMD_CONFIG_DEFAULTS,
.bf_enable_beacon_filter = cpu_to_le32(1),
};
return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
}
int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
struct ieee80211_vif *vif,
u32 flags)
{
struct iwl_beacon_filter_cmd cmd = {};
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@ -720,7 +791,7 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
if (!ret)
mvmvif->bf_data.bf_enabled = false;
@ -728,23 +799,89 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
return ret;
}
int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool enable, u32 flags)
{
int ret;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mac_power_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
if (!vif->bss_conf.assoc)
return 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
if (enable) {
/* configure skip over dtim up to 300 msec */
int dtimper = mvm->hw->conf.ps_dtim_period ?: 1;
int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
if (WARN_ON(!dtimper_msec))
return 0;
cmd.flags |=
cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
cmd.skip_dtim_periods = 300 / dtimper_msec;
}
iwl_mvm_power_log(mvm, &cmd);
#ifdef CONFIG_IWLWIFI_DEBUGFS
memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
#endif
ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
sizeof(cmd), &cmd);
if (ret)
return ret;
/* configure beacon filtering */
if (mvmvif != mvm->bf_allowed_vif)
return 0;
if (enable) {
struct iwl_beacon_filter_cmd cmd_bf = {
IWL_BF_CMD_CONFIG_D0I3,
.bf_enable_beacon_filter = cpu_to_le32(1),
};
ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
flags, true);
} else {
if (mvmvif->bf_data.bf_enabled)
ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
else
ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
}
return ret;
}
int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
struct ieee80211_vif *vif,
bool force,
u32 flags)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (!mvmvif->bf_data.bf_enabled)
if (mvmvif != mvm->bf_allowed_vif)
return 0;
return iwl_mvm_enable_beacon_filter(mvm, vif);
if (!mvmvif->bf_data.bf_enabled) {
/* disable beacon filtering explicitly if force is true */
if (force)
return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
return 0;
}
return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
}
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
};
int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm)
{
struct iwl_powertable_cmd cmd = {
.keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC,
};
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
}

View file

@ -1,319 +0,0 @@
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <net/mac80211.h>
#include "iwl-debug.h"
#include "mvm.h"
#include "iwl-modparams.h"
#include "fw-api-power.h"
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
struct iwl_powertable_cmd *cmd)
{
IWL_DEBUG_POWER(mvm,
"Sending power table command for power level %d, flags = 0x%X\n",
iwlmvm_mod_params.power_scheme,
le16_to_cpu(cmd->flags));
IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
le32_to_cpu(cmd->rx_data_timeout));
IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
le32_to_cpu(cmd->tx_data_timeout));
if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
le32_to_cpu(cmd->skip_dtim_periods));
if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
le32_to_cpu(cmd->lprx_rssi_threshold));
}
}
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_powertable_cmd *cmd)
{
struct ieee80211_hw *hw = mvm->hw;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
int dtimper, dtimper_msec;
int keep_alive;
bool radar_detect = false;
struct iwl_mvm_vif *mvmvif __maybe_unused =
iwl_mvm_vif_from_mac80211(vif);
/*
* Regardless of power management state the driver must set
* keep alive period. FW will use it for sending keep alive NDPs
* immediately after association.
*/
cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
if (!vif->bss_conf.assoc)
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
mvmvif->dbgfs_pm.disable_power_off)
cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
#endif
if (!vif->bss_conf.ps)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
if (vif->bss_conf.beacon_rate &&
(vif->bss_conf.beacon_rate->bitrate == 10 ||
vif->bss_conf.beacon_rate->bitrate == 60)) {
cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
cmd->lprx_rssi_threshold =
cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
}
dtimper = hw->conf.ps_dtim_period ?: 1;
/* Check if radar detection is required on current channel */
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
WARN_ON(!chanctx_conf);
if (chanctx_conf) {
chan = chanctx_conf->def.chan;
radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
}
rcu_read_unlock();
/* Check skip over DTIM conditions */
if (!radar_detect && (dtimper <= 10) &&
(iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
cmd->skip_dtim_periods = cpu_to_le32(3);
}
/* Check that keep alive period is at least 3 * DTIM */
dtimper_msec = dtimper * vif->bss_conf.beacon_int;
keep_alive = max_t(int, 3 * dtimper_msec,
MSEC_PER_SEC * cmd->keep_alive_seconds);
keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
cmd->keep_alive_seconds = keep_alive;
if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
} else {
cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
if (mvmvif->dbgfs_pm.skip_over_dtim)
cmd->flags |=
cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
else
cmd->flags &=
cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
}
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
cmd->rx_data_timeout =
cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
cmd->tx_data_timeout =
cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
cmd->skip_dtim_periods =
cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
if (mvmvif->dbgfs_pm.lprx_ena)
cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
else
cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
}
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
cmd->lprx_rssi_threshold =
cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
int ret;
bool ba_enable;
struct iwl_powertable_cmd cmd = {};
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
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)
return 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
sizeof(cmd), &cmd);
if (ret)
return ret;
ba_enable = !!(cmd.flags &
cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
}
static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
struct iwl_powertable_cmd cmd = {};
struct iwl_mvm_vif *mvmvif __maybe_unused =
iwl_mvm_vif_from_mac80211(vif);
if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
mvmvif->dbgfs_pm.disable_power_off)
cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
#endif
iwl_mvm_power_log(mvm, &cmd);
return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
sizeof(cmd), &cmd);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, char *buf,
int bufsz)
{
struct iwl_powertable_cmd cmd = {};
int pos = 0;
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
(cmd.flags &
cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
0 : 1);
pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
le32_to_cpu(cmd.skip_dtim_periods));
pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
iwlmvm_mod_params.power_scheme);
pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
le16_to_cpu(cmd.flags));
pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
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, "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",
le32_to_cpu(cmd.lprx_rssi_threshold));
}
return pos;
}
#endif
const struct iwl_mvm_power_ops pm_legacy_ops = {
.power_update_mode = iwl_mvm_power_legacy_update_mode,
.power_disable = iwl_mvm_power_legacy_disable,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
#endif
};

View file

@ -65,9 +65,14 @@
#include "fw-api.h"
#include "mvm.h"
#define QUOTA_100 IWL_MVM_MAX_QUOTA
#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
struct iwl_mvm_quota_iterator_data {
int n_interfaces[MAX_BINDINGS];
int colors[MAX_BINDINGS];
int low_latency[MAX_BINDINGS];
int n_low_latency_bindings;
struct ieee80211_vif *new_vif;
};
@ -107,22 +112,29 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (vif->bss_conf.assoc)
data->n_interfaces[id]++;
break;
break;
return;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
if (mvmvif->ap_ibss_active)
data->n_interfaces[id]++;
break;
break;
return;
case NL80211_IFTYPE_MONITOR:
if (mvmvif->monitor_active)
data->n_interfaces[id]++;
break;
break;
return;
case NL80211_IFTYPE_P2P_DEVICE:
break;
return;
default:
WARN_ON_ONCE(1);
break;
return;
}
data->n_interfaces[id]++;
if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
data->n_low_latency_bindings++;
data->low_latency[id] = true;
}
}
@ -162,12 +174,13 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
{
struct iwl_time_quota_cmd cmd = {};
int i, idx, ret, num_active_macs, quota, quota_rem;
int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
struct iwl_mvm_quota_iterator_data data = {
.n_interfaces = {},
.colors = { -1, -1, -1, -1 },
.new_vif = newvif,
};
u32 ll_max_duration;
lockdep_assert_held(&mvm->mutex);
@ -186,6 +199,21 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
}
switch (data.n_low_latency_bindings) {
case 0: /* no low latency - use default */
ll_max_duration = 0;
break;
case 1: /* SingleBindingLowLatencyMode */
ll_max_duration = IWL_MVM_LOWLAT_SINGLE_BINDING_MAXDUR;
break;
case 2: /* DualBindingLowLatencyMode */
ll_max_duration = IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR;
break;
default: /* MultiBindingLowLatencyMode */
ll_max_duration = 0;
break;
}
/*
* The FW's scheduling session consists of
* IWL_MVM_MAX_QUOTA fragments. Divide these fragments
@ -197,11 +225,39 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
num_active_macs += data.n_interfaces[i];
}
quota = 0;
quota_rem = 0;
if (num_active_macs) {
quota = IWL_MVM_MAX_QUOTA / num_active_macs;
quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
n_non_lowlat = num_active_macs;
if (data.n_low_latency_bindings == 1) {
for (i = 0; i < MAX_BINDINGS; i++) {
if (data.low_latency[i]) {
n_non_lowlat -= data.n_interfaces[i];
break;
}
}
}
if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
/*
* Reserve quota for the low latency binding in case that
* there are several data bindings but only a single
* low latency one. Split the rest of the quota equally
* between the other data interfaces.
*/
quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
quota_rem = QUOTA_100 - n_non_lowlat * quota -
QUOTA_LOWLAT_MIN;
} else if (num_active_macs) {
/*
* There are 0 or more than 1 low latency bindings, or all the
* data interfaces belong to the single low latency binding.
* Split the quota equally between the data interfaces.
*/
quota = QUOTA_100 / num_active_macs;
quota_rem = QUOTA_100 % num_active_macs;
} else {
/* values don't really matter - won't be used */
quota = 0;
quota_rem = 0;
}
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@ -211,19 +267,42 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
cmd.quotas[idx].id_and_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
if (data.n_interfaces[i] <= 0) {
if (data.n_interfaces[i] <= 0)
cmd.quotas[idx].quota = cpu_to_le32(0);
cmd.quotas[idx].max_duration = cpu_to_le32(0);
} else {
else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
data.low_latency[i])
/*
* There is more than one binding, but only one of the
* bindings is in low latency. For this case, allocate
* the minimal required quota for the low latency
* binding.
*/
cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
else
cmd.quotas[idx].quota =
cpu_to_le32(quota * data.n_interfaces[i]);
WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
"Binding=%d, quota=%u > max=%u\n",
idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
if (data.n_interfaces[i] && !data.low_latency[i])
cmd.quotas[idx].max_duration =
cpu_to_le32(ll_max_duration);
else
cmd.quotas[idx].max_duration = cpu_to_le32(0);
}
idx++;
}
/* Give the remainder of the session to the first binding */
le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
/* Give the remainder of the session to the first data binding */
for (i = 0; i < MAX_BINDINGS; i++) {
if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
break;
}
}
iwl_mvm_adjust_quota_for_noa(mvm, &cmd);

View file

@ -380,49 +380,49 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
* (2.4 GHz) band.
*/
static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = {
7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0
};
/* Expected TpT tables. 4 indexes:
* 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI
*/
static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0},
{0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0},
{0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0},
{0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0},
};
static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275},
{0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280},
{0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173},
{0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284},
};
static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308},
{0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312},
{0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466},
{0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691},
};
static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0},
{0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0},
{0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0},
{0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0},
};
static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300},
{0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303},
{0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053},
{0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221},
};
static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = {
{0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319},
{0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320},
{0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219},
@ -905,7 +905,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
rate->bw = RATE_MCS_CHAN_WIDTH_20;
WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
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];
@ -1169,12 +1169,12 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
lq_sta->visited_columns = 0;
}
static s32 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
const struct rs_tx_column *column,
u32 bw)
{
/* Used to choose among HT tables */
s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
if (WARN_ON_ONCE(column->mode != RS_LEGACY &&
column->mode != RS_SISO &&
@ -1262,9 +1262,8 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm,
&(lq_sta->lq_info[lq_sta->active_tbl]);
s32 active_sr = active_tbl->win[index].success_ratio;
s32 active_tpt = active_tbl->expected_tpt[index];
/* expected "search" throughput */
s32 *tpt_tbl = tbl->expected_tpt;
const u16 *tpt_tbl = tbl->expected_tpt;
s32 new_rate, high, low, start_hi;
u16 high_low;
@ -1479,7 +1478,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
const struct rs_tx_column *next_col;
allow_column_func_t allow_func;
u8 valid_ants = iwl_fw_valid_tx_ant(mvm->fw);
s32 *expected_tpt_tbl;
const u16 *expected_tpt_tbl;
s32 tpt, max_expected_tpt;
for (i = 0; i < MAX_NEXT_COLUMNS; i++) {

View file

@ -277,7 +277,7 @@ enum rs_column {
struct iwl_scale_tbl_info {
struct rs_rate rate;
enum rs_column column;
s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};

View file

@ -129,22 +129,16 @@ static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
struct ieee80211_rx_status *rx_status)
{
int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
int rssi_all_band_a, rssi_all_band_b;
u32 agc_a, agc_b, max_agc;
u32 agc_a, agc_b;
u32 val;
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
max_agc = max_t(u32, agc_a, agc_b);
val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >>
IWL_OFDM_RSSI_ALLBAND_A_POS;
rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >>
IWL_OFDM_RSSI_ALLBAND_B_POS;
/*
* dBm = rssi dB - agc dB - constant.

View file

@ -407,6 +407,8 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
return 0;
}
@ -475,6 +477,7 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
if (iwl_mvm_is_radio_killed(mvm)) {
ieee80211_scan_completed(mvm->hw, true);
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
mvm->scan_status = IWL_MVM_SCAN_NONE;
return;
}
@ -487,7 +490,7 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
if (ret) {
IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
/* mac80211's state will be cleaned in the fw_restart flow */
/* mac80211's state will be cleaned in the nic_restart flow */
goto out_remove_notif;
}
@ -508,11 +511,16 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
/* scan status must be locked for proper checking */
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
"completed" : "aborted");
mvm->scan_status = IWL_MVM_SCAN_NONE;
/* might already be something else again, don't reset if so */
if (mvm->scan_status == IWL_MVM_SCAN_SCHED)
mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_sched_scan_stopped(mvm->hw);
return 0;

View file

@ -66,27 +66,27 @@
#include "sta.h"
#include "rs.h"
static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
{
memset(cmd_v5, 0, sizeof(*cmd_v5));
cmd_v5->add_modify = cmd_v6->add_modify;
cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
cmd_v5->sta_id = cmd_v6->sta_id;
cmd_v5->modify_mask = cmd_v6->modify_mask;
cmd_v5->station_flags = cmd_v6->station_flags;
cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
cmd_v5->assoc_id = cmd_v6->assoc_id;
cmd_v5->beamform_flags = cmd_v6->beamform_flags;
cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
cmd_v5->add_modify = cmd_v7->add_modify;
cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
cmd_v5->sta_id = cmd_v7->sta_id;
cmd_v5->modify_mask = cmd_v7->modify_mask;
cmd_v5->station_flags = cmd_v7->station_flags;
cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
cmd_v5->assoc_id = cmd_v7->assoc_id;
cmd_v5->beamform_flags = cmd_v7->beamform_flags;
cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk;
}
static void
@ -110,7 +110,7 @@ iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
}
static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
struct iwl_mvm_add_sta_cmd_v6 *cmd,
struct iwl_mvm_add_sta_cmd_v7 *cmd,
int *status)
{
struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
@ -119,14 +119,14 @@ static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
cmd, status);
iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
&cmd_v5, status);
}
static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
struct iwl_mvm_add_sta_cmd_v6 *cmd)
struct iwl_mvm_add_sta_cmd_v7 *cmd)
{
struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
@ -134,7 +134,7 @@ static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
sizeof(*cmd), cmd);
iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
&cmd_v5);
@ -175,19 +175,30 @@ static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
&sta_cmd);
}
static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
enum nl80211_iftype iftype)
{
int sta_id;
u32 reserved_ids = 0;
BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
lockdep_assert_held(&mvm->mutex);
/* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
if (iftype != NL80211_IFTYPE_STATION)
reserved_ids = BIT(0);
/* Don't take rcu_read_lock() since we are protected by mvm->mutex */
for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
if (BIT(sta_id) & reserved_ids)
continue;
if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex)))
return sta_id;
}
return IWL_MVM_STATION_COUNT;
}
@ -196,7 +207,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
bool update)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd;
int ret;
u32 status;
u32 agg_size = 0, mpdu_dens = 0;
@ -312,7 +323,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
lockdep_assert_held(&mvm->mutex);
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
sta_id = iwl_mvm_find_free_sta_id(mvm);
sta_id = iwl_mvm_find_free_sta_id(mvm,
ieee80211_vif_type_p2p(vif));
else
sta_id = mvm_sta->sta_id;
@ -368,7 +380,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain)
{
struct iwl_mvm_add_sta_cmd_v6 cmd = {};
struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@ -522,6 +534,10 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
/* unassoc - go ahead - remove the AP STA now */
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
/* clear d0i3_ap_sta_id if no longer relevant */
if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
}
/*
@ -560,10 +576,10 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
}
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
u32 qmask)
u32 qmask, enum nl80211_iftype iftype)
{
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
sta->sta_id = iwl_mvm_find_free_sta_id(mvm);
sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
return -ENOSPC;
}
@ -587,13 +603,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
const u8 *addr,
u16 mac_id, u16 color)
{
struct iwl_mvm_add_sta_cmd_v6 cmd;
struct iwl_mvm_add_sta_cmd_v7 cmd;
int ret;
u32 status;
lockdep_assert_held(&mvm->mutex);
memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
cmd.sta_id = sta->sta_id;
cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
color));
@ -627,7 +643,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
lockdep_assert_held(&mvm->mutex);
/* Add the aux station, but without any queues */
ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
NL80211_IFTYPE_UNSPECIFIED);
if (ret)
return ret;
@ -699,7 +716,8 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
lockdep_assert_held(&mvm->mutex);
qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask,
ieee80211_vif_type_p2p(vif));
if (ret)
return ret;
@ -735,7 +753,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u16 ssn, bool start)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
struct iwl_mvm_add_sta_cmd_v6 cmd = {};
struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@ -794,7 +812,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u8 queue, bool start)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
struct iwl_mvm_add_sta_cmd_v6 cmd = {};
struct iwl_mvm_add_sta_cmd_v7 cmd = {};
int ret;
u32 status;
@ -833,7 +851,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
return ret;
}
static const u8 tid_to_ac[] = {
static const u8 tid_to_mac80211_ac[] = {
IEEE80211_AC_BE,
IEEE80211_AC_BK,
IEEE80211_AC_BK,
@ -844,6 +862,17 @@ static const u8 tid_to_ac[] = {
IEEE80211_AC_VO,
};
static const u8 tid_to_ucode_ac[] = {
AC_BE,
AC_BK,
AC_BK,
AC_BE,
AC_VI,
AC_VI,
AC_VO,
AC_VO,
};
int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
@ -874,7 +903,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
}
/* the new tx queue is still connected to the same mac80211 queue */
mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]];
spin_lock_bh(&mvmsta->lock);
tid_data = &mvmsta->tid_data[tid];
@ -916,7 +945,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
tid_data->ssn = 0xffff;
spin_unlock_bh(&mvmsta->lock);
fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
if (ret)
@ -1411,7 +1440,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_add_sta_cmd_v6 cmd = {
struct iwl_mvm_add_sta_cmd_v7 cmd = {
.add_modify = STA_MODE_MODIFY,
.sta_id = mvmsta->sta_id,
.station_flags_msk = cpu_to_le32(STA_FLG_PS),
@ -1427,28 +1456,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
u16 cnt)
u16 cnt, u16 tids, bool more_data,
bool agg)
{
u16 sleep_state_flags =
(reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_add_sta_cmd_v6 cmd = {
struct iwl_mvm_add_sta_cmd_v7 cmd = {
.add_modify = STA_MODE_MODIFY,
.sta_id = mvmsta->sta_id,
.modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
.sleep_tx_count = cpu_to_le16(cnt),
.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
/*
* Same modify mask for sleep_tx_count and sleep_state_flags so
* we must set the sleep_state_flags too.
*/
.sleep_state_flags = cpu_to_le16(sleep_state_flags),
};
int ret;
int tid, ret;
unsigned long _tids = tids;
/* convert TIDs to ACs - we don't support TSPEC so that's OK
* Note that this field is reserved and unused by firmware not
* supporting GO uAPSD, so it's safe to always do this.
*/
for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
/* If we're releasing frames from aggregation queues then check if the
* all queues combined that we're releasing frames from have
* - more frames than the service period, in which case more_data
* needs to be set
* - fewer than 'cnt' frames, in which case we need to adjust the
* firmware command (but do that unconditionally)
*/
if (agg) {
int remaining = cnt;
spin_lock_bh(&mvmsta->lock);
for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
struct iwl_mvm_tid_data *tid_data;
u16 n_queued;
tid_data = &mvmsta->tid_data[tid];
if (WARN(tid_data->state != IWL_AGG_ON &&
tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
"TID %d state is %d\n",
tid, tid_data->state)) {
spin_unlock_bh(&mvmsta->lock);
ieee80211_sta_eosp(sta);
return;
}
n_queued = iwl_mvm_tid_queued(tid_data);
if (n_queued > remaining) {
more_data = true;
remaining = 0;
break;
}
remaining -= n_queued;
}
spin_unlock_bh(&mvmsta->lock);
cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
if (WARN_ON(cnt - remaining == 0)) {
ieee80211_sta_eosp(sta);
return;
}
}
/* Note: this is ignored by firmware not supporting GO uAPSD */
if (more_data)
cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
mvmsta->next_status_eosp = true;
cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
} else {
cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
}
/* TODO: somehow the fw doesn't seem to take PS_POLL into account */
ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
if (ret)
IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
}
int iwl_mvm_rx_eosp_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_mvm_eosp_notification *notif = (void *)pkt->data;
struct ieee80211_sta *sta;
u32 sta_id = le32_to_cpu(notif->sta_id);
if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
return 0;
rcu_read_lock();
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
if (!IS_ERR_OR_NULL(sta))
ieee80211_sta_eosp(sta);
rcu_read_unlock();
return 0;
}

View file

@ -195,24 +195,33 @@ struct iwl_mvm;
/**
* DOC: AP mode - PS
*
* When a station is asleep, the fw will set it as "asleep". All the
* non-aggregation frames to that station will be dropped by the fw
* (%TX_STATUS_FAIL_DEST_PS failure code).
* AMPDUs are in a separate queue that is stopped by the fw. We just need to
* let mac80211 know how many frames we have in these queues so that it can
* properly handle trigger frames.
* When the a trigger frame is received, mac80211 tells the driver to send
* frames from the AMPDU queues or AC queue depending on which queue are
* delivery-enabled and what TID has frames to transmit (Note that mac80211 has
* all the knowledege since all the non-agg frames are buffered / filtered, and
* the driver tells mac80211 about agg frames). The driver needs to tell the fw
* to let frames out even if the station is asleep. This is done by
* %iwl_mvm_sta_modify_sleep_tx_count.
* When we receive a frame from that station with PM bit unset, the
* driver needs to let the fw know that this station isn't alseep any more.
* This is done by %iwl_mvm_sta_modify_ps_wake.
* When a station is asleep, the fw will set it as "asleep". All frames on
* shared queues (i.e. non-aggregation queues) to that station will be dropped
* by the fw (%TX_STATUS_FAIL_DEST_PS failure code).
*
* TODO - EOSP handling
* AMPDUs are in a separate queue that is stopped by the fw. We just need to
* let mac80211 know when there are frames in these queues so that it can
* properly handle trigger frames.
*
* When a trigger frame is received, mac80211 tells the driver to send frames
* from the AMPDU queues or sends frames to non-aggregation queues itself,
* depending on which ACs are delivery-enabled and what TID has frames to
* transmit. Note that mac80211 has all the knowledege since all the non-agg
* frames are buffered / filtered, and the driver tells mac80211 about agg
* frames). The driver needs to tell the fw to let frames out even if the
* station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
*
* When we receive a frame from that station with PM bit unset, the driver
* needs to let the fw know that this station isn't asleep any more. This is
* done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
* station's wakeup.
*
* For a GO, the Service Period might be cut short due to an absence period
* of the GO. In this (and all other cases) the firmware notifies us with the
* EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we
* already sent to the device will be rejected again.
*
* See also "AP support for powersaving clients" in mac80211.h.
*/
/**
@ -261,6 +270,12 @@ struct iwl_mvm_tid_data {
u16 ssn;
};
static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
{
return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number),
tid_data->next_reclaimed);
}
/**
* struct iwl_mvm_sta - representation of a station in the driver
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
@ -269,7 +284,11 @@ struct iwl_mvm_tid_data {
* @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
* tid.
* @max_agg_bufsize: the maximal size of the AGG buffer for this station
* @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced
* by debugfs.
* @bt_reduced_txpower: is reduced tx power enabled for this station
* @next_status_eosp: the next reclaimed packet is a PS-Poll response and
* we need to signal the EOSP
* @lock: lock to protect the whole struct. Since %tid_data is access from Tx
* and from Tx response flow, it needs a spinlock.
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
@ -287,7 +306,9 @@ struct iwl_mvm_sta {
u32 mac_id_n_color;
u16 tid_disable_agg;
u8 max_agg_bufsize;
bool bt_reduced_txpower_dbg;
bool bt_reduced_txpower;
bool next_status_eosp;
spinlock_t lock;
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
@ -345,6 +366,10 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
struct ieee80211_sta *sta, u32 iv32,
u16 *phase1key);
int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
/* AMPDU */
int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, u16 ssn, bool start);
@ -359,7 +384,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
u32 qmask);
u32 qmask, enum nl80211_iftype iftype);
void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta);
int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@ -375,7 +400,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
u16 cnt);
u16 cnt, u16 tids, bool more_data,
bool agg);
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain);

View file

@ -126,6 +126,7 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
* in iwl_mvm_te_handle_notif).
*/
clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
/*
* Of course, our status bit is just as racy as mac80211, so in
@ -210,6 +211,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
ieee80211_ready_on_channel(mvm->hw);
}
} else {

View file

@ -403,7 +403,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
}
}
static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
{
struct iwl_host_cmd cmd = {
.id = REPLY_THERMAL_MNG_BACKOFF,
@ -412,6 +412,8 @@ static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
.flags = CMD_SYNC,
};
backoff = max(backoff, mvm->thermal_throttle.min_backoff);
if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
backoff);
@ -534,7 +536,7 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
.support_tx_backoff = true,
};
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
{
struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
@ -546,6 +548,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
tt->params = &iwl7000_tt_params;
tt->throttle = false;
tt->min_backoff = min_backoff;
INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
}

View file

@ -377,6 +377,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
/* From now on, we cannot access info->control */
/*
* we handle that entirely ourselves -- for uAPSD the firmware
* will always send a notification, and for PS-Poll responses
* we'll notify mac80211 when getting frame status
*/
info->flags &= ~IEEE80211_TX_STATUS_EOSP;
spin_lock(&mvmsta->lock);
if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
@ -437,6 +444,17 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
lockdep_assert_held(&mvmsta->lock);
if ((tid_data->state == IWL_AGG_ON ||
tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
iwl_mvm_tid_queued(tid_data) == 0) {
/*
* Now that this aggregation queue is empty tell mac80211 so it
* knows we no longer have frames buffered for the station on
* this TID (for the TIM bitmap calculation.)
*/
ieee80211_sta_set_buffered(sta, tid, false);
}
if (tid_data->ssn != tid_data->next_reclaimed)
return;
@ -674,6 +692,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
iwl_mvm_check_ratid_empty(mvm, sta, tid);
spin_unlock_bh(&mvmsta->lock);
}
if (mvmsta->next_status_eosp) {
mvmsta->next_status_eosp = false;
ieee80211_sta_eosp(sta);
}
} else {
sta = NULL;
mvmsta = NULL;

View file

@ -376,9 +376,67 @@ struct iwl_error_event_table {
u32 flow_handler; /* FH read/write pointers, RX credit */
} __packed;
/*
* UMAC error struct - relevant starting from family 8000 chip.
* Note: This structure is read from the device with IO accesses,
* and the reading already does the endian conversion. As it is
* read with u32-sized accesses, any members with a different size
* need to be ordered correctly though!
*/
struct iwl_umac_error_event_table {
u32 valid; /* (nonzero) valid, (0) log is empty */
u32 error_id; /* type of error */
u32 pc; /* program counter */
u32 blink1; /* branch link */
u32 blink2; /* branch link */
u32 ilink1; /* interrupt link */
u32 ilink2; /* interrupt link */
u32 data1; /* error-specific data */
u32 data2; /* error-specific data */
u32 line; /* source code line of error */
u32 umac_ver; /* umac version */
} __packed;
#define ERROR_START_OFFSET (1 * sizeof(u32))
#define ERROR_ELEM_SIZE (7 * sizeof(u32))
static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
{
struct iwl_trans *trans = mvm->trans;
struct iwl_umac_error_event_table table;
u32 base;
base = mvm->umac_error_event_table;
if (base < 0x800000 || base >= 0x80C000) {
IWL_ERR(mvm,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
(mvm->cur_ucode == IWL_UCODE_INIT)
? "Init" : "RT");
return;
}
iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
mvm->status, table.valid);
}
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc);
IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver);
}
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
{
struct iwl_trans *trans = mvm->trans;
@ -394,7 +452,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
base = mvm->fw->inst_errlog_ptr;
}
if (base < 0x800000 || base >= 0x80C000) {
if (base < 0x800000) {
IWL_ERR(mvm,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
@ -451,13 +509,17 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
if (mvm->support_umac_log)
iwl_mvm_dump_umac_error_log(mvm);
}
void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
{
const struct fw_img *img;
int ofs, len = 0;
u8 *buf;
int i;
__le32 *buf;
if (!mvm->ucode_loaded)
return;
@ -471,7 +533,12 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
return;
iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
iwl_print_hex_error(mvm->trans, buf, len);
len = len >> 2;
for (i = 0; i < len; i++) {
IWL_ERR(mvm, "0x%08X\n", le32_to_cpu(buf[i]));
/* Add a small delay to let syslog catch up */
udelay(10);
}
kfree(buf);
}
@ -514,7 +581,7 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_smps_mode smps_request)
{
struct iwl_mvm_vif *mvmvif;
enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
enum ieee80211_smps_mode smps_mode;
int i;
lockdep_assert_held(&mvm->mutex);
@ -523,6 +590,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
return;
if (vif->type == NL80211_IFTYPE_AP)
smps_mode = IEEE80211_SMPS_OFF;
else
smps_mode = IEEE80211_SMPS_AUTOMATIC;
mvmvif = iwl_mvm_vif_from_mac80211(vif);
mvmvif->smps_requests[req_type] = smps_request;
for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
@ -536,3 +608,22 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
ieee80211_request_smps(vif, smps_mode);
}
int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool value)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int res;
lockdep_assert_held(&mvm->mutex);
mvmvif->low_latency = value;
res = iwl_mvm_update_quotas(mvm, NULL);
if (res)
return res;
iwl_mvm_bt_coex_vif_change(mvm);
return iwl_mvm_power_update_mac(mvm, vif);
}

View file

@ -66,6 +66,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
#include <linux/acpi.h>
#include "iwl-trans.h"
#include "iwl-drv.h"
@ -385,12 +386,91 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
/* 8000 Series */
{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
#endif /* CONFIG_IWLMVM */
{0}
};
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
#ifdef CONFIG_ACPI
#define SPL_METHOD "SPLC"
#define SPL_DOMAINTYPE_MODULE BIT(0)
#define SPL_DOMAINTYPE_WIFI BIT(1)
#define SPL_DOMAINTYPE_WIGIG BIT(2)
#define SPL_DOMAINTYPE_RFEM BIT(3)
static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
{
union acpi_object *limits, *domain_type, *power_limit;
if (splx->type != ACPI_TYPE_PACKAGE ||
splx->package.count != 2 ||
splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
splx->package.elements[0].integer.value != 0) {
IWL_ERR(trans, "Unsupported splx structure");
return 0;
}
limits = &splx->package.elements[1];
if (limits->type != ACPI_TYPE_PACKAGE ||
limits->package.count < 2 ||
limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
IWL_ERR(trans, "Invalid limits element");
return 0;
}
domain_type = &limits->package.elements[0];
power_limit = &limits->package.elements[1];
if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
IWL_DEBUG_INFO(trans, "WiFi power is not limited");
return 0;
}
return power_limit->integer.value;
}
static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
{
acpi_handle pxsx_handle;
acpi_handle handle;
struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
acpi_status status;
pxsx_handle = ACPI_HANDLE(&pdev->dev);
if (!pxsx_handle) {
IWL_ERR(trans, "Could not retrieve root port ACPI handle");
return;
}
/* Get the method's handle */
status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
if (ACPI_FAILURE(status)) {
IWL_DEBUG_INFO(trans, "SPL method not found");
return;
}
/* Call SPLC with no arguments */
status = acpi_evaluate_object(handle, NULL, NULL, &splx);
if (ACPI_FAILURE(status)) {
IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
return;
}
trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
trans->dflt_pwr_limit);
kfree(splx.pointer);
}
#else /* CONFIG_ACPI */
static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
#endif
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
@ -415,6 +495,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_free_trans;
}
set_dflt_pwr_limit(iwl_trans, pdev);
/* register transport layer debugfs here */
ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
if (ret)

View file

@ -802,10 +802,9 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 inta;
lockdep_assert_held(&trans_pcie->irq_lock);
lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock);
trace_iwlwifi_dev_irq(trans->dev);

View file

@ -89,6 +89,7 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
static void iwl_pcie_apm_config(struct iwl_trans *trans)
{
@ -132,8 +133,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
*/
/* Disable L0S exit timer (platform NMI Work/Around) */
iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
/*
* Disable L0s without affecting L1;
@ -203,19 +205,23 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
/*
* Enable DMA clock and wait for it to stabilize.
*
* Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
* do not disable clocks. This preserves any hardware bits already
* set by default in "CLK_CTRL_REG" after reset.
* Write to "CLK_EN_REG"; "1" bits enable clocks, while "0"
* bits do not disable clocks. This preserves any hardware
* bits already set by default in "CLK_CTRL_REG" after reset.
*/
iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(20);
if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
iwl_write_prph(trans, APMG_CLK_EN_REG,
APMG_CLK_VAL_DMA_CLK_RQT);
udelay(20);
/* Disable L1-Active */
iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
/* Disable L1-Active */
iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
/* 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);
/* 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->status);
@ -273,7 +279,8 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
spin_unlock(&trans_pcie->irq_lock);
iwl_pcie_set_pwr(trans, false);
if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
iwl_pcie_set_pwr(trans, false);
iwl_op_mode_nic_config(trans->op_mode);
@ -435,78 +442,106 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
return ret;
}
static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
const struct fw_img *image,
int cpu,
int *first_ucode_section)
{
int shift_param;
u32 address;
int ret = 0;
int i, ret = 0;
u32 last_read_idx = 0;
if (cpu == 1) {
shift_param = 0;
address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR;
*first_ucode_section = 0;
} else {
shift_param = 16;
address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR;
(*first_ucode_section)++;
}
/* set CPU to started */
iwl_trans_set_bits_mask(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
CSR_CPU_STATUS_LOADING_STARTED << shift_param,
1);
for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
last_read_idx = i;
/* set last complete descriptor number */
iwl_trans_set_bits_mask(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED
<< shift_param,
1);
if (!image->sec[i].data ||
image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
IWL_DEBUG_FW(trans,
"Break since Data not valid or Empty section, sec = %d\n",
i);
break;
}
/* set last loaded block */
iwl_trans_set_bits_mask(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK
<< shift_param,
1);
if (i == (*first_ucode_section) + 1)
/* set CPU to started */
iwl_set_bits_prph(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
LMPM_CPU_HDRS_LOADING_COMPLETED
<< shift_param);
ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
if (ret)
return ret;
}
/* image loading complete */
iwl_trans_set_bits_mask(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
CSR_CPU_STATUS_LOADING_COMPLETED
<< shift_param,
1);
iwl_set_bits_prph(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
/* set FH_TCSR_0_REG */
iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1);
*first_ucode_section = last_read_idx;
/* verify image verification started */
ret = iwl_poll_bit(trans, address,
CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS,
CSR_SECURE_TIME_OUT);
if (ret < 0) {
IWL_ERR(trans, "secure boot process didn't start\n");
return ret;
return 0;
}
static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
const struct fw_img *image,
int cpu,
int *first_ucode_section)
{
int shift_param;
int i, ret = 0;
u32 last_read_idx = 0;
if (cpu == 1) {
shift_param = 0;
*first_ucode_section = 0;
} else {
shift_param = 16;
(*first_ucode_section)++;
}
/* wait for image verification to complete */
ret = iwl_poll_bit(trans, address,
CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED,
CSR_SECURE_TIME_OUT);
for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
last_read_idx = i;
if (ret < 0) {
IWL_ERR(trans, "Time out on secure boot process\n");
return ret;
if (!image->sec[i].data ||
image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
IWL_DEBUG_FW(trans,
"Break since Data not valid or Empty section, sec = %d\n",
i);
break;
}
ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
if (ret)
return ret;
}
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
iwl_set_bits_prph(trans,
CSR_UCODE_LOAD_STATUS_ADDR,
(LMPM_CPU_UCODE_LOADING_COMPLETED |
LMPM_CPU_HDRS_LOADING_COMPLETED |
LMPM_CPU_UCODE_LOADING_STARTED) <<
shift_param);
*first_ucode_section = last_read_idx;
return 0;
}
static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
int i, ret = 0;
int ret = 0;
int first_ucode_section;
IWL_DEBUG_FW(trans,
"working with %s image\n",
@ -518,53 +553,68 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
/* configure the ucode to be ready to get the secured image */
if (image->is_secure) {
/* set secure boot inspector addresses */
iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0);
iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0);
iwl_write_prph(trans,
LMPM_SECURE_INSPECTOR_CODE_ADDR,
LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
/* release CPU1 reset if secure inspector image burned in OTP */
iwl_write32(trans, CSR_RESET, 0);
}
iwl_write_prph(trans,
LMPM_SECURE_INSPECTOR_DATA_ADDR,
LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
/* load to FW the binary sections of CPU1 */
IWL_DEBUG_INFO(trans, "Loading CPU1\n");
for (i = 0;
i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
i++) {
if (!image->sec[i].data)
break;
ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
/* set CPU1 header address */
iwl_write_prph(trans,
LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
LMPM_SECURE_CPU1_HDR_MEM_SPACE);
/* load to FW the binary Secured sections of CPU1 */
ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
&first_ucode_section);
if (ret)
return ret;
}
/* configure the ucode to start secure process on CPU1 */
if (image->is_secure) {
/* config CPU1 to start secure protocol */
ret = iwl_pcie_secure_set(trans, 1);
if (ret)
return ret;
} else {
/* Remove all resets to allow NIC to operate */
iwl_write32(trans, CSR_RESET, 0);
/* load to FW the binary Non secured sections of CPU1 */
ret = iwl_pcie_load_cpu_sections(trans, image, 1,
&first_ucode_section);
if (ret)
return ret;
}
if (image->is_dual_cpus) {
/* load to FW the binary sections of CPU2 */
IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n");
for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU;
i < IWL_UCODE_SECTION_MAX; i++) {
if (!image->sec[i].data)
break;
ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
if (ret)
return ret;
}
/* set CPU2 header address */
iwl_write_prph(trans,
LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
LMPM_SECURE_CPU2_HDR_MEM_SPACE);
if (image->is_secure) {
/* set CPU2 for secure protocol */
ret = iwl_pcie_secure_set(trans, 2);
if (ret)
return ret;
/* load to FW the binary sections of CPU2 */
if (image->is_secure)
ret = iwl_pcie_load_cpu_secured_sections(
trans, image, 2,
&first_ucode_section);
else
ret = iwl_pcie_load_cpu_sections(trans, image, 2,
&first_ucode_section);
if (ret)
return ret;
}
/* release CPU reset */
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
else
iwl_write32(trans, CSR_RESET, 0);
if (image->is_secure) {
/* wait for image verification to complete */
ret = iwl_poll_prph_bit(trans,
LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
LMPM_SECURE_BOOT_STATUS_SUCCESS,
LMPM_SECURE_BOOT_STATUS_SUCCESS,
LMPM_SECURE_TIME_OUT);
if (ret < 0) {
IWL_ERR(trans, "Time out on secure boot process\n");
return ret;
}
}
@ -1407,16 +1457,15 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
{
struct iwl_trans *trans = file->private_data;
char *buf = NULL;
int pos = 0;
ssize_t ret = -EFAULT;
ret = pos = iwl_dump_fh(trans, &buf);
if (buf) {
ret = simple_read_from_buffer(user_buf,
count, ppos, buf, pos);
kfree(buf);
}
ssize_t ret;
ret = iwl_dump_fh(trans, &buf);
if (ret < 0)
return ret;
if (!buf)
return -EINVAL;
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
return ret;
}

View file

@ -705,8 +705,9 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
/* Enable L1-Active */
iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG,
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
}
void iwl_trans_pcie_tx_reset(struct iwl_trans *trans)