Merge git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
This commit is contained in:
commit
e9c6531677
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
/* ... 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 */
|
||||
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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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), \
|
||||
#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_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)
|
||||
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
|
||||
|
|
|
@ -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_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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
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->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;
|
||||
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",
|
||||
"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,9 +467,22 @@ 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;
|
||||
|
|
|
@ -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,43 +115,58 @@ 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)) {
|
||||
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)) {
|
||||
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:
|
||||
/*
|
||||
|
|
|
@ -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,27 +1372,21 @@ 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)
|
||||
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))
|
||||
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,
|
||||
struct ieee80211_vif *vif,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -270,6 +266,8 @@ struct iwl_mvm_vif_bf_data {
|
|||
* should get quota etc.
|
||||
* @monitor_active: indicates that monitor context is configured, and that the
|
||||
* 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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -185,6 +185,7 @@ 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.
|
||||
*/
|
||||
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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -74,15 +74,11 @@
|
|||
|
||||
#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;
|
||||
|
||||
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",
|
||||
|
@ -105,8 +101,9 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
|
|||
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 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;
|
||||
struct iwl_power_constraint *power_iterator = _data;
|
||||
|
||||
switch (ieee80211_vif_type_p2p(vif)) {
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
break;
|
||||
|
||||
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_get_global_constraint(struct iwl_mvm *mvm,
|
||||
struct iwl_power_constraint *constraint)
|
||||
{
|
||||
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_atomic(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
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;
|
||||
|
||||
mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
ret = iwl_mvm_power_mac_update_mode(mvm, vif);
|
||||
WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
|
||||
}
|
||||
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
|
||||
return 0;
|
||||
|
||||
static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
bool assign)
|
||||
{
|
||||
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) {
|
||||
int ret = _iwl_mvm_power_update_device(mvm, assign);
|
||||
mvm->ps_prevented = assign;
|
||||
WARN_ONCE(ret, "Failed to update power device state\n");
|
||||
ret = iwl_mvm_power_update_device(mvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ieee80211_iterate_active_interfaces(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_power_binding_iterator,
|
||||
mvm);
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
return;
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (mvmvif->ap_ibss_active)
|
||||
data->n_interfaces[id]++;
|
||||
break;
|
||||
return;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (mvmvif->monitor_active)
|
||||
data->n_interfaces[id]++;
|
||||
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];
|
||||
}
|
||||
|
||||
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;
|
||||
if (num_active_macs) {
|
||||
quota = IWL_MVM_MAX_QUOTA / num_active_macs;
|
||||
quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,10 +511,15 @@ 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");
|
||||
|
||||
/* 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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,6 +133,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
|
|||
*/
|
||||
|
||||
/* Disable L0S exit timer (platform NMI Work/Around) */
|
||||
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);
|
||||
|
||||
|
@ -203,11 +205,13 @@ 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);
|
||||
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 */
|
||||
|
@ -215,7 +219,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
|
|||
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);
|
||||
iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
|
||||
APMG_RTC_INT_STT_RFKILL);
|
||||
}
|
||||
|
||||
set_bit(STATUS_DEVICE_ENABLED, &trans->status);
|
||||
|
||||
|
@ -273,6 +279,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
|
|||
|
||||
spin_unlock(&trans_pcie->irq_lock);
|
||||
|
||||
if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
|
||||
iwl_pcie_set_pwr(trans, false);
|
||||
|
||||
iwl_op_mode_nic_config(trans->op_mode);
|
||||
|
@ -435,70 +442,97 @@ 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)++;
|
||||
}
|
||||
|
||||
for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
|
||||
last_read_idx = i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (i == (*first_ucode_section) + 1)
|
||||
/* set CPU to started */
|
||||
iwl_trans_set_bits_mask(trans,
|
||||
iwl_set_bits_prph(trans,
|
||||
CSR_UCODE_LOAD_STATUS_ADDR,
|
||||
CSR_CPU_STATUS_LOADING_STARTED << shift_param,
|
||||
1);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
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,
|
||||
iwl_set_bits_prph(trans,
|
||||
CSR_UCODE_LOAD_STATUS_ADDR,
|
||||
CSR_CPU_STATUS_LOADING_COMPLETED
|
||||
<< shift_param,
|
||||
1);
|
||||
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 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)++;
|
||||
}
|
||||
|
||||
for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
|
||||
last_read_idx = i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
if (ret < 0) {
|
||||
IWL_ERR(trans, "Time out on secure boot process\n");
|
||||
return ret;
|
||||
}
|
||||
*first_ucode_section = last_read_idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -506,7 +540,8 @@ static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu)
|
|||
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,52 +553,67 @@ 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) {
|
||||
/* set CPU2 header address */
|
||||
iwl_write_prph(trans,
|
||||
LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR,
|
||||
LMPM_SECURE_CPU2_HDR_MEM_SPACE);
|
||||
|
||||
/* 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 (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) {
|
||||
/* set CPU2 for secure protocol */
|
||||
ret = iwl_pcie_secure_set(trans, 2);
|
||||
if (ret)
|
||||
/* 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;
|
||||
ssize_t ret;
|
||||
|
||||
ret = pos = iwl_dump_fh(trans, &buf);
|
||||
if (buf) {
|
||||
ret = simple_read_from_buffer(user_buf,
|
||||
count, ppos, buf, pos);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -705,6 +705,7 @@ 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 */
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue