diff options
author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2025-04-21 21:51:31 +0000 |
---|---|---|
committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2025-04-21 21:57:06 +0000 |
commit | 9104a97c9be3b44f22f9ea4f8965b472acf590b8 (patch) | |
tree | 03fee902084487126421ab93f6764b082a60d6af | |
parent | 5d6ad179ba1337044d054d06596e2169952660b6 (diff) |
-rw-r--r-- | cfg/ax210.c | 46 | ||||
-rw-r--r-- | cfg/bz.c | 47 | ||||
-rw-r--r-- | cfg/dr.c | 167 | ||||
-rw-r--r-- | cfg/sc.c | 40 | ||||
-rw-r--r-- | fw/acpi.c | 200 | ||||
-rw-r--r-- | fw/acpi.h | 49 | ||||
-rw-r--r-- | fw/api/binding.h | 2 | ||||
-rw-r--r-- | fw/api/coex.h | 29 | ||||
-rw-r--r-- | fw/api/commands.h | 15 | ||||
-rw-r--r-- | fw/api/context.h | 3 | ||||
-rw-r--r-- | fw/api/d3.h | 69 | ||||
-rw-r--r-- | fw/api/datapath.h | 2 | ||||
-rw-r--r-- | fw/api/dbg-tlv.h | 4 | ||||
-rw-r--r-- | fw/api/debug.h | 6 | ||||
-rw-r--r-- | fw/api/location.h | 30 | ||||
-rw-r--r-- | fw/api/mac-cfg.h | 115 | ||||
-rw-r--r-- | fw/api/mac.h | 8 | ||||
-rw-r--r-- | fw/api/nvm-reg.h | 57 | ||||
-rw-r--r-- | fw/api/offload.h | 8 | ||||
-rw-r--r-- | fw/api/power.h | 87 | ||||
-rw-r--r-- | fw/api/scan.h | 46 | ||||
-rw-r--r-- | fw/api/stats.h | 14 | ||||
-rw-r--r-- | fw/api/tdls.h | 8 | ||||
-rw-r--r-- | fw/api/time-event.h | 16 | ||||
-rw-r--r-- | fw/api/tx.h | 44 | ||||
-rw-r--r-- | fw/dbg.c | 86 | ||||
-rw-r--r-- | fw/dbg.h | 13 | ||||
-rw-r--r-- | fw/debugfs.c | 42 | ||||
-rw-r--r-- | fw/dump.c | 30 | ||||
-rw-r--r-- | fw/error-dump.h | 2 | ||||
-rw-r--r-- | fw/file.h | 11 | ||||
-rw-r--r-- | fw/img.c | 2 | ||||
-rw-r--r-- | fw/img.h | 3 | ||||
-rw-r--r-- | fw/init.c | 4 | ||||
-rw-r--r-- | fw/paging.c | 2 | ||||
-rw-r--r-- | fw/regulatory.c | 78 | ||||
-rw-r--r-- | fw/regulatory.h | 67 | ||||
-rw-r--r-- | fw/uefi.c | 132 | ||||
-rw-r--r-- | fw/uefi.h | 56 | ||||
-rw-r--r-- | iwl-config.h | 17 | ||||
-rw-r--r-- | iwl-context-info-gen3.h | 40 | ||||
-rw-r--r-- | iwl-context-info.h | 32 | ||||
-rw-r--r-- | iwl-csr.h | 17 | ||||
-rw-r--r-- | iwl-drv.c | 130 | ||||
-rw-r--r-- | iwl-drv.h | 5 | ||||
-rw-r--r-- | iwl-io.c | 2 | ||||
-rw-r--r-- | iwl-nvm-parse.c | 6 | ||||
-rw-r--r-- | iwl-op-mode.h | 79 | ||||
-rw-r--r-- | iwl-prph.h | 5 | ||||
-rw-r--r-- | iwl-trans.c | 225 | ||||
-rw-r--r-- | iwl-trans.h | 108 | ||||
-rw-r--r-- | iwl-utils.c | 85 | ||||
-rw-r--r-- | iwl-utils.h | 56 | ||||
-rw-r--r-- | mvm/binding.c | 7 | ||||
-rw-r--r-- | mvm/coex.c | 82 | ||||
-rw-r--r-- | mvm/constants.h | 8 | ||||
-rw-r--r-- | mvm/d3.c | 276 | ||||
-rw-r--r-- | mvm/debugfs-vif.c | 66 | ||||
-rw-r--r-- | mvm/debugfs.c | 84 | ||||
-rw-r--r-- | mvm/ftm-initiator.c | 7 | ||||
-rw-r--r-- | mvm/ftm-responder.c | 2 | ||||
-rw-r--r-- | mvm/fw.c | 142 | ||||
-rw-r--r-- | mvm/link.c | 50 | ||||
-rw-r--r-- | mvm/mac-ctxt.c | 173 | ||||
-rw-r--r-- | mvm/mac80211.c | 253 | ||||
-rw-r--r-- | mvm/mld-key.c | 14 | ||||
-rw-r--r-- | mvm/mld-mac80211.c | 215 | ||||
-rw-r--r-- | mvm/mld-sta.c | 69 | ||||
-rw-r--r-- | mvm/mvm.h | 105 | ||||
-rw-r--r-- | mvm/nvm.c | 10 | ||||
-rw-r--r-- | mvm/offloading.c | 2 | ||||
-rw-r--r-- | mvm/ops.c | 290 | ||||
-rw-r--r-- | mvm/phy-ctxt.c | 8 | ||||
-rw-r--r-- | mvm/power.c | 4 | ||||
-rw-r--r-- | mvm/rs-fw.c | 13 | ||||
-rw-r--r-- | mvm/rx.c | 47 | ||||
-rw-r--r-- | mvm/rxmq.c | 57 | ||||
-rw-r--r-- | mvm/scan.c | 69 | ||||
-rw-r--r-- | mvm/sta.c | 149 | ||||
-rw-r--r-- | mvm/sta.h | 13 | ||||
-rw-r--r-- | mvm/tdls.c | 20 | ||||
-rw-r--r-- | mvm/time-event.c | 26 | ||||
-rw-r--r-- | mvm/tx.c | 127 | ||||
-rw-r--r-- | mvm/utils.c | 70 | ||||
-rw-r--r-- | pcie/ctxt-info-gen3.c | 8 | ||||
-rw-r--r-- | pcie/drv.c | 108 | ||||
-rw-r--r-- | pcie/internal.h | 11 | ||||
-rw-r--r-- | pcie/rx.c | 6 | ||||
-rw-r--r-- | pcie/trans-gen2.c | 30 | ||||
-rw-r--r-- | pcie/trans.c | 313 | ||||
-rw-r--r-- | pcie/tx-gen2.c | 10 | ||||
-rw-r--r-- | pcie/tx.c | 31 | ||||
-rw-r--r-- | tests/devinfo.c | 2 |
93 files changed, 3750 insertions, 1674 deletions
diff --git a/cfg/ax210.c b/cfg/ax210.c index 975e8aed1526..dcba1a5d793b 100644 --- a/cfg/ax210.c +++ b/cfg/ax210.c @@ -31,40 +31,21 @@ #define IWL_SO_A_GF_A_FW_PRE "iwlwifi-so-a0-gf-a0" #define IWL_TY_A_GF_A_FW_PRE "iwlwifi-ty-a0-gf-a0" #define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0" -#define IWL_SO_A_MR_A_FW_PRE "iwlwifi-so-a0-mr-a0" #define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0" #define IWL_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0" #define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0" -#define IWL_MA_A_MR_A_FW_PRE "iwlwifi-ma-a0-mr-a0" #define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0" #define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0" #define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0" -#define IWL_MA_B_MR_A_FW_PRE "iwlwifi-ma-b0-mr-a0" #define IWL_SO_A_JF_B_MODULE_FIRMWARE(api) \ IWL_SO_A_JF_B_FW_PRE "-" __stringify(api) ".ucode" #define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \ IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SO_A_GF_A_MODULE_FIRMWARE(api) \ - IWL_SO_A_GF_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_TY_A_GF_A_MODULE_FIRMWARE(api) \ - IWL_TY_A_GF_A_FW_PRE "-" __stringify(api) ".ucode" #define IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_A_GF_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_A_GF_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_A_GF4_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_A_MR_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_A_MR_A_FW_PRE "-" __stringify(api) ".ucode" #define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_B_GF_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_B_GF_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_B_GF4_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_B_GF4_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_B_MR_A_FW_MODULE_FIRMWARE(api) \ - IWL_MA_B_MR_A_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_base_params iwl_ax210_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, @@ -267,13 +248,6 @@ const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long = { .trans.low_latency_xtal = true, }; -const struct iwl_cfg iwl_cfg_so_a0_ms_a0 = { - .fw_name_pre = IWL_SO_A_MR_A_FW_PRE, - .uhb_supported = false, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - const struct iwl_cfg iwl_cfg_ma = { .fw_name_mac = "ma", .uhb_supported = true, @@ -289,19 +263,11 @@ const struct iwl_cfg iwl_cfg_so_a0_hr_a0 = { MODULE_FIRMWARE(IWL_SO_A_JF_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SO_A_GF_A_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_TY_A_GF_A_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); +IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); MODULE_FIRMWARE(IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_A_GF_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_A_GF4_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_A_MR_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); +IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX); MODULE_FIRMWARE(IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_B_GF_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_B_GF4_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_B_MR_A_FW_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); - -MODULE_FIRMWARE("iwlwifi-so-a0-gf-a0.pnvm"); -MODULE_FIRMWARE("iwlwifi-so-a0-gf4-a0.pnvm"); -MODULE_FIRMWARE("iwlwifi-ty-a0-gf-a0.pnvm"); -MODULE_FIRMWARE("iwlwifi-ma-b0-gf-a0.pnvm"); -MODULE_FIRMWARE("iwlwifi-ma-b0-gf4-a0.pnvm"); +IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX); @@ -10,10 +10,10 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 92 +#define IWL_BZ_UCODE_API_MAX 96 /* Lowest firmware API version supported */ -#define IWL_BZ_UCODE_API_MIN 90 +#define IWL_BZ_UCODE_API_MIN 92 /* NVM versions */ #define IWL_BZ_NVM_VERSION 0x0a1d @@ -37,20 +37,6 @@ #define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \ IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BZ_A_GF_A_MODULE_FIRMWARE(api) \ - IWL_BZ_A_GF_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BZ_A_GF4_A_MODULE_FIRMWARE(api) \ - IWL_BZ_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BZ_A_FM_B_MODULE_FIRMWARE(api) \ - IWL_BZ_A_FM_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BZ_A_FM_C_MODULE_FIRMWARE(api) \ - IWL_BZ_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BZ_A_FM4_B_MODULE_FIRMWARE(api) \ - IWL_BZ_A_FM4_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_GL_B_FM_B_MODULE_FIRMWARE(api) \ - IWL_GL_B_FM_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_GL_C_FM_C_MODULE_FIRMWARE(api) \ - IWL_GL_C_FM_C_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_base_params iwl_bz_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, @@ -148,8 +134,20 @@ const struct iwl_cfg_trans_params iwl_bz_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; +const struct iwl_cfg_trans_params iwl_gl_trans_cfg = { + .device_family = IWL_DEVICE_FAMILY_BZ, + .base_params = &iwl_bz_base_params, + .mq_rx_supported = true, + .rf_id = true, + .gen2 = true, + .umac_prph_offset = 0x300000, + .xtal_latency = 12000, + .low_latency_xtal = true, +}; + const char iwl_bz_name[] = "Intel(R) TBD Bz device"; const char iwl_fm_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; +const char iwl_wh_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz"; const char iwl_gl_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; const char iwl_mtp_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; @@ -169,14 +167,11 @@ const struct iwl_cfg iwl_cfg_gl = { .num_rbds = IWL_NUM_RBDS_BZ_EHT, }; - MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BZ_A_FM_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BZ_A_FM_C_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BZ_A_FM4_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_GL_B_FM_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_GL_C_FM_C_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); - -MODULE_FIRMWARE("iwlwifi-gl-c0-fm-c0.pnvm"); +IWL_FW_AND_PNVM(IWL_BZ_A_GF_A_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_BZ_A_GF4_A_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_API_MAX); diff --git a/cfg/dr.c b/cfg/dr.c new file mode 100644 index 000000000000..ab7c0f8d54f4 --- /dev/null +++ b/cfg/dr.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include <linux/module.h> +#include <linux/stringify.h> +#include "iwl-config.h" +#include "iwl-prph.h" +#include "fw/api/txq.h" + +/* Highest firmware API version supported */ +#define IWL_DR_UCODE_API_MAX 96 + +/* Lowest firmware API version supported */ +#define IWL_DR_UCODE_API_MIN 96 + +/* NVM versions */ +#define IWL_DR_NVM_VERSION 0x0a1d + +/* Memory offsets and lengths */ +#define IWL_DR_DCCM_OFFSET 0x800000 /* LMAC1 */ +#define IWL_DR_DCCM_LEN 0x10000 /* LMAC1 */ +#define IWL_DR_DCCM2_OFFSET 0x880000 +#define IWL_DR_DCCM2_LEN 0x8000 +#define IWL_DR_SMEM_OFFSET 0x400000 +#define IWL_DR_SMEM_LEN 0xD0000 + +#define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0" +#define IWL_BR_A_PET_A_FW_PRE "iwlwifi-br-a0-petc-a0" +#define IWL_BR_A_PE_A_FW_PRE "iwlwifi-br-a0-pe-a0" + +#define IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(api) \ + IWL_DR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(api) \ + IWL_BR_A_PET_A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(api) \ + IWL_BR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode" + +static const struct iwl_base_params iwl_dr_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, + .num_of_queues = 512, + .max_tfd_queue_size = 65536, + .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, +}; + +#define IWL_DEVICE_DR_COMMON \ + .ucode_api_max = IWL_DR_UCODE_API_MAX, \ + .ucode_api_min = IWL_DR_UCODE_API_MIN, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = 10, \ + .non_shared_ant = ANT_B, \ + .dccm_offset = IWL_DR_DCCM_OFFSET, \ + .dccm_len = IWL_DR_DCCM_LEN, \ + .dccm2_offset = IWL_DR_DCCM2_OFFSET, \ + .dccm2_len = IWL_DR_DCCM2_LEN, \ + .smem_offset = IWL_DR_SMEM_OFFSET, \ + .smem_len = IWL_DR_SMEM_LEN, \ + .apmg_not_supported = true, \ + .trans.mq_rx_supported = true, \ + .vht_mu_mimo_supported = true, \ + .mac_addr_from_csr = 0x30, \ + .nvm_ver = IWL_DR_NVM_VERSION, \ + .trans.rf_id = true, \ + .trans.gen2 = true, \ + .nvm_type = IWL_NVM_EXT, \ + .dbgc_supported = true, \ + .min_umac_error_event_table = 0xD0000, \ + .d3_debug_data_base_addr = 0x401000, \ + .d3_debug_data_length = 60 * 1024, \ + .mon_smem_regs = { \ + .write_ptr = { \ + .addr = LDBG_M2S_BUF_WPTR, \ + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ + }, \ + .cycle_cnt = { \ + .addr = LDBG_M2S_BUF_WRAP_CNT, \ + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ + }, \ + }, \ + .trans.umac_prph_offset = 0x300000, \ + .trans.device_family = IWL_DEVICE_FAMILY_DR, \ + .trans.base_params = &iwl_dr_base_params, \ + .min_txq_size = 128, \ + .gp2_reg_addr = 0xd02c68, \ + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \ + .mon_dram_regs = { \ + .write_ptr = { \ + .addr = DBGC_CUR_DBGBUF_STATUS, \ + .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \ + }, \ + .cycle_cnt = { \ + .addr = DBGC_DBGBUF_WRAP_AROUND, \ + .mask = 0xffffffff, \ + }, \ + .cur_frag = { \ + .addr = DBGC_CUR_DBGBUF_STATUS, \ + .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ + }, \ + }, \ + .mon_dbgi_regs = { \ + .write_ptr = { \ + .addr = DBGI_SRAM_FIFO_POINTERS, \ + .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \ + }, \ + } + +#define IWL_DEVICE_DR \ + IWL_DEVICE_DR_COMMON, \ + .uhb_supported = true, \ + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ + .num_rbds = IWL_NUM_RBDS_DR_EHT, \ + .ht_params = &iwl_22000_ht_params + +/* + * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an + * A-MPDU, with additional overhead to account for processing time. + */ +#define IWL_NUM_RBDS_DR_EHT (512 * 16) + +const struct iwl_cfg_trans_params iwl_dr_trans_cfg = { + .device_family = IWL_DEVICE_FAMILY_DR, + .base_params = &iwl_dr_base_params, + .mq_rx_supported = true, + .rf_id = true, + .gen2 = true, + .integrated = true, + .umac_prph_offset = 0x300000, + .xtal_latency = 12000, + .low_latency_xtal = true, + .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, +}; + +const char iwl_dr_name[] = "Intel(R) TBD Dr device"; + +const struct iwl_cfg iwl_cfg_dr = { + .fw_name_mac = "dr", + IWL_DEVICE_DR, +}; + +const struct iwl_cfg_trans_params iwl_br_trans_cfg = { + .device_family = IWL_DEVICE_FAMILY_DR, + .base_params = &iwl_dr_base_params, + .mq_rx_supported = true, + .rf_id = true, + .gen2 = true, + .integrated = true, + .umac_prph_offset = 0x300000, + .xtal_latency = 12000, + .low_latency_xtal = true, + .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, +}; + +const char iwl_br_name[] = "Intel(R) TBD Br device"; + +const struct iwl_cfg iwl_cfg_br = { + .fw_name_mac = "br", + IWL_DEVICE_DR, +}; + +MODULE_FIRMWARE(IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); @@ -10,10 +10,10 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 92 +#define IWL_SC_UCODE_API_MAX 96 /* Lowest firmware API version supported */ -#define IWL_SC_UCODE_API_MIN 90 +#define IWL_SC_UCODE_API_MIN 92 /* NVM versions */ #define IWL_SC_NVM_VERSION 0x0a1d @@ -38,28 +38,10 @@ #define IWL_SC2F_A_FM_C_FW_PRE "iwlwifi-sc2f-a0-fm-c0" #define IWL_SC2F_A_WH_A_FW_PRE "iwlwifi-sc2f-a0-wh-a0" -#define IWL_SC_A_FM_B_FW_MODULE_FIRMWARE(api) \ - IWL_SC_A_FM_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC_A_FM_C_FW_MODULE_FIRMWARE(api) \ - IWL_SC_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" #define IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_HR_A_FW_PRE "-" __stringify(api) ".ucode" #define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC_A_GF_A_FW_MODULE_FIRMWARE(api) \ - IWL_SC_A_GF_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC_A_GF4_A_FW_MODULE_FIRMWARE(api) \ - IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC_A_WH_A_FW_MODULE_FIRMWARE(api) \ - IWL_SC_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC2_A_FM_C_FW_MODULE_FIRMWARE(api) \ - IWL_SC2_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC2_A_WH_A_FW_MODULE_FIRMWARE(api) \ - IWL_SC2_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC2F_A_FM_C_FW_MODULE_FIRMWARE(api) \ - IWL_SC2F_A_FM_C_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SC2F_A_WH_A_FW_MODULE_FIRMWARE(api) \ - IWL_SC2F_A_WH_A_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_base_params iwl_sc_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, @@ -181,14 +163,14 @@ const struct iwl_cfg iwl_cfg_sc2f = { IWL_DEVICE_SC, }; -MODULE_FIRMWARE(IWL_SC_A_FM_B_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC_A_FM_C_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +IWL_FW_AND_PNVM(IWL_SC_A_FM_B_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX); MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC_A_GF_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC_A_GF4_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC2_A_FM_C_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC2_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC2F_A_FM_C_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SC2F_A_WH_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); +IWL_FW_AND_PNVM(IWL_SC_A_GF_A_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC_A_GF4_A_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC_A_WH_A_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC2_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC2_A_WH_A_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC2F_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SC2F_A_WH_A_FW_PRE, IWL_SC_UCODE_API_MAX); diff --git a/fw/acpi.c b/fw/acpi.c index 8c8880b44827..efa7b673ebc7 100644 --- a/fw/acpi.c +++ b/fw/acpi.c @@ -79,9 +79,9 @@ static void *iwl_acpi_get_object(struct device *dev, acpi_string method) * method (DSM) interface. The returned acpi object must be freed by calling * function. */ -static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func, - union acpi_object *args, - const guid_t *guid) +union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev, + int func, union acpi_object *args, + const guid_t *guid) { union acpi_object *obj; @@ -108,7 +108,7 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, size_t expected_size) { union acpi_object *obj; - int ret = 0; + int ret; obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid); if (IS_ERR(obj)) { @@ -123,8 +123,10 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, } else if (obj->type == ACPI_TYPE_BUFFER) { __le64 le_value = 0; - if (WARN_ON_ONCE(expected_size > sizeof(le_value))) - return -EINVAL; + if (WARN_ON_ONCE(expected_size > sizeof(le_value))) { + ret = -EINVAL; + goto out; + } /* if the buffer size doesn't match the expected size */ if (obj->buffer.length != expected_size) @@ -145,8 +147,9 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, } IWL_DEBUG_DEV_RADIO(dev, - "ACPI: DSM method evaluated: func=%d, ret=%d\n", - func, ret); + "ACPI: DSM method evaluated: func=%d, value=%lld\n", + func, *value); + ret = 0; out: ACPI_FREE(obj); return ret; @@ -259,13 +262,14 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *tas_data) { union acpi_object *wifi_pkg, *data; - int ret, tbl_rev, i, block_list_size, enabled; + int ret, tbl_rev, block_list_size, enabled; + u32 tas_selection; data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - /* try to read wtas table revision 1 or revision 0*/ + /* try to read wtas table */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WTAS_WIFI_DATA_SIZE, &tbl_rev); @@ -274,27 +278,23 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, goto out_free; } - if (tbl_rev == 1 && wifi_pkg->package.elements[1].type == - ACPI_TYPE_INTEGER) { - u32 tas_selection = - (u32)wifi_pkg->package.elements[1].integer.value; - - enabled = iwl_parse_tas_selection(fwrt, tas_data, - tas_selection); - - } else if (tbl_rev == 0 && - wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { - enabled = !!wifi_pkg->package.elements[1].integer.value; - } else { + if (tbl_rev < 0 || tbl_rev > 2 || + wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { ret = -EINVAL; goto out_free; } - if (!enabled) { - IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); - ret = 0; - goto out_free; - } + tas_selection = (u32)wifi_pkg->package.elements[1].integer.value; + enabled = tas_selection & IWL_WTAS_ENABLED_MSK; + + IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", + tas_selection); + tas_data->table_source = BIOS_SOURCE_ACPI; + tas_data->table_revision = tbl_rev; + tas_data->tas_selection = tas_selection; + + IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n", + enabled ? "is" : "not"); IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev); if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER || @@ -305,13 +305,14 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, ret = -EINVAL; goto out_free; } + block_list_size = wifi_pkg->package.elements[2].integer.value; - tas_data->block_list_size = cpu_to_le32(block_list_size); + tas_data->block_list_size = block_list_size; IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); - for (i = 0; i < block_list_size; i++) { - u32 country; + for (int i = 0; i < block_list_size; i++) { + u16 country; if (wifi_pkg->package.elements[3 + i].type != ACPI_TYPE_INTEGER) { @@ -322,11 +323,11 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, } country = wifi_pkg->package.elements[3 + i].integer.value; - tas_data->block_list_array[i] = cpu_to_le32(country); + tas_data->block_list_array[i] = country; IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); } - ret = 1; + ret = enabled; out_free: kfree(data); return ret; @@ -357,6 +358,11 @@ int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) } mcc_val = wifi_pkg->package.elements[1].integer.value; + if (mcc_val != BIOS_MCC_CHINA) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n"); + goto out_free; + } mcc[0] = (mcc_val >> 8) & 0xff; mcc[1] = mcc_val & 0xff; @@ -424,38 +430,28 @@ out_free: return ret; } -static int iwl_acpi_sar_set_profile(union acpi_object *table, - struct iwl_sar_profile *profile, - bool enabled, u8 num_chains, - u8 num_sub_bands) +static int +iwl_acpi_parse_chains_table(union acpi_object *table, + struct iwl_sar_profile_chain *chains, + u8 num_chains, u8 num_sub_bands) { - int i, j, idx = 0; - - /* - * The table from ACPI is flat, but we store it in a - * structured array. - */ - for (i = 0; i < BIOS_SAR_MAX_CHAINS_PER_PROFILE; i++) { - for (j = 0; j < BIOS_SAR_MAX_SUB_BANDS_NUM; j++) { + for (u8 chain = 0; chain < num_chains; chain++) { + for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM; + subband++) { /* if we don't have the values, use the default */ - if (i >= num_chains || j >= num_sub_bands) { - profile->chains[i].subbands[j] = 0; + if (subband >= num_sub_bands) { + chains[chain].subbands[subband] = 0; + } else if (table->type != ACPI_TYPE_INTEGER || + table->integer.value > U8_MAX) { + return -EINVAL; } else { - if (table[idx].type != ACPI_TYPE_INTEGER || - table[idx].integer.value > U8_MAX) - return -EINVAL; - - profile->chains[i].subbands[j] = - table[idx].integer.value; - - idx++; + chains[chain].subbands[subband] = + table->integer.value; + table++; } } } - /* Only if all values were valid can the profile be enabled */ - profile->enabled = enabled; - return 0; } @@ -538,9 +534,11 @@ read_table: /* The profile from WRDS is officially profile 1, but goes * into sar_profiles[0] (because we don't have a profile 0). */ - ret = iwl_acpi_sar_set_profile(table, &fwrt->sar_profiles[0], - flags & IWL_SAR_ENABLE_MSK, - num_chains, num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains, + num_chains, num_sub_bands); + if (!ret && flags & IWL_SAR_ENABLE_MSK) + fwrt->sar_profiles[0].enabled = true; + out_free: kfree(data); return ret; @@ -552,7 +550,7 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) bool enabled; int i, n_profiles, tbl_rev, pos; int ret = 0; - u8 num_chains, num_sub_bands; + u8 num_sub_bands; data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD); if (IS_ERR(data)) @@ -568,7 +566,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV2; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2; goto read_table; @@ -584,7 +581,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV1; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1; goto read_table; @@ -600,7 +596,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV0; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0; goto read_table; @@ -632,23 +627,54 @@ read_table: /* the tables start at element 3 */ pos = 3; + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1); + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0); + + /* parse non-cdb chains for all profiles */ for (i = 0; i < n_profiles; i++) { union acpi_object *table = &wifi_pkg->package.elements[pos]; + /* The EWRD profiles officially go from 2 to 4, but we * save them in sar_profiles[1-3] (because we don't * have profile 0). So in the array we start from 1. */ - ret = iwl_acpi_sar_set_profile(table, - &fwrt->sar_profiles[i + 1], - enabled, num_chains, - num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, + fwrt->sar_profiles[i + 1].chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); if (ret < 0) - break; + goto out_free; /* go to the next table */ - pos += num_chains * num_sub_bands; + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; } + /* non-cdb table revisions */ + if (tbl_rev < 2) + goto set_enabled; + + /* parse cdb chains for all profiles */ + for (i = 0; i < n_profiles; i++) { + struct iwl_sar_profile_chain *chains; + union acpi_object *table; + + table = &wifi_pkg->package.elements[pos]; + chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0]; + ret = iwl_acpi_parse_chains_table(table, + chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); + if (ret < 0) + goto out_free; + + /* go to the next table */ + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; + } + +set_enabled: + for (i = 0; i < n_profiles; i++) + fwrt->sar_profiles[i + 1].enabled = enabled; + out_free: kfree(data); return ret; @@ -998,3 +1024,37 @@ out_free: kfree(data); return ret; } + +int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) +{ + union acpi_object *wifi_pkg, *data; + int ret = -ENOENT; + int tbl_rev; + + data = iwl_acpi_get_object(fwrt->dev, ACPI_DSBR_METHOD); + if (IS_ERR(data)) + return ret; + + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_DSBR_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg)) + goto out_free; + + if (tbl_rev != ACPI_DSBR_WIFI_DATA_REV) { + IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI DSBR revision:%d\n", + tbl_rev); + goto out_free; + } + + if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) + goto out_free; + + *value = wifi_pkg->package.elements[1].integer.value; + IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from ACPI value: 0x%x\n", + *value); + ret = 0; +out_free: + kfree(data); + return ret; +} diff --git a/fw/acpi.h b/fw/acpi.h index bb88398a6987..e50b93472dd2 100644 --- a/fw/acpi.h +++ b/fw/acpi.h @@ -28,6 +28,7 @@ #define ACPI_WPFC_METHOD "WPFC" #define ACPI_GLAI_METHOD "GLAI" #define ACPI_WBEM_METHOD "WBEM" +#define ACPI_DSBR_METHOD "DSBR" #define ACPI_WIFI_DOMAIN (0x07) @@ -76,6 +77,13 @@ #define ACPI_WBEM_WIFI_DATA_SIZE 2 /* * One element for domain type, + * and one for DSBR response data + */ +#define ACPI_DSBR_WIFI_DATA_SIZE 2 +#define ACPI_DSBR_WIFI_DATA_REV 1 + +/* + * One element for domain type, * and one for the status */ #define ACPI_GLAI_WIFI_DATA_SIZE 2 @@ -101,6 +109,30 @@ #define ACPI_DSM_REV 0 +#define DSM_INTERNAL_FUNC_GET_PLAT_INFO 1 +/* TBD: VPRO is BIT(0) in the result, but what's the result? */ + +#define DSM_INTERNAL_FUNC_PRODUCT_RESET 2 + +/* DSM_INTERNAL_FUNC_PRODUCT_RESET - product reset (aka "PLDR") */ +enum iwl_dsm_internal_product_reset_cmds { + DSM_INTERNAL_PLDR_CMD_GET_MODE = 1, + DSM_INTERNAL_PLDR_CMD_SET_MODE = 2, + DSM_INTERNAL_PLDR_CMD_GET_STATUS = 3, +}; + +enum iwl_dsm_internal_product_reset_mode { + DSM_INTERNAL_PLDR_MODE_EN_PROD_RESET = BIT(0), + DSM_INTERNAL_PLDR_MODE_EN_WIFI_FLR = BIT(1), + DSM_INTERNAL_PLDR_MODE_EN_BT_OFF_ON = BIT(2), +}; + +struct iwl_dsm_internal_product_reset_cmd { + /* cmd is from enum iwl_dsm_internal_product_reset_cmds */ + u16 cmd; + u16 value; +} __packed; + #define IWL_ACPI_WBEM_REV0_MASK (BIT(0) | BIT(1)) #define IWL_ACPI_WBEM_REVISION 0 @@ -110,6 +142,10 @@ struct iwl_fw_runtime; extern const guid_t iwl_guid; +union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev, + int func, union acpi_object *args, + const guid_t *guid); + /** * iwl_acpi_get_mcc - read MCC from ACPI, if available * @@ -153,10 +189,14 @@ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value); int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value); + +int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); + #else /* CONFIG_ACPI */ -static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, - int func, union acpi_object *args) +static inline union acpi_object * +iwl_acpi_get_dsm_object(struct device *dev, int rev, int func, + union acpi_object *args, const guid_t *guid) { return ERR_PTR(-ENOENT); } @@ -221,6 +261,11 @@ static inline int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value) { return -ENOENT; } + +static inline int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) +{ + return -ENOENT; +} #endif /* CONFIG_ACPI */ #endif /* __iwl_fw_acpi__ */ diff --git a/fw/api/binding.h b/fw/api/binding.h index 2397fdc37fc5..9b942c4aabd9 100644 --- a/fw/api/binding.h +++ b/fw/api/binding.h @@ -56,8 +56,6 @@ struct iwl_binding_cmd { } __packed; /* BINDING_CMD_API_S_VER_2 */ #define IWL_BINDING_CMD_SIZE_V1 sizeof(struct iwl_binding_cmd_v1) -#define IWL_LMAC_24G_INDEX 0 -#define IWL_LMAC_5G_INDEX 1 /* The maximal number of fragments in the FW's schedule session */ #define IWL_MVM_MAX_QUOTA 128 diff --git a/fw/api/coex.h b/fw/api/coex.h index b97a43353779..ddc84430d895 100644 --- a/fw/api/coex.h +++ b/fw/api/coex.h @@ -95,7 +95,7 @@ enum iwl_bt_ci_compliance { }; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ /** - * struct iwl_bt_coex_profile_notif - notification about BT coex + * struct iwl_bt_coex_prof_old_notif - notification about BT coex * @mbox_msg: message from BT to WiFi * @msg_idx: the index of the message * @bt_ci_compliance: enum %iwl_bt_ci_compliance @@ -110,7 +110,7 @@ enum iwl_bt_ci_compliance { * @wifi_loss_mid_high_rssi: The predicted lost WiFi rate (% of air time that * BT is utilizing) when the RSSI is mid/high (>= -65 dBm) */ -struct iwl_bt_coex_profile_notif { +struct iwl_bt_coex_prof_old_notif { __le32 mbox_msg[4]; __le32 msg_idx; __le32 bt_ci_compliance; @@ -126,4 +126,29 @@ struct iwl_bt_coex_profile_notif { * BT_COEX_PROFILE_NTFY_API_S_VER_5 */ +/** + * enum iwl_bt_coex_subcmd_ids - coex configuration command IDs + */ +enum iwl_bt_coex_subcmd_ids { + /** + *@PROFILE_NOTIF: &struct iwl_bt_coex_profile_notif + */ + PROFILE_NOTIF = 0xFF, +}; + +#define COEX_NUM_BAND 3 +#define COEX_NUM_CHAINS 2 + +/** + * struct iwl_bt_coex_profile_notif - notification about BT coex + * @wifi_loss_low_rssi: The predicted lost WiFi rate (% of air time that BT is + * utilizing) when the RSSI is low (<= -65 dBm) + * @wifi_loss_mid_high_rssi: The predicted lost WiFi rate (% of air time that + * BT is utilizing) when the RSSI is mid/high (>= -65 dBm) + */ +struct iwl_bt_coex_profile_notif { + u8 wifi_loss_low_rssi[COEX_NUM_BAND][COEX_NUM_CHAINS]; + u8 wifi_loss_mid_high_rssi[COEX_NUM_BAND][COEX_NUM_CHAINS]; +} __packed; /* BT_COEX_BT_PROFILE_NTF_API_S_VER_1 */ + #endif /* __iwl_fw_api_coex_h__ */ diff --git a/fw/api/commands.h b/fw/api/commands.h index 7544c4cb1a30..34a1f97653c0 100644 --- a/fw/api/commands.h +++ b/fw/api/commands.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2022, 2024 Intel Corporation */ #ifndef __iwl_fw_api_commands_h__ #define __iwl_fw_api_commands_h__ @@ -25,6 +25,8 @@ * @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids * @LOCATION_GROUP: location group, uses command IDs from * &enum iwl_location_subcmd_ids + * @BT_COEX_GROUP: bt coex group, uses command IDs from + * &enum iwl_bt_coex_subcmd_ids * @PROT_OFFLOAD_GROUP: protocol offload group, uses command IDs from * &enum iwl_prot_offload_subcmd_ids * @REGULATORY_AND_NVM_GROUP: regulatory/NVM group, uses command IDs from @@ -43,6 +45,7 @@ enum iwl_mvm_command_groups { SCAN_GROUP = 0x6, NAN_GROUP = 0x7, LOCATION_GROUP = 0x8, + BT_COEX_GROUP = 0x9, PROT_OFFLOAD_GROUP = 0xb, REGULATORY_AND_NVM_GROUP = 0xc, DEBUG_GROUP = 0xf, @@ -144,8 +147,8 @@ enum iwl_legacy_cmds { /** * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2 or * &struct iwl_tx_cmd_gen3, - * response in &struct iwl_mvm_tx_resp or - * &struct iwl_mvm_tx_resp_v3 + * response in &struct iwl_tx_resp or + * &struct iwl_tx_resp_v3 */ TX_CMD = 0x1c, @@ -398,7 +401,7 @@ enum iwl_legacy_cmds { REDUCE_TX_POWER_CMD = 0x9f, /** - * @MISSED_BEACONS_NOTIFICATION: &struct iwl_missed_beacons_notif + * @MISSED_BEACONS_NOTIFICATION: &struct iwl_missed_beacons_notif_v4 */ MISSED_BEACONS_NOTIFICATION = 0xa2, @@ -444,7 +447,7 @@ enum iwl_legacy_cmds { /** * @BA_NOTIF: - * BlockAck notification, uses &struct iwl_mvm_compressed_ba_notif + * BlockAck notification, uses &struct iwl_compressed_ba_notif * or &struct iwl_mvm_ba_notif depending on the HW */ BA_NOTIF = 0xc5, @@ -467,7 +470,7 @@ enum iwl_legacy_cmds { MARKER_CMD = 0xcb, /** - * @BT_PROFILE_NOTIFICATION: &struct iwl_bt_coex_profile_notif + * @BT_PROFILE_NOTIFICATION: &struct iwl_bt_coex_prof_old_notif */ BT_PROFILE_NOTIFICATION = 0xce, diff --git a/fw/api/context.h b/fw/api/context.h index 1fa5678c1cd6..a9fa5f054ce0 100644 --- a/fw/api/context.h +++ b/fw/api/context.h @@ -40,4 +40,7 @@ enum iwl_ctxt_action { FW_CTXT_ACTION_REMOVE, }; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ +#define IWL_LMAC_24G_INDEX 0 +#define IWL_LMAC_5G_INDEX 1 + #endif /* __iwl_fw_api_context_h__ */ diff --git a/fw/api/d3.h b/fw/api/d3.h index ffee7927cf26..c2362bc786b2 100644 --- a/fw/api/d3.h +++ b/fw/api/d3.h @@ -368,7 +368,7 @@ enum iwl_wowlan_flags { }; /** - * struct iwl_wowlan_config_cmd - WoWLAN configuration (versions 5 and 6) + * struct iwl_wowlan_config_cmd_v6 - WoWLAN configuration (versions 5 and 6) * @wakeup_filter: filter from &enum iwl_wowlan_wakeup_filters * @non_qos_seq: non-QoS sequence counter to use next. * Reserved if the struct has version >= 6. @@ -380,7 +380,7 @@ enum iwl_wowlan_flags { * @sta_id: station ID for wowlan. * @reserved: reserved */ -struct iwl_wowlan_config_cmd { +struct iwl_wowlan_config_cmd_v6 { __le32 wakeup_filter; __le16 non_qos_seq; __le16 qos_seq[8]; @@ -390,7 +390,27 @@ struct iwl_wowlan_config_cmd { u8 flags; u8 sta_id; u8 reserved; -} __packed; /* WOWLAN_CONFIG_API_S_VER_5 */ +} __packed; /* WOWLAN_CONFIG_API_S_VER_6 */ + +/** + * struct iwl_wowlan_config_cmd - WoWLAN configuration + * @wakeup_filter: filter from &enum iwl_wowlan_wakeup_filters + * @wowlan_ba_teardown_tids: bitmap of BA sessions to tear down + * @is_11n_connection: indicates HT connection + * @offloading_tid: TID reserved for firmware use + * @flags: extra flags, see &enum iwl_wowlan_flags + * @sta_id: station ID for wowlan. + * @reserved: reserved + */ +struct iwl_wowlan_config_cmd { + __le32 wakeup_filter; + u8 wowlan_ba_teardown_tids; + u8 is_11n_connection; + u8 offloading_tid; + u8 flags; + u8 sta_id; + u8 reserved[3]; +} __packed; /* WOWLAN_CONFIG_API_S_VER_7 */ #define IWL_NUM_RSC 16 #define WOWLAN_KEY_MAX_SIZE 32 @@ -890,7 +910,7 @@ struct iwl_wowlan_mlo_gtk { } __packed; /* WOWLAN_MLO_GTK_KEY_API_S_VER_1 */ /** - * struct iwl_wowlan_info_notif - WoWLAN information notification + * struct iwl_wowlan_info_notif_v4 - WoWLAN information notification * @gtk: GTK data * @igtk: IGTK data * @bigtk: BIGTK data @@ -910,7 +930,7 @@ struct iwl_wowlan_mlo_gtk { * @reserved2: reserved * @mlo_gtks: array of GTKs of size num_mlo_link_keys for version >= 4 */ -struct iwl_wowlan_info_notif { +struct iwl_wowlan_info_notif_v4 { struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; struct iwl_wowlan_igtk_status bigtk[WOWLAN_BIGTK_KEYS_NUM]; @@ -930,6 +950,45 @@ struct iwl_wowlan_info_notif { } __packed; /* WOWLAN_INFO_NTFY_API_S_VER_3, _VER_4 */ /** + * struct iwl_wowlan_info_notif - WoWLAN information notification + * @gtk: GTK data + * @igtk: IGTK data + * @bigtk: BIGTK data + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched patterns + * @qos_seq_ctr: QoS sequence counters to use next + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @num_of_gtk_rekeys: number of GTK rekeys + * @transmitted_ndps: number of transmitted neighbor discovery packets + * @received_beacons: number of received beacons + * @tid_tear_down: bit mask of tids whose BA sessions were closed + * in suspend state + * @station_id: station id + * @num_mlo_link_keys: number of &struct iwl_wowlan_mlo_gtk structs + * following this notif + * @tid_offloaded_tx: tid used by the firmware to transmit data packets + * while in wowlan + * @mlo_gtks: array of GTKs of size num_mlo_link_keys + */ +struct iwl_wowlan_info_notif { + struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; + struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; + struct iwl_wowlan_igtk_status bigtk[WOWLAN_BIGTK_KEYS_NUM]; + __le64 replay_ctr; + __le16 pattern_number; + __le16 qos_seq_ctr; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + u8 tid_tear_down; + u8 station_id; + u8 num_mlo_link_keys; + u8 tid_offloaded_tx; + struct iwl_wowlan_mlo_gtk mlo_gtks[]; +} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_5 */ + +/** * struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification * @wake_packet_length: wakeup packet length * @station_id: station id diff --git a/fw/api/datapath.h b/fw/api/datapath.h index 2ab38eaeb290..570a3f722510 100644 --- a/fw/api/datapath.h +++ b/fw/api/datapath.h @@ -391,7 +391,7 @@ enum iwl_datapath_monitor_notif_type { struct iwl_datapath_monitor_notif { __le32 type; - u8 mac_id; + u8 link_id; u8 reserved[3]; } __packed; /* MONITOR_NTF_API_S_VER_1 */ diff --git a/fw/api/dbg-tlv.h b/fw/api/dbg-tlv.h index 855cd13a181e..550de6db1834 100644 --- a/fw/api/dbg-tlv.h +++ b/fw/api/dbg-tlv.h @@ -476,6 +476,8 @@ enum iwl_fw_ini_region_device_memory_subtype { * @IWL_FW_INI_TIME_POINT_PRESET_OVERRIDE_START: start handling override preset * request * @IWL_FW_INI_TIME_SCAN_FAILURE: failed scan channel list + * @IWL_FW_INI_TIME_ESR_LINK_UP: EMLSR is active (several links are activated) + * @IWL_FW_INI_TIME_ESR_LINK_DOWN: EMLSR is inactive (only one active link left) * @IWL_FW_INI_TIME_POINT_NUM: number of time points */ enum iwl_fw_ini_time_point { @@ -509,6 +511,8 @@ enum iwl_fw_ini_time_point { IWL_FW_INI_TIME_POINT_PRESET_OVERRIDE_EXT_REQ, IWL_FW_INI_TIME_POINT_PRESET_OVERRIDE_START, IWL_FW_INI_TIME_SCAN_FAILURE, + IWL_FW_INI_TIME_ESR_LINK_UP, + IWL_FW_INI_TIME_ESR_LINK_DOWN, IWL_FW_INI_TIME_POINT_NUM, }; /* FW_TLV_DEBUG_TIME_POINT_API_E */ diff --git a/fw/api/debug.h b/fw/api/debug.h index bea0f4668cc8..aa88e91d117e 100644 --- a/fw/api/debug.h +++ b/fw/api/debug.h @@ -477,6 +477,9 @@ struct iwl_mvm_tas_status_per_mac { * @tas_status_mac: TAS status per lmac, uses * &struct iwl_mvm_tas_status_per_mac * @in_dual_radio: is TAS in dual radio? - TRUE/FALSE + * @uhb_allowed_flags: see &enum iwl_tas_uhb_allowed_flags. + * This member is valid only when fw has + * %IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT capability. * @reserved: reserved */ struct iwl_mvm_tas_status_resp { @@ -486,7 +489,8 @@ struct iwl_mvm_tas_status_resp { __le16 block_list[16]; struct iwl_mvm_tas_status_per_mac tas_status_mac[2]; u8 in_dual_radio; - u8 reserved[3]; + u8 uhb_allowed_flags; + u8 reserved[2]; } __packed; /*DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3*/ /** diff --git a/fw/api/location.h b/fw/api/location.h index 30a54c7fa001..b8dff139aa05 100644 --- a/fw/api/location.h +++ b/fw/api/location.h @@ -616,6 +616,9 @@ struct iwl_tof_range_req_ap_entry_v2 { * continue with the session and will provide the LMR feedback. * @IWL_INITIATOR_AP_FLAGS_TEST_INCORRECT_SAC: send an incorrect SAC in the * first NDP exchange. This is used for testing. + * @IWL_INITIATOR_AP_FLAGS_TEST_BAD_SLTF: use incorrect secure LTF tx key. This + * is used for testing. Only supported from version 15 of the range request + * command. */ enum iwl_initiator_ap_flags { IWL_INITIATOR_AP_FLAGS_ASAP = BIT(1), @@ -633,6 +636,7 @@ enum iwl_initiator_ap_flags { IWL_INITIATOR_AP_FLAGS_PMF = BIT(14), IWL_INITIATOR_AP_FLAGS_TERMINATE_ON_LMR_FEEDBACK = BIT(15), IWL_INITIATOR_AP_FLAGS_TEST_INCORRECT_SAC = BIT(16), + IWL_INITIATOR_AP_FLAGS_TEST_BAD_SLTF = BIT(17), }; /** @@ -767,7 +771,7 @@ enum iwl_location_cipher { * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of * the number of measurement iterations (min 2^0 = 1, max 2^14) * @sta_id: the station id of the AP. Only relevant when associated to the AP, - * otherwise should be set to &IWL_MVM_INVALID_STA. + * otherwise should be set to &IWL_INVALID_STA. * @cipher: pairwise cipher suite for secured measurement. * &enum iwl_location_cipher. * @hltk: HLTK to be used for secured 11az measurement @@ -814,7 +818,7 @@ struct iwl_tof_range_req_ap_entry_v6 { * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of * the number of measurement iterations (min 2^0 = 1, max 2^14) * @sta_id: the station id of the AP. Only relevant when associated to the AP, - * otherwise should be set to &IWL_MVM_INVALID_STA. + * otherwise should be set to &IWL_INVALID_STA. * @cipher: pairwise cipher suite for secured measurement. * &enum iwl_location_cipher. * @hltk: HLTK to be used for secured 11az measurement @@ -827,10 +831,10 @@ struct iwl_tof_range_req_ap_entry_v6 { * &IWL_INITIATOR_AP_FLAGS_TB is set. * @rx_pn: the next expected PN for protected management frames Rx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @tx_pn: the next PN to use for protected management frames Tx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. */ struct iwl_tof_range_req_ap_entry_v7 { __le32 initiator_ap_flags; @@ -872,7 +876,7 @@ struct iwl_tof_range_req_ap_entry_v7 { * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of * the number of measurement iterations (min 2^0 = 1, max 2^14) * @sta_id: the station id of the AP. Only relevant when associated to the AP, - * otherwise should be set to &IWL_MVM_INVALID_STA. + * otherwise should be set to &IWL_INVALID_STA. * @cipher: pairwise cipher suite for secured measurement. * &enum iwl_location_cipher. * @hltk: HLTK to be used for secured 11az measurement @@ -885,10 +889,10 @@ struct iwl_tof_range_req_ap_entry_v7 { * &IWL_INITIATOR_AP_FLAGS_TB is set. * @rx_pn: the next expected PN for protected management frames Rx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @tx_pn: the next PN to use for protected management frames Tx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @r2i_ndp_params: parameters for R2I NDP ranging negotiation. * bits 0 - 2: max LTF repetitions * bits 3 - 5: max number of spatial streams @@ -946,7 +950,7 @@ struct iwl_tof_range_req_ap_entry_v8 { * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of * the number of measurement iterations (min 2^0 = 1, max 2^14) * @sta_id: the station id of the AP. Only relevant when associated to the AP, - * otherwise should be set to &IWL_MVM_INVALID_STA. + * otherwise should be set to &IWL_INVALID_STA. * @cipher: pairwise cipher suite for secured measurement. * &enum iwl_location_cipher. * @hltk: HLTK to be used for secured 11az measurement @@ -961,10 +965,10 @@ struct iwl_tof_range_req_ap_entry_v8 { * &IWL_INITIATOR_AP_FLAGS_TB or &IWL_INITIATOR_AP_FLAGS_NON_TB is set. * @rx_pn: the next expected PN for protected management frames Rx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @tx_pn: the next PN to use for protected management frames Tx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @r2i_ndp_params: parameters for R2I NDP ranging negotiation. * bits 0 - 2: max LTF repetitions * bits 3 - 5: max number of spatial streams @@ -1029,7 +1033,7 @@ struct iwl_tof_range_req_ap_entry_v9 { * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of * the number of measurement iterations (min 2^0 = 1, max 2^14) * @sta_id: the station id of the AP. Only relevant when associated to the AP, - * otherwise should be set to &IWL_MVM_INVALID_STA. + * otherwise should be set to &IWL_INVALID_STA. * @cipher: pairwise cipher suite for secured measurement. * &enum iwl_location_cipher. * @hltk: HLTK to be used for secured 11az measurement @@ -1042,10 +1046,10 @@ struct iwl_tof_range_req_ap_entry_v9 { * &IWL_INITIATOR_AP_FLAGS_TB is set. * @rx_pn: the next expected PN for protected management frames Rx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @tx_pn: the next PN to use for protected management frames Tx. LE byte * order. Only valid if &IWL_INITIATOR_AP_FLAGS_SECURED is set and sta_id - * is set to &IWL_MVM_INVALID_STA. + * is set to &IWL_INVALID_STA. * @r2i_ndp_params: parameters for R2I NDP ranging negotiation. * bits 0 - 2: max LTF repetitions * bits 3 - 5: max number of spatial streams diff --git a/fw/api/mac-cfg.h b/fw/api/mac-cfg.h index ca6fa66d1917..37bb7002c1c9 100644 --- a/fw/api/mac-cfg.h +++ b/fw/api/mac-cfg.h @@ -42,7 +42,7 @@ enum iwl_mac_conf_subcmd_ids { */ LINK_CONFIG_CMD = 0x9, /** - * @STA_CONFIG_CMD: &struct iwl_mvm_sta_cfg_cmd + * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd */ STA_CONFIG_CMD = 0xA, /** @@ -50,7 +50,7 @@ enum iwl_mac_conf_subcmd_ids { */ AUX_STA_CMD = 0xB, /** - * @STA_REMOVE_CMD: &struct iwl_mvm_remove_sta_cmd + * @STA_REMOVE_CMD: &struct iwl_remove_sta_cmd */ STA_REMOVE_CMD = 0xC, /** @@ -62,11 +62,19 @@ enum iwl_mac_conf_subcmd_ids { */ ROC_CMD = 0xE, /** + * @MISSED_BEACONS_NOTIF: &struct iwl_missed_beacons_notif + */ + MISSED_BEACONS_NOTIF = 0xF6, + /** + * @EMLSR_TRANS_FAIL_NOTIF: &struct iwl_esr_trans_fail_notif + */ + EMLSR_TRANS_FAIL_NOTIF = 0xF7, + /** * @ROC_NOTIF: &struct iwl_roc_notif */ ROC_NOTIF = 0xF8, /** - * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif + * @SESSION_PROTECTION_NOTIF: &struct iwl_session_prot_notif */ SESSION_PROTECTION_NOTIF = 0xFB, @@ -374,6 +382,8 @@ struct iwl_mac_config_cmd { * @LINK_CONTEXT_MODIFY_EHT_PARAMS: covers iwl_link_ctx_cfg_cmd::puncture_mask. * This flag can be set only if the MAC that this link relates to has * eht_support set to true. No longer used since _VER_3 of this command. + * @LINK_CONTEXT_MODIFY_BANDWIDTH: Covers iwl_link_ctx_cfg_cmd::modify_bandwidth. + * Request RX OMI to the AP to modify bandwidth of this link. * @LINK_CONTEXT_MODIFY_ALL: set all above flags */ enum iwl_link_ctx_modify_flags { @@ -385,6 +395,7 @@ enum iwl_link_ctx_modify_flags { LINK_CONTEXT_MODIFY_HE_PARAMS = BIT(5), LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE = BIT(6), LINK_CONTEXT_MODIFY_EHT_PARAMS = BIT(7), + LINK_CONTEXT_MODIFY_BANDWIDTH = BIT(8), LINK_CONTEXT_MODIFY_ALL = 0xff, }; /* LINK_CONTEXT_MODIFY_MASK_E_VER_1 */ @@ -426,6 +437,22 @@ enum iwl_link_ctx_flags { }; /* LINK_CONTEXT_FLAG_E_VER_1 */ /** + * enum iwl_link_modify_bandwidth - link modify (RX OMI) bandwidth + * @IWL_LINK_MODIFY_BW_20: request 20 MHz + * @IWL_LINK_MODIFY_BW_40: request 40 MHz + * @IWL_LINK_MODIFY_BW_80: request 80 MHz + * @IWL_LINK_MODIFY_BW_160: request 160 MHz + * @IWL_LINK_MODIFY_BW_320: request 320 MHz + */ +enum iwl_link_modify_bandwidth { + IWL_LINK_MODIFY_BW_20, + IWL_LINK_MODIFY_BW_40, + IWL_LINK_MODIFY_BW_80, + IWL_LINK_MODIFY_BW_160, + IWL_LINK_MODIFY_BW_320, +}; + +/** * struct iwl_link_config_cmd - command structure to configure the LINK context * in MLD API * ( LINK_CONFIG_CMD =0x9 ) @@ -446,6 +473,11 @@ enum iwl_link_ctx_flags { * @listen_lmac: indicates whether the link should be allocated on the Listen * Lmac or on the Main Lmac. Cannot be changed on an active Link. * Relevant only for eSR. + * @block_tx: tell the firmware that this link can't Tx. This should be used + * only when a link is de-activated because of CSA with mode = 1. + * Available since version 5. + * @modify_bandwidth: bandwidth request value for RX OMI (see also + * %LINK_CONTEXT_MODIFY_BANDWIDTH), from &enum iwl_link_modify_bandwidth. * @reserved1: in version 2, listen_lmac became reserved * @cck_rates: basic rates available for CCK * @ofdm_rates: basic rates available for OFDM @@ -472,7 +504,9 @@ enum iwl_link_ctx_flags { * @bssid_index: index of the associated VAP * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame * @spec_link_id: link_id as the AP knows it - * @reserved2: alignment + * @ul_mu_data_disable: OM Control UL MU Data Disable RX Support (bit 44) in + * HE MAC Capabilities information field as defined in figure 9-897 in + * IEEE802.11REVme-D5.0 * @ibss_bssid_addr: bssid for ibss * @reserved_for_ibss_bssid_addr: reserved * @reserved3: reserved for future use @@ -487,8 +521,12 @@ struct iwl_link_config_cmd { __le32 modify_mask; __le32 active; union { - __le32 listen_lmac; - __le32 reserved1; + __le32 listen_lmac; /* only _VER_1 */ + struct { + u8 block_tx; /* since _VER_5 */ + u8 modify_bandwidth; /* since _VER_6 */ + u8 reserved1[2]; + }; }; __le32 cck_rates; __le32 ofdm_rates; @@ -508,24 +546,24 @@ struct iwl_link_config_cmd { __le16 puncture_mask; /* removed in _VER_3 */ __le16 frame_time_rts_th; __le32 flags; - __le32 flags_mask; + __le32 flags_mask; /* removed in _VER_6 */ /* The below fields are for multi-bssid */ u8 ref_bssid_addr[6]; __le16 reserved_for_ref_bssid_addr; u8 bssid_index; u8 bss_color; u8 spec_link_id; - u8 reserved2; + u8 ul_mu_data_disable; u8 ibss_bssid_addr[6]; __le16 reserved_for_ibss_bssid_addr; __le32 reserved3[8]; -} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3 */ +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3, _VER_4, _VER_5, _VER_6 */ /* Currently FW supports link ids in the range 0-3 and can have * at most two active links for each vif. */ -#define IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM 2 -#define IWL_MVM_FW_MAX_LINK_ID 3 +#define IWL_FW_MAX_ACTIVE_LINKS_NUM 2 +#define IWL_FW_MAX_LINK_ID 3 /** * enum iwl_fw_sta_type - FW station types @@ -547,7 +585,7 @@ enum iwl_fw_sta_type { }; /* STATION_TYPE_E_VER_1 */ /** - * struct iwl_mvm_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's + * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's * station table * ( STA_CONFIG_CMD = 0xA ) * @@ -579,7 +617,7 @@ enum iwl_fw_sta_type { * capa * @htc_flags: which features are supported in HTC */ -struct iwl_mvm_sta_cfg_cmd { +struct iwl_sta_cfg_cmd { __le32 sta_id; __le32 link_id; u8 peer_mld_address[ETH_ALEN]; @@ -620,13 +658,13 @@ struct iwl_mvm_aux_sta_cmd { } __packed; /* AUX_STA_CMD_API_S_VER_1 */ /** - * struct iwl_mvm_remove_sta_cmd - a cmd structure to remove a sta added by + * struct iwl_remove_sta_cmd - a cmd structure to remove a sta added by * STA_CONFIG_CMD or AUX_STA_CONFIG_CMD * ( STA_REMOVE_CMD = 0xC ) * * @sta_id: index of station to remove */ -struct iwl_mvm_remove_sta_cmd { +struct iwl_remove_sta_cmd { __le32 sta_id; } __packed; /* REMOVE_STA_API_S_VER_1 */ @@ -663,4 +701,51 @@ struct iwl_mvm_esr_mode_notif { __le32 action; } __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_1 */ +/** + * struct iwl_missed_beacons_notif - sent when by the firmware upon beacon loss + * ( MISSED_BEACONS_NOTIF = 0xF6 ) + * @link_id: fw link ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @other_link_id: used in EMLSR only. The fw link ID for + * &consec_missed_beacons_other_link. IWL_MVM_FW_LINK_ID_INVALID (0xff) if + * invalid. + * @consec_missed_beacons_other_link: number of consecutive missed beacons on + * &other_link_id. + */ +struct iwl_missed_beacons_notif { + __le32 link_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 other_link_id; + __le32 consec_missed_beacons_other_link; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_5 */ + +/* + * enum iwl_esr_trans_fail_code: to be used to parse the notif below + * + * @ESR_TRANS_FAILED_TX_STATUS_ERROR: failed to TX EML OMN frame + * @ESR_TRANSITION_FAILED_TX_TIMEOUT: timeout on the EML OMN frame + * @ESR_TRANSITION_FAILED_BEACONS_NOT_HEARD: can't get a beacon on the new link + */ +enum iwl_esr_trans_fail_code { + ESR_TRANS_FAILED_TX_STATUS_ERROR, + ESR_TRANSITION_FAILED_TX_TIMEOUT, + ESR_TRANSITION_FAILED_BEACONS_NOT_HEARD, +}; + +/** + * struct iwl_esr_trans_fail_notif - FW reports a failure in EMLSR transition + * + * @link_id: the link_id that still works after the failure + * @activation: true if the link was activated, false otherwise + * @err_code: see &enum iwl_esr_trans_fail_code + */ +struct iwl_esr_trans_fail_notif { + __le32 link_id; + __le32 activation; + __le32 err_code; +} __packed; /* ESR_TRANSITION_FAILED_NTFY_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/fw/api/mac.h b/fw/api/mac.h index bcbbf8c4a297..26301c0b06a1 100644 --- a/fw/api/mac.h +++ b/fw/api/mac.h @@ -16,8 +16,8 @@ #define NUM_MAC_INDEX (NUM_MAC_INDEX_DRIVER + 1) #define NUM_MAC_INDEX_CDB (NUM_MAC_INDEX_DRIVER + 2) -#define IWL_MVM_STATION_COUNT_MAX 16 -#define IWL_MVM_INVALID_STA 0xFF +#define IWL_STATION_COUNT_MAX 16 +#define IWL_INVALID_STA 0xFF enum iwl_ac { AC_BK, @@ -378,7 +378,7 @@ struct iwl_missed_beacons_notif_ver_3 { } __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ /** - * struct iwl_missed_beacons_notif - information on missed beacons + * struct iwl_missed_beacons_notif_v4 - information on missed beacons * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) * @link_id: fw link ID * @consec_missed_beacons_since_last_rx: number of consecutive missed @@ -387,7 +387,7 @@ struct iwl_missed_beacons_notif_ver_3 { * @num_expected_beacons: number of expected beacons * @num_recvd_beacons: number of received beacons */ -struct iwl_missed_beacons_notif { +struct iwl_missed_beacons_notif_v4 { __le32 link_id; __le32 consec_missed_beacons_since_last_rx; __le32 consec_missed_beacons; diff --git a/fw/api/nvm-reg.h b/fw/api/nvm-reg.h index d424d0126367..5cdc09d465d4 100644 --- a/fw/api/nvm-reg.h +++ b/fw/api/nvm-reg.h @@ -464,21 +464,30 @@ struct iwl_tas_config_cmd_v3 { } __packed; /* TAS_CONFIG_CMD_API_S_VER_3 */ /** + * enum iwl_tas_uhb_allowed_flags - per country TAS UHB allowed flags. + * @TAS_UHB_ALLOWED_CANADA: TAS UHB is allowed in Canada. This flag is valid + * only when fw has %IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT capability. + */ +enum iwl_tas_uhb_allowed_flags { + TAS_UHB_ALLOWED_CANADA = BIT(0), +}; + +/** * struct iwl_tas_config_cmd_v4 - configures the TAS * @override_tas_iec: indicates whether to override default value of IEC regulatory * @enable_tas_iec: in case override_tas_iec is set - * indicates whether IEC regulatory is enabled or disabled * @usa_tas_uhb_allowed: if set, allow TAS UHB in the USA - * @reserved: reserved -*/ + * @uhb_allowed_flags: see &enum iwl_tas_uhb_allowed_flags. + */ struct iwl_tas_config_cmd_v4 { u8 override_tas_iec; u8 enable_tas_iec; u8 usa_tas_uhb_allowed; - u8 reserved; + u8 uhb_allowed_flags; } __packed; /* TAS_CONFIG_CMD_API_S_VER_4 */ -struct iwl_tas_config_cmd { +struct iwl_tas_config_cmd_v2_v4 { struct iwl_tas_config_cmd_common common; union { struct iwl_tas_config_cmd_v3 v3; @@ -487,6 +496,46 @@ struct iwl_tas_config_cmd { }; /** + * enum bios_source - source of bios data + * @BIOS_SOURCE_NONE: BIOS source is not defined + * @BIOS_SOURCE_ACPI: BIOS source is ACPI + * @BIOS_SOURCE_UEFI: BIOS source is UEFI + */ +enum bios_source { + BIOS_SOURCE_NONE, + BIOS_SOURCE_ACPI, + BIOS_SOURCE_UEFI, +}; + +/** + * struct bios_value_u32 - BIOS configuration. + * @table_source: see &enum bios_source + * @table_revision: table revision. + * @reserved: reserved + * @value: value in bios. + */ +struct bios_value_u32 { + u8 table_source; + u8 table_revision; + u8 reserved[2]; + __le32 value; +} __packed; /* BIOS_TABLE_SOURCE_U32_S_VER_1 */ + +/** + * struct iwl_tas_config_cmd - configures the TAS. + * @block_list_size: size of relevant field in block_list_array + * @block_list_array: list of countries where TAS must be disabled + * @reserved: reserved + * @tas_config_info: see @struct bios_value_u32 + */ +struct iwl_tas_config_cmd { + __le16 block_list_size; + __le16 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; + u8 reserved[2]; + struct bios_value_u32 tas_config_info; +} __packed; /* TAS_CONFIG_CMD_API_S_VER_5 */ + +/** * enum iwl_lari_config_masks - bit masks for the various LARI config operations * @LARI_CONFIG_DISABLE_11AC_UKRAINE_MSK: disable 11ac in ukraine * @LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK: ETSI 5.8GHz SRD passive scan diff --git a/fw/api/offload.h b/fw/api/offload.h index 6a7bbfd6b2b7..9b09b835560b 100644 --- a/fw/api/offload.h +++ b/fw/api/offload.h @@ -31,7 +31,7 @@ enum iwl_prot_offload_subcmd_ids { /** * @STORED_BEACON_NTF: &struct iwl_stored_beacon_notif_v2 or - * &struct iwl_stored_beacon_notif_v3 + * &struct iwl_stored_beacon_notif */ STORED_BEACON_NTF = 0xFF, }; @@ -71,18 +71,18 @@ struct iwl_stored_beacon_notif_v2 { } __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_2 */ /** - * struct iwl_stored_beacon_notif_v3 - Stored beacon notification + * struct iwl_stored_beacon_notif - Stored beacon notification * * @common: fields common for all versions * @sta_id: station for which the beacon was received * @reserved: reserved for alignment * @data: beacon data, length in @byte_count */ -struct iwl_stored_beacon_notif_v3 { +struct iwl_stored_beacon_notif { struct iwl_stored_beacon_notif_common common; u8 sta_id; u8 reserved[3]; u8 data[MAX_STORED_BEACON_SIZE]; -} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_3 */ +} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_3, _VER_4 */ #endif /* __iwl_fw_api_offload_h__ */ diff --git a/fw/api/power.h b/fw/api/power.h index 6e6a92d173cc..37ec26596ee7 100644 --- a/fw/api/power.h +++ b/fw/api/power.h @@ -266,7 +266,7 @@ struct iwl_reduce_tx_power_cmd { } __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ enum iwl_dev_tx_power_cmd_mode { - IWL_TX_POWER_MODE_SET_MAC = 0, + IWL_TX_POWER_MODE_SET_LINK = 0, IWL_TX_POWER_MODE_SET_DEVICE = 1, IWL_TX_POWER_MODE_SET_CHAINS = 2, IWL_TX_POWER_MODE_SET_ACK = 3, @@ -283,20 +283,16 @@ enum iwl_dev_tx_power_cmd_mode { /** * struct iwl_dev_tx_power_common - Common part of the TX power reduction cmd * @set_mode: see &enum iwl_dev_tx_power_cmd_mode - * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @link_id: id of the link ctx for which we are reducing TX power. + * For version 9 / 10, this is the link id. For earlier versions, it is + * the mac id. * @pwr_restriction: TX power restriction in 1/8 dBms. - * @dev_24: device TX power restriction in 1/8 dBms - * @dev_52_low: device TX power restriction upper band - low - * @dev_52_high: device TX power restriction upper band - high */ struct iwl_dev_tx_power_common { __le32 set_mode; - __le32 mac_context_id; + __le32 link_id; __le16 pwr_restriction; - __le16 dev_24; - __le16 dev_52_low; - __le16 dev_52_high; -}; +} __packed; /** * struct iwl_dev_tx_power_cmd_v3 - TX power reduction command version 3 @@ -412,8 +408,20 @@ struct iwl_dev_tx_power_cmd_v8 { __le32 tpc_vlp_backoff_level; } __packed; /* TX_REDUCED_POWER_API_S_VER_8 */ +/* + * @dev_24: device TX power restriction in 1/8 dBms + * @dev_52_low: device TX power restriction upper band - low + * @dev_52_high: device TX power restriction upper band - high + */ +struct iwl_dev_tx_power_cmd_per_band { + __le16 dev_24; + __le16 dev_52_low; + __le16 dev_52_high; +} __packed; + /** - * struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion) + * struct iwl_dev_tx_power_cmd_v3_v8 - TX power reduction command (multiversion) + * @per_band: per band restrictions * @common: common part of the command * @v3: version 3 part of the command * @v4: version 4 part of the command @@ -422,8 +430,9 @@ struct iwl_dev_tx_power_cmd_v8 { * @v7: version 7 part of the command * @v8: version 8 part of the command */ -struct iwl_dev_tx_power_cmd { +struct iwl_dev_tx_power_cmd_v3_v8 { struct iwl_dev_tx_power_common common; + struct iwl_dev_tx_power_cmd_per_band per_band; union { struct iwl_dev_tx_power_cmd_v3 v3; struct iwl_dev_tx_power_cmd_v4 v4; @@ -434,6 +443,60 @@ struct iwl_dev_tx_power_cmd { }; }; +/** + * struct iwl_dev_tx_power_cmd_v9 - TX power reduction cmd + * @reserved: reserved (padding) + * @per_chain: per chain restrictions + * @per_chain_restriction_changed: is per_chain_restriction has changed + * from last command. used if set_mode is + * IWL_TX_POWER_MODE_SET_SAR_TIMER. + * note: if not changed, the command is used for keep alive only. + * @reserved1: reserved (padding) + * @timer_period: timer in milliseconds. if expires FW will change to default + * BIOS values. relevant if setMode is IWL_TX_POWER_MODE_SET_SAR_TIMER + */ +struct iwl_dev_tx_power_cmd_v9 { + __le16 reserved; + __le16 per_chain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V1]; + u8 per_chain_restriction_changed; + u8 reserved1[3]; + __le32 timer_period; +} __packed; /* TX_REDUCED_POWER_API_S_VER_9 */ + +/** + * struct iwl_dev_tx_power_cmd_v10 - TX power reduction cmd + * @per_chain: per chain restrictions + * @per_chain_restriction_changed: is per_chain_restriction has changed + * from last command. used if set_mode is + * IWL_TX_POWER_MODE_SET_SAR_TIMER. + * note: if not changed, the command is used for keep alive only. + * @reserved: reserved (padding) + * @timer_period: timer in milliseconds. if expires FW will change to default + * BIOS values. relevant if setMode is IWL_TX_POWER_MODE_SET_SAR_TIMER + * @flags: reduce power flags. + */ +struct iwl_dev_tx_power_cmd_v10 { + __le16 per_chain[IWL_NUM_CHAIN_TABLES_V2][IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2]; + u8 per_chain_restriction_changed; + u8 reserved; + __le32 timer_period; + __le32 flags; +} __packed; /* TX_REDUCED_POWER_API_S_VER_10 */ + +/* + * struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion) + * @common: common part of the command + * @v9: version 9 part of the command + * @v10: version 10 part of the command + */ +struct iwl_dev_tx_power_cmd { + struct iwl_dev_tx_power_common common; + union { + struct iwl_dev_tx_power_cmd_v9 v9; + struct iwl_dev_tx_power_cmd_v10 v10; + }; +} __packed; /* TX_REDUCED_POWER_API_S_VER_9_VER10 */ + #define IWL_NUM_GEO_PROFILES 3 #define IWL_NUM_GEO_PROFILES_V3 8 #define IWL_NUM_BANDS_PER_CHAIN_V1 2 diff --git a/fw/api/scan.h b/fw/api/scan.h index 8598031567bb..f486d624500b 100644 --- a/fw/api/scan.h +++ b/fw/api/scan.h @@ -731,39 +731,46 @@ enum iwl_umac_scan_general_params_flags2 { * struct iwl_scan_channel_cfg_umac * @flags: bitmap - 0-19: directed scan to i'th ssid. * @channel_num: channel number 1-13 etc. - * @band: band of channel: 0 for 2GHz, 1 for 5GHz - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two scan iterations on one channel. + * @v1: command version 1 + * @v1.iter_count: repetition count for the channel. + * @v1.iter_interval: interval between two scan iterations on one channel. + * @v2: command versions 2-4 + * @v2.band: band of channel: 0 for 2GHz, 1 for 5GHz + * @v2.iter_count: repetition count for the channel. + * @v2.iter_interval: interval between two scan iterations on one channel. + * @v5: command versions 5 and up + * @v5.iter_count: repetition count for the channel. + * @v5.iter_interval: interval between two scan iterations on one channel. + * @v5.psd_20: highest PSD value for all APs known so far + * on this channel. */ struct iwl_scan_channel_cfg_umac { #define IWL_CHAN_CFG_FLAGS_BAND_POS 30 __le32 flags; + u8 channel_num; /* All versions are of the same size, so use a union without adjusting * the command size later */ union { struct { - u8 channel_num; u8 iter_count; __le16 iter_interval; - } v1; /* SCAN_CHANNEL_CONFIG_API_S_VER_1 */ + } __packed v1; /* SCAN_CHANNEL_CONFIG_API_S_VER_1 */ struct { - u8 channel_num; u8 band; u8 iter_count; u8 iter_interval; - } v2; /* SCAN_CHANNEL_CONFIG_API_S_VER_2 - * SCAN_CHANNEL_CONFIG_API_S_VER_3 - * SCAN_CHANNEL_CONFIG_API_S_VER_4 - */ + } __packed v2; /* SCAN_CHANNEL_CONFIG_API_S_VER_2 + * SCAN_CHANNEL_CONFIG_API_S_VER_3 + * SCAN_CHANNEL_CONFIG_API_S_VER_4 + */ struct { - u8 channel_num; u8 psd_20; u8 iter_count; u8 iter_interval; - } v5; /* SCAN_CHANNEL_CONFIG_API_S_VER_5 */ - }; + } __packed v5; /* SCAN_CHANNEL_CONFIG_API_S_VER_5 */ + } __packed; } __packed; /** @@ -1133,6 +1140,19 @@ struct iwl_umac_scan_abort { } __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ /** + * enum iwl_umac_scan_abort_status + * + * @IWL_UMAC_SCAN_ABORT_STATUS_SUCCESS: scan was successfully aborted + * @IWL_UMAC_SCAN_ABORT_STATUS_IN_PROGRESS: scan abort is in progress + * @IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND: nothing to abort + */ +enum iwl_umac_scan_abort_status { + IWL_UMAC_SCAN_ABORT_STATUS_SUCCESS = 0, + IWL_UMAC_SCAN_ABORT_STATUS_IN_PROGRESS, + IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND, +}; + +/** * struct iwl_umac_scan_complete * @uid: scan id, &enum iwl_umac_scan_uid_offsets * @last_schedule: last scheduling line diff --git a/fw/api/stats.h b/fw/api/stats.h index 2271b19213fa..0a9f14fb04be 100644 --- a/fw/api/stats.h +++ b/fw/api/stats.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020 - 2021, 2023 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020 - 2021, 2023 - 2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -327,14 +327,14 @@ struct mvm_statistics_load { __le32 air_time[MAC_INDEX_AUX]; __le32 byte_count[MAC_INDEX_AUX]; __le32 pkt_count[MAC_INDEX_AUX]; - u8 avg_energy[IWL_MVM_STATION_COUNT_MAX]; + u8 avg_energy[IWL_STATION_COUNT_MAX]; } __packed; /* STATISTICS_RX_MAC_STATION_S_VER_3 */ struct mvm_statistics_load_v1 { __le32 air_time[NUM_MAC_INDEX]; __le32 byte_count[NUM_MAC_INDEX]; __le32 pkt_count[NUM_MAC_INDEX]; - u8 avg_energy[IWL_MVM_STATION_COUNT_MAX]; + u8 avg_energy[IWL_STATION_COUNT_MAX]; } __packed; /* STATISTICS_RX_MAC_STATION_S_VER_1 */ struct mvm_statistics_rx { @@ -594,7 +594,7 @@ struct iwl_stats_ntfy_per_sta { } __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */ #define IWL_STATS_MAX_PHY_OPERATIONAL 3 -#define IWL_STATS_MAX_FW_LINKS (IWL_MVM_FW_MAX_LINK_ID + 1) +#define IWL_STATS_MAX_FW_LINKS (IWL_FW_MAX_LINK_ID + 1) /** * struct iwl_system_statistics_notif_oper @@ -608,7 +608,7 @@ struct iwl_system_statistics_notif_oper { __le32 time_stamp; struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS]; struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; - struct iwl_stats_ntfy_per_sta per_sta[IWL_MVM_STATION_COUNT_MAX]; + struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; } __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ /** @@ -651,7 +651,7 @@ struct iwl_statistics_operational_ntfy { __le32 flags; struct iwl_stats_ntfy_per_mac per_mac[MAC_INDEX_AUX]; struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; - struct iwl_stats_ntfy_per_sta per_sta[IWL_MVM_STATION_COUNT_MAX]; + struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; __le64 rx_time; __le64 tx_time; __le64 on_time_rf; @@ -699,7 +699,7 @@ struct iwl_statistics_operational_ntfy_ver_14 { __le64 tx_time; __le64 on_time_rf; __le64 on_time_scan; - __le32 average_energy[IWL_MVM_STATION_COUNT_MAX]; + __le32 average_energy[IWL_STATION_COUNT_MAX]; __le32 reserved; } __packed; /* STATISTICS_OPERATIONAL_NTFY_API_S_VER_14 */ diff --git a/fw/api/tdls.h b/fw/api/tdls.h index 893438aadab0..cfa6532a3cdd 100644 --- a/fw/api/tdls.h +++ b/fw/api/tdls.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -10,7 +10,7 @@ #include "fw/api/tx.h" #include "fw/api/phy-ctxt.h" -#define IWL_MVM_TDLS_STA_COUNT 4 +#define IWL_TDLS_STA_COUNT 4 /* Type of TDLS request */ enum iwl_tdls_channel_switch_type { @@ -128,7 +128,7 @@ struct iwl_tdls_config_cmd { u8 tdls_peer_count; u8 tx_to_ap_tid; __le16 tx_to_ap_ssn; - struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; + struct iwl_tdls_sta_info sta_info[IWL_TDLS_STA_COUNT]; __le32 pti_req_data_offset; struct iwl_tx_cmd pti_req_tx_cmd; @@ -155,7 +155,7 @@ struct iwl_tdls_config_sta_info_res { */ struct iwl_tdls_config_res { __le32 tx_to_ap_last_seq; - struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; + struct iwl_tdls_config_sta_info_res sta_info[IWL_TDLS_STA_COUNT]; } __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ #endif /* __iwl_fw_api_tdls_h__ */ diff --git a/fw/api/time-event.h b/fw/api/time-event.h index f4b827b58bd3..18d030334a6a 100644 --- a/fw/api/time-event.h +++ b/fw/api/time-event.h @@ -395,7 +395,7 @@ struct iwl_roc_notif { } __packed; /* ROC_NOTIF_API_S_VER_1 */ /** - * enum iwl_mvm_session_prot_conf_id - session protection's configurations + * enum iwl_session_prot_conf_id - session protection's configurations * @SESSION_PROTECT_CONF_ASSOC: Start a session protection for association. * The firmware will allocate two events. * Valid for BSS_STA and P2P_STA. @@ -424,7 +424,7 @@ struct iwl_roc_notif { * be taken into account. * @SESSION_PROTECT_CONF_MAX_ID: not used */ -enum iwl_mvm_session_prot_conf_id { +enum iwl_session_prot_conf_id { SESSION_PROTECT_CONF_ASSOC, SESSION_PROTECT_CONF_GO_CLIENT_ASSOC, SESSION_PROTECT_CONF_P2P_DEVICE_DISCOV, @@ -433,12 +433,12 @@ enum iwl_mvm_session_prot_conf_id { }; /* SESSION_PROTECTION_CONF_ID_E_VER_1 */ /** - * struct iwl_mvm_session_prot_cmd - configure a session protection + * struct iwl_session_prot_cmd - configure a session protection * @id_and_color: the id and color of the link (or mac, for command version 1) * for which this session protection is sent * @action: can be either FW_CTXT_ACTION_ADD or FW_CTXT_ACTION_REMOVE, * see &enum iwl_ctxt_action - * @conf_id: see &enum iwl_mvm_session_prot_conf_id + * @conf_id: see &enum iwl_session_prot_conf_id * @duration_tu: the duration of the whole protection in TUs. * @repetition_count: not used * @interval: not used @@ -448,7 +448,7 @@ enum iwl_mvm_session_prot_conf_id { * The firmware supports only one concurrent session protection per vif. * Adding a new session protection will remove any currently running session. */ -struct iwl_mvm_session_prot_cmd { +struct iwl_session_prot_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ __le32 id_and_color; __le32 action; @@ -462,17 +462,17 @@ struct iwl_mvm_session_prot_cmd { */ /** - * struct iwl_mvm_session_prot_notif - session protection started / ended + * struct iwl_session_prot_notif - session protection started / ended * @mac_link_id: the mac id (or link id, for notif ver > 2) for which the * session protection started / ended * @status: 1 means success, 0 means failure * @start: 1 means the session protection started, 0 means it ended - * @conf_id: see &enum iwl_mvm_session_prot_conf_id + * @conf_id: see &enum iwl_session_prot_conf_id * * Note that any session protection will always get two notifications: start * and end even the firmware could not schedule it. */ -struct iwl_mvm_session_prot_notif { +struct iwl_session_prot_notif { __le32 mac_link_id; __le32 status; __le32 start; diff --git a/fw/api/tx.h b/fw/api/tx.h index c5277e2f8cd4..0a39e4b6eb62 100644 --- a/fw/api/tx.h +++ b/fw/api/tx.h @@ -191,7 +191,7 @@ enum iwl_tx_offload_assist_flags_pos { * cleared. Combination of RATE_MCS_* * @sta_id: index of destination station in FW station table * @sec_ctl: security control, TX_CMD_SEC_* - * @initial_rate_index: index into the the rate table for initial TX attempt. + * @initial_rate_index: index into the rate table for initial TX attempt. * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. * @reserved2: reserved * @key: security key @@ -298,8 +298,7 @@ struct iwl_tx_cmd_gen3 { __le32 rate_n_flags; u8 reserved[8]; struct ieee80211_hdr hdr[]; -} __packed; /* TX_CMD_API_S_VER_8, - TX_CMD_API_S_VER_10 */ +} __packed; /* TX_CMD_API_S_VER_8, TX_CMD_API_S_VER_10 */ /* * TX response related data @@ -482,11 +481,11 @@ struct agg_tx_status { #define TX_RES_RATE_TABLE_COL_GET(_f) (((_f) & TX_RES_RATE_TABLE_COLOR_MSK) >>\ TX_RES_RATE_TABLE_COLOR_POS) -#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) -#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) +#define IWL_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) +#define IWL_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) /** - * struct iwl_mvm_tx_resp_v3 - notifies that fw is TXing a packet + * struct iwl_tx_resp_v3 - notifies that fw is TXing a packet * ( REPLY_TX = 0x1c ) * @frame_count: 1 no aggregation, >1 aggregation * @bt_kill_count: num of times blocked by bluetooth (unused for agg) @@ -517,7 +516,7 @@ struct agg_tx_status { * After the array of statuses comes the SSN of the SCD. Look at * %iwl_mvm_get_scd_ssn for more details. */ -struct iwl_mvm_tx_resp_v3 { +struct iwl_tx_resp_v3 { u8 frame_count; u8 bt_kill_count; u8 failure_rts; @@ -543,7 +542,7 @@ struct iwl_mvm_tx_resp_v3 { } __packed; /* TX_RSP_API_S_VER_3 */ /** - * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet + * struct iwl_tx_resp - notifies that fw is TXing a packet * ( REPLY_TX = 0x1c ) * @frame_count: 1 no aggregation, >1 aggregation * @bt_kill_count: num of times blocked by bluetooth (unused for agg) @@ -575,7 +574,7 @@ struct iwl_mvm_tx_resp_v3 { * After the array of statuses comes the SSN of the SCD. Look at * %iwl_mvm_get_scd_ssn for more details. */ -struct iwl_mvm_tx_resp { +struct iwl_tx_resp { u8 frame_count; u8 bt_kill_count; u8 failure_rts; @@ -601,7 +600,8 @@ struct iwl_mvm_tx_resp { __le16 reserved2; struct agg_tx_status status; } __packed; /* TX_RSP_API_S_VER_6, - TX_RSP_API_S_VER_7 */ + TX_RSP_API_S_VER_7, + TX_RSP_API_S_VER_8 */ /** * struct iwl_mvm_ba_notif - notifies about reception of BA @@ -638,14 +638,14 @@ struct iwl_mvm_ba_notif { } __packed; /** - * struct iwl_mvm_compressed_ba_tfd - progress of a TFD queue + * struct iwl_compressed_ba_tfd - progress of a TFD queue * @q_num: TFD queue number * @tfd_index: Index of first un-acked frame in the TFD queue * @scd_queue: For debug only - the physical queue the TFD queue is bound to * @tid: TID of the queue (0-7) * @reserved: reserved for alignment */ -struct iwl_mvm_compressed_ba_tfd { +struct iwl_compressed_ba_tfd { __le16 q_num; __le16 tfd_index; u8 scd_queue; @@ -654,12 +654,12 @@ struct iwl_mvm_compressed_ba_tfd { } __packed; /* COMPRESSED_BA_TFD_API_S_VER_1 */ /** - * struct iwl_mvm_compressed_ba_ratid - progress of a RA TID queue + * struct iwl_compressed_ba_ratid - progress of a RA TID queue * @q_num: RA TID queue number * @tid: TID of the queue * @ssn: BA window current SSN */ -struct iwl_mvm_compressed_ba_ratid { +struct iwl_compressed_ba_ratid { u8 q_num; u8 tid; __le16 ssn; @@ -685,7 +685,7 @@ enum iwl_mvm_ba_resp_flags { }; /** - * struct iwl_mvm_compressed_ba_notif - notifies about reception of BA + * struct iwl_compressed_ba_notif - notifies about reception of BA * ( BA_NOTIF = 0xc5 ) * @flags: status flag, see the &iwl_mvm_ba_resp_flags * @sta_id: Index of recipient (BA-sending) station in fw's station table @@ -704,12 +704,12 @@ enum iwl_mvm_ba_resp_flags { * @tx_rate: the rate the aggregation was sent at * @tfd_cnt: number of TFD-Q elements * @ra_tid_cnt: number of RATID-Q elements - * @tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd + * @tfd: array of TFD queue status updates. See &iwl_compressed_ba_tfd * for details. Length in @tfd_cnt. * @ra_tid: array of RA-TID queue status updates. For debug purposes only. See - * &iwl_mvm_compressed_ba_ratid for more details. Length in @ra_tid_cnt. + * &iwl_compressed_ba_ratid for more details. Length in @ra_tid_cnt. */ -struct iwl_mvm_compressed_ba_notif { +struct iwl_compressed_ba_notif { __le32 flags; u8 sta_id; u8 reduced_txp; @@ -726,8 +726,8 @@ struct iwl_mvm_compressed_ba_notif { __le16 tfd_cnt; __le16 ra_tid_cnt; union { - DECLARE_FLEX_ARRAY(struct iwl_mvm_compressed_ba_ratid, ra_tid); - DECLARE_FLEX_ARRAY(struct iwl_mvm_compressed_ba_tfd, tfd); + DECLARE_FLEX_ARRAY(struct iwl_compressed_ba_ratid, ra_tid); + DECLARE_FLEX_ARRAY(struct iwl_compressed_ba_tfd, tfd); }; } __packed; /* COMPRESSED_BA_RES_API_S_VER_4, COMPRESSED_BA_RES_API_S_VER_5 */ @@ -823,7 +823,7 @@ struct iwl_mac_beacon_cmd { */ struct iwl_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; + struct iwl_tx_resp beacon_notify_hdr; __le64 tsf; __le32 ibss_mgr_status; } __packed; @@ -836,7 +836,7 @@ struct iwl_beacon_notif { * @gp2: last beacon time in gp2 */ struct iwl_extended_beacon_notif_v5 { - struct iwl_mvm_tx_resp beacon_notify_hdr; + struct iwl_tx_resp beacon_notify_hdr; __le64 tsf; __le32 ibss_mgr_status; __le32 gp2; @@ -558,41 +558,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt, } /* - * alloc_sgtable - allocates scallerlist table in the given size, - * fills it with pages and returns it + * alloc_sgtable - allocates (chained) scatterlist in the given size, + * fills it with pages and returns it * @size: the size (in bytes) of the table -*/ -static struct scatterlist *alloc_sgtable(int size) + */ +static struct scatterlist *alloc_sgtable(ssize_t size) { - int alloc_size, nents, i; - struct page *new_page; - struct scatterlist *iter; - struct scatterlist *table; + struct scatterlist *result = NULL, *prev; + int nents, i, n_prev; nents = DIV_ROUND_UP(size, PAGE_SIZE); - table = kcalloc(nents, sizeof(*table), GFP_KERNEL); - if (!table) - return NULL; - sg_init_table(table, nents); - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = alloc_page(GFP_KERNEL); - if (!new_page) { - /* release all previous allocated pages in the table */ - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = sg_page(iter); - if (new_page) - __free_page(new_page); - } - kfree(table); + +#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result)) + /* + * We need an additional entry for table chaining, + * this ensures the loop can finish i.e. we can + * fit at least two entries per page (obviously, + * many more really fit.) + */ + BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2); + + while (nents > 0) { + struct scatterlist *new, *iter; + int n_fill, n_alloc; + + if (nents <= N_ENTRIES_PER_PAGE) { + /* last needed table */ + n_fill = nents; + n_alloc = nents; + nents = 0; + } else { + /* fill a page with entries */ + n_alloc = N_ENTRIES_PER_PAGE; + /* reserve one for chaining */ + n_fill = n_alloc - 1; + nents -= n_fill; + } + + new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL); + if (!new) { + if (result) + _devcd_free_sgtable(result); return NULL; } - alloc_size = min_t(int, size, PAGE_SIZE); - size -= PAGE_SIZE; - sg_set_page(iter, new_page, alloc_size, 0); + sg_init_table(new, n_alloc); + + if (!result) + result = new; + else + sg_chain(prev, n_prev, new); + prev = new; + n_prev = n_alloc; + + for_each_sg(new, iter, n_fill, i) { + struct page *new_page = alloc_page(GFP_KERNEL); + + if (!new_page) { + _devcd_free_sgtable(result); + return NULL; + } + + sg_set_page(iter, new_page, PAGE_SIZE, 0); + } } - return table; + + return result; } static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt, @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2019, 2021-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2019, 2021-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -287,7 +287,7 @@ static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans, trans->dbg.umac_error_event_table = umac_error_event_table; } -static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync) +static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt) { enum iwl_fw_ini_time_point tp_id; @@ -303,7 +303,7 @@ static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync) tp_id = IWL_FW_INI_TIME_POINT_FW_ASSERT; } - _iwl_dbg_tlv_time_point(fwrt, tp_id, NULL, sync); + iwl_dbg_tlv_time_point_sync(fwrt, tp_id, NULL); } static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, @@ -327,18 +327,19 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt); void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, u32 timepoint, u32 timepoint_data); +bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id); void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt); -#define IWL_FW_CHECK_FAILED(_obj, _fmt, ...) \ - IWL_ERR_LIMIT(_obj, _fmt, __VA_ARGS__) +#define IWL_FW_CHECK_FAILED(_obj, ...) \ + IWL_ERR_LIMIT(_obj, __VA_ARGS__) #define IWL_FW_CHECK(_obj, _cond, _fmt, ...) \ ({ \ bool __cond = (_cond); \ \ if (unlikely(__cond)) \ - IWL_FW_CHECK_FAILED(_obj, _fmt, __VA_ARGS__); \ + IWL_FW_CHECK_FAILED(_obj, _fmt, ##__VA_ARGS__); \ \ unlikely(__cond); \ }) diff --git a/fw/debugfs.c b/fw/debugfs.c index 893b21fcaf87..f0c813d675f4 100644 --- a/fw/debugfs.c +++ b/fw/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -123,6 +123,24 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \ FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) +static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_fw_runtime *fwrt, + char *buf, size_t count) +{ + if (count == 0) + return 0; + + if (!iwl_trans_fw_running(fwrt->trans)) + return count; + + iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER, NULL); + + iwl_fw_dbg_collect(fwrt, FW_DBG_TRIGGER_USER, buf, (count - 1), NULL); + + return count; +} + +FWRT_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 16); + static int iwl_dbgfs_enabled_severities_write(struct iwl_fw_runtime *fwrt, char *buf, size_t count) { @@ -282,6 +300,26 @@ static ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt, FWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20); +static ssize_t iwl_dbgfs_fw_ver_read(struct iwl_fw_runtime *fwrt, + size_t size, char *buf) +{ + char *pos = buf; + char *endpos = buf + size; + + pos += scnprintf(pos, endpos - pos, "FW id: %s\n", + fwrt->fw->fw_version); + pos += scnprintf(pos, endpos - pos, "FW: %s\n", + fwrt->fw->human_readable); + pos += scnprintf(pos, endpos - pos, "Device: %s\n", + fwrt->trans->name); + pos += scnprintf(pos, endpos - pos, "Bus: %s\n", + fwrt->dev->bus->name); + + return pos - buf; +} + +FWRT_DEBUGFS_READ_FILE_OPS(fw_ver, 1024); + struct iwl_dbgfs_fw_info_priv { struct iwl_fw_runtime *fwrt; }; @@ -403,5 +441,7 @@ void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200); FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200); FWRT_DEBUGFS_ADD_FILE(enabled_severities, dbgfs_dir, 0200); + FWRT_DEBUGFS_ADD_FILE(fw_dbg_collect, dbgfs_dir, 0200); FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400); + FWRT_DEBUGFS_ADD_FILE(fw_ver, dbgfs_dir, 0400); } diff --git a/fw/dump.c b/fw/dump.c index 8f107ceec407..c7b261c8ec96 100644 --- a/fw/dump.c +++ b/fw/dump.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -530,3 +530,31 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) } } IWL_EXPORT_SYMBOL(iwl_fwrt_dump_error_logs); + +bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id) +{ + struct error_table_start { + /* cf. struct iwl_error_event_table */ + u32 valid; + __le32 err_id; + } err_info = {}; + int ret; + + if (err_id) + *err_id = 0; + + if (!base) + return false; + + ret = iwl_trans_read_mem_bytes(trans, base, + &err_info, sizeof(err_info)); + + if (ret) + return true; + + if (err_info.valid && err_id) + *err_id = le32_to_cpu(err_info.err_id); + + return !!err_info.valid; +} +IWL_EXPORT_SYMBOL(iwl_fwrt_read_err_table); diff --git a/fw/error-dump.h b/fw/error-dump.h index e63b08b7d336..3af275133da0 100644 --- a/fw/error-dump.h +++ b/fw/error-dump.h @@ -169,7 +169,7 @@ struct iwl_fw_error_dump_info { * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer * @fw_mon_base_ptr: base pointer of the data * @fw_mon_cycle_cnt: number of wraparounds - * @fw_mon_base_high_ptr: used in AX210 devices, the base adderss is 64 bit + * @fw_mon_base_high_ptr: used in AX210 devices, the base address is 64 bit * so fw_mon_base_ptr holds LSB 32 bits and fw_mon_base_high_ptr hold * MSB 32 bits * @reserved: for future use diff --git a/fw/file.h b/fw/file.h index ae05227b6153..9860903ecd3f 100644 --- a/fw/file.h +++ b/fw/file.h @@ -104,6 +104,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_CURRENT_PC = 68, IWL_UCODE_TLV_FW_NUM_STATIONS = IWL_UCODE_TLV_CONST_BASE + 0, + IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1, IWL_UCODE_TLV_FW_NUM_BEACONS = IWL_UCODE_TLV_CONST_BASE + 2, IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0, @@ -384,7 +385,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * to report the CSI information with (certain) RX frames * @IWL_UCODE_TLV_CAPA_FTM_CALIBRATED: has FTM calibrated and thus supports both * initiator and responder - * @IWL_UCODE_TLV_CAPA_MLME_OFFLOAD: supports MLME offload + * @IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA: supports (de)activating UNII-4 + * for US/CA/WW from BIOS * @IWL_UCODE_TLV_CAPA_PROTECTED_TWT: Supports protection of TWT action frames * @IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE: Supports the firmware handshake in * reset flow @@ -397,6 +399,9 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT: Support secure LTF measurement. * @IWL_UCODE_TLV_CAPA_MONITOR_PASSIVE_CHANS: Support monitor mode on otherwise * passive channels + * @IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA: supports (de)activating 5G9 + * for CA from BIOS. + * @IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT: supports %TAS_UHB_ALLOWED_CANADA * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -474,7 +479,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP = (__force iwl_ucode_tlv_capa_t)93, /* set 3 */ - IWL_UCODE_TLV_CAPA_MLME_OFFLOAD = (__force iwl_ucode_tlv_capa_t)96, + IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA = (__force iwl_ucode_tlv_capa_t)96, /* * @IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT: supports PSC channels @@ -497,6 +502,8 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT = (__force iwl_ucode_tlv_capa_t)117, IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT = (__force iwl_ucode_tlv_capa_t)121, IWL_UCODE_TLV_CAPA_MONITOR_PASSIVE_CHANS = (__force iwl_ucode_tlv_capa_t)122, + IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA = (__force iwl_ucode_tlv_capa_t)123, + IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT = (__force iwl_ucode_tlv_capa_t)124, NUM_IWL_UCODE_TLV_CAPA /* * This construction make both sparse (which cannot increment the previous @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright(c) 2019 - 2021 Intel Corporation + * Copyright(c) 2024 Intel Corporation */ #include <fw/api/commands.h> #include "img.h" @@ -75,6 +76,7 @@ static const struct { { "NMI_INTERRUPT_ACTION_PT", 0x7C }, { "NMI_INTERRUPT_UNKNOWN", 0x84 }, { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, + { "NMI_INTERRUPT_PREG", 0x88 }, { "PNVM_MISSING", FW_SYSASSERT_PNVM_MISSING }, { "ADVANCED_SYSASSERT", 0 }, }; @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -51,6 +51,7 @@ struct iwl_ucode_capabilities { u32 error_log_addr; u32 error_log_size; u32 num_stations; + u32 num_links; u32 num_beacons; unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)]; unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; diff --git a/fw/init.c b/fw/init.c index d8b083be5b6b..de87e0e3e072 100644 --- a/fw/init.c +++ b/fw/init.c @@ -39,10 +39,12 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, } IWL_EXPORT_SYMBOL(iwl_fw_runtime_init); +/* Assumes the appropriate lock is held by the caller */ void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt) { iwl_fw_suspend_timestamp(fwrt); - iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_HOST_D3_START, NULL); + iwl_dbg_tlv_time_point_sync(fwrt, IWL_FW_INI_TIME_POINT_HOST_D3_START, + NULL); } IWL_EXPORT_SYMBOL(iwl_fw_runtime_suspend); diff --git a/fw/paging.c b/fw/paging.c index 945bc4160cc9..a7b7cae874a2 100644 --- a/fw/paging.c +++ b/fw/paging.c @@ -249,7 +249,7 @@ static int iwl_send_paging_cmd(struct iwl_fw_runtime *fwrt, }; int blk_idx; - /* loop for for all paging blocks + CSS block */ + /* loop for all paging blocks + CSS block */ for (blk_idx = 0; blk_idx < fwrt->num_of_paging_blk + 1; blk_idx++) { dma_addr_t addr = fwrt->fw_paging_db[blk_idx].fw_paging_phys; __le32 phy_addr; diff --git a/fw/regulatory.c b/fw/regulatory.c index 560a91998cc4..ea435ee94312 100644 --- a/fw/regulatory.c +++ b/fw/regulatory.c @@ -39,6 +39,7 @@ IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); IWL_BIOS_TABLE_LOADER_DATA(mcc, char); IWL_BIOS_TABLE_LOADER_DATA(eckv, u32); IWL_BIOS_TABLE_LOADER_DATA(wbem, u32); +IWL_BIOS_TABLE_LOADER_DATA(dsbr, u32); static const struct dmi_system_id dmi_ppag_approved_list[] = { @@ -100,6 +101,11 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = { DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), }, }, + { .ident = "WIKO", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "WIKO"), + }, + }, {} }; @@ -424,25 +430,31 @@ bool iwl_is_tas_approved(void) } IWL_EXPORT_SYMBOL(iwl_is_tas_approved); -int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, - struct iwl_tas_data *tas_data, - const u32 tas_selection) +struct iwl_tas_selection_data +iwl_parse_tas_selection(const u32 tas_selection_in, const u8 tbl_rev) { - u8 override_iec = u32_get_bits(tas_selection, + struct iwl_tas_selection_data tas_selection_out = {}; + u8 override_iec = u32_get_bits(tas_selection_in, IWL_WTAS_OVERRIDE_IEC_MSK); - u8 enabled_iec = u32_get_bits(tas_selection, IWL_WTAS_ENABLE_IEC_MSK); - u8 usa_tas_uhb = u32_get_bits(tas_selection, IWL_WTAS_USA_UHB_MSK); - int enabled = tas_selection & IWL_WTAS_ENABLED_MSK; - - IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", - tas_selection); + u8 canada_tas_uhb = u32_get_bits(tas_selection_in, + IWL_WTAS_CANADA_UHB_MSK); + u8 enabled_iec = u32_get_bits(tas_selection_in, + IWL_WTAS_ENABLE_IEC_MSK); + u8 usa_tas_uhb = u32_get_bits(tas_selection_in, + IWL_WTAS_USA_UHB_MSK); + + if (tbl_rev > 0) { + tas_selection_out.usa_tas_uhb_allowed = usa_tas_uhb; + tas_selection_out.override_tas_iec = override_iec; + tas_selection_out.enable_tas_iec = enabled_iec; + } - tas_data->usa_tas_uhb_allowed = usa_tas_uhb; - tas_data->override_tas_iec = override_iec; - tas_data->enable_tas_iec = enabled_iec; + if (tbl_rev > 1) + tas_selection_out.canada_tas_uhb_allowed = canada_tas_uhb; - return enabled; + return tas_selection_out; } +IWL_EXPORT_SYMBOL(iwl_parse_tas_selection); static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { @@ -552,10 +564,16 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); if (!ret) { - if (cmd_ver < 9) - value &= DSM_UNII4_ALLOW_BITMAP_CMD_V8; - else - value &= DSM_UNII4_ALLOW_BITMAP; + value &= DSM_UNII4_ALLOW_BITMAP; + + /* Since version 9, bits 4 and 5 are supported + * regardless of this capability. + */ + if (cmd_ver < 9 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA)) + value &= ~(DSM_VALUE_UNII4_CANADA_OVERRIDE_MSK | + DSM_VALUE_UNII4_CANADA_EN_MSK); cmd->oem_unii4_allow_bitmap = cpu_to_le32(value); } @@ -564,7 +582,13 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, if (!ret) { if (cmd_ver < 8) value &= ~ACTIVATE_5G2_IN_WW_MASK; - if (cmd_ver < 12) + + /* Since version 12, bits 5 and 6 are supported + * regardless of this capability. + */ + if (cmd_ver < 12 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA)) value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V11; cmd->chan_state_active_bitmap = cpu_to_le32(value); @@ -634,3 +658,19 @@ int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, GET_BIOS_TABLE(dsm, fwrt, func, value); } IWL_EXPORT_SYMBOL(iwl_bios_get_dsm); + +bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc) +{ + /* Some kind of regulatory mess means we need to currently disallow + * puncturing in the US and Canada unless enabled in BIOS. + */ + switch (mcc) { + case IWL_MCC_US: + return puncturing & IWL_UEFI_CNV_PUNCTURING_USA_EN_MSK; + case IWL_MCC_CANADA: + return puncturing & IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK; + default: + return true; + } +} +IWL_EXPORT_SYMBOL(iwl_puncturing_is_allowed_in_bios); diff --git a/fw/regulatory.h b/fw/regulatory.h index e2c056f483c1..b355d7bef14c 100644 --- a/fw/regulatory.h +++ b/fw/regulatory.h @@ -40,11 +40,21 @@ #define IWL_PPAG_ETSI_CHINA_MASK 3 #define IWL_PPAG_REV3_MASK 0x7FF -#define IWL_WTAS_ENABLED_MSK 0x1 -#define IWL_WTAS_OVERRIDE_IEC_MSK 0x2 -#define IWL_WTAS_ENABLE_IEC_MSK 0x4 +#define IWL_WTAS_ENABLED_MSK BIT(0) +#define IWL_WTAS_OVERRIDE_IEC_MSK BIT(1) +#define IWL_WTAS_ENABLE_IEC_MSK BIT(2) +#define IWL_WTAS_CANADA_UHB_MSK BIT(15) #define IWL_WTAS_USA_UHB_MSK BIT(16) +struct iwl_tas_selection_data { + u8 override_tas_iec:1, + enable_tas_iec:1, + usa_tas_uhb_allowed:1, + canada_tas_uhb_allowed:1; +}; + +#define BIOS_MCC_CHINA 0x434e + /* * The profile for revision 2 is a superset of revision 1, which is in * turn a superset of revision 0. So we can store all revisions @@ -95,11 +105,11 @@ struct iwl_ppag_chain { }; struct iwl_tas_data { - __le32 block_list_size; - __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; - u8 override_tas_iec; - u8 enable_tas_iec; - u8 usa_tas_uhb_allowed; + u8 block_list_size; + u16 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; + u8 table_source; + u8 table_revision; + u32 tas_selection; }; /* For DSM revision 0 and 4 */ @@ -142,12 +152,11 @@ enum iwl_dsm_unii4_bitmap { DSM_VALUE_UNII4_CANADA_EN_MSK = BIT(5), }; -#define DSM_UNII4_ALLOW_BITMAP_CMD_V8 (DSM_VALUE_UNII4_US_OVERRIDE_MSK | \ - DSM_VALUE_UNII4_US_EN_MSK | \ - DSM_VALUE_UNII4_ETSI_OVERRIDE_MSK | \ - DSM_VALUE_UNII4_ETSI_EN_MSK) -#define DSM_UNII4_ALLOW_BITMAP (DSM_UNII4_ALLOW_BITMAP_CMD_V8 | \ - DSM_VALUE_UNII4_CANADA_OVERRIDE_MSK | \ +#define DSM_UNII4_ALLOW_BITMAP (DSM_VALUE_UNII4_US_OVERRIDE_MSK |\ + DSM_VALUE_UNII4_US_EN_MSK |\ + DSM_VALUE_UNII4_ETSI_OVERRIDE_MSK |\ + DSM_VALUE_UNII4_ETSI_EN_MSK |\ + DSM_VALUE_UNII4_CANADA_OVERRIDE_MSK |\ DSM_VALUE_UNII4_CANADA_EN_MSK) enum iwl_dsm_values_rfi { @@ -182,9 +191,8 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); bool iwl_is_tas_approved(void); -int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, - struct iwl_tas_data *tas_data, - const u32 tas_selection); +struct iwl_tas_selection_data +iwl_parse_tas_selection(const u32 tas_selection, const u8 tbl_rev); int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); @@ -217,4 +225,29 @@ static inline u32 iwl_bios_get_ppag_flags(const u32 ppag_modes, return ppag_modes & (ppag_ver < 3 ? IWL_PPAG_ETSI_CHINA_MASK : IWL_PPAG_REV3_MASK); } + +bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc); + +#define IWL_DSBR_FW_MODIFIED_URM_MASK BIT(8) +#define IWL_DSBR_PERMANENT_URM_MASK BIT(9) + +int iwl_bios_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); + +static inline void iwl_bios_setup_step(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt) +{ + u32 dsbr; + + if (!trans->trans_cfg->integrated) + return; + + if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + return; + + if (iwl_bios_get_dsbr(fwrt, &dsbr)) + dsbr = 0; + + trans->dsbr_urm_fw_dependent = !!(dsbr & IWL_DSBR_FW_MODIFIED_URM_MASK); + trans->dsbr_urm_permanent = !!(dsbr & IWL_DSBR_PERMANENT_URM_MASK); +} #endif /* __fw_regulatory_h__ */ diff --git a/fw/uefi.c b/fw/uefi.c index fb982d4fe851..434eed4130b9 100644 --- a/fw/uefi.c +++ b/fw/uefi.c @@ -13,9 +13,12 @@ #include <linux/efi.h> #include "fw/runtime.h" -#define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \ - 0xb2, 0xec, 0xf5, 0xa3, \ - 0x59, 0x4f, 0x4a, 0xea) +#define IWL_EFI_WIFI_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \ + 0xb2, 0xec, 0xf5, 0xa3, \ + 0x59, 0x4f, 0x4a, 0xea) +#define IWL_EFI_WIFI_BT_GUID EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, \ + 0x8d, 0x03, 0x77, 0x2e, \ + 0xcc, 0x3d, 0xa5, 0x31) struct iwl_uefi_pnvm_mem_desc { __le32 addr; @@ -61,7 +64,7 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) *len = 0; - data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID, + data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_WIFI_GUID, &package_size); if (IS_ERR(data)) { IWL_DEBUG_FW(trans, @@ -76,18 +79,18 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) return data; } -static -void *iwl_uefi_get_verified_variable(struct iwl_trans *trans, - efi_char16_t *uefi_var_name, - char *var_name, - unsigned int expected_size, - unsigned long *size) +static void * +iwl_uefi_get_verified_variable_guid(struct iwl_trans *trans, + efi_guid_t *guid, + efi_char16_t *uefi_var_name, + char *var_name, + unsigned int expected_size, + unsigned long *size) { void *var; unsigned long var_size; - var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID, - &var_size); + var = iwl_uefi_get_variable(uefi_var_name, guid, &var_size); if (IS_ERR(var)) { IWL_DEBUG_RADIO(trans, @@ -112,6 +115,18 @@ void *iwl_uefi_get_verified_variable(struct iwl_trans *trans, return var; } +static void * +iwl_uefi_get_verified_variable(struct iwl_trans *trans, + efi_char16_t *uefi_var_name, + char *var_name, + unsigned int expected_size, + unsigned long *size) +{ + return iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_GUID, + uefi_var_name, var_name, + expected_size, size); +} + int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data) { @@ -311,8 +326,9 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; - data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME, - "STEP", sizeof(*data), NULL); + data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID, + IWL_UEFI_STEP_NAME, + "STEP", sizeof(*data), NULL); if (IS_ERR(data)) return; @@ -554,27 +570,31 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, struct iwl_tas_data *tas_data) { struct uefi_cnv_var_wtas *uefi_tas; - int ret = 0, enabled, i; + int ret, enabled; uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME, "WTAS", sizeof(*uefi_tas), NULL); if (IS_ERR(uefi_tas)) return -EINVAL; - if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) { + if (uefi_tas->revision < IWL_UEFI_MIN_WTAS_REVISION || + uefi_tas->revision > IWL_UEFI_MAX_WTAS_REVISION) { ret = -EINVAL; IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n", uefi_tas->revision); goto out; } - enabled = iwl_parse_tas_selection(fwrt, tas_data, - uefi_tas->tas_selection); - if (!enabled) { - IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); - ret = 0; - goto out; - } + IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", + uefi_tas->tas_selection); + + enabled = uefi_tas->tas_selection & IWL_WTAS_ENABLED_MSK; + tas_data->table_source = BIOS_SOURCE_UEFI; + tas_data->table_revision = uefi_tas->revision; + tas_data->tas_selection = uefi_tas->tas_selection; + + IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n", + enabled ? "is" : "not"); IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", uefi_tas->revision); @@ -584,15 +604,16 @@ int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, ret = -EINVAL; goto out; } - tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size); + + tas_data->block_list_size = uefi_tas->black_list_size; IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size); - for (i = 0; i < uefi_tas->black_list_size; i++) { - tas_data->block_list_array[i] = - cpu_to_le32(uefi_tas->black_list[i]); + for (u8 i = 0; i < uefi_tas->black_list_size; i++) { + tas_data->block_list_array[i] = uefi_tas->black_list[i]; IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", uefi_tas->black_list[i]); } + ret = enabled; out: kfree(uefi_tas); return ret; @@ -638,7 +659,7 @@ int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) goto out; } - if (data->mcc != UEFI_MCC_CHINA) { + if (data->mcc != BIOS_MCC_CHINA) { ret = -EINVAL; IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n"); goto out; @@ -729,3 +750,58 @@ out: kfree(data); return ret; } + +int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_puncturing_data *data; + /* default value is not enabled if there is any issue in reading + * uefi variable or revision is not supported + */ + int puncturing = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, + IWL_UEFI_PUNCTURING_NAME, + "UefiCnvWlanPuncturing", + sizeof(*data), NULL); + if (IS_ERR(data)) + return puncturing; + + if (data->revision != IWL_UEFI_PUNCTURING_REVISION) { + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n", + data->revision); + } else { + puncturing = data->puncturing & IWL_UEFI_PUNCTURING_REV0_MASK; + IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n", + puncturing); + } + + kfree(data); + return puncturing; +} +IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing); + +int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) +{ + struct uefi_cnv_wlan_dsbr_data *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable_guid(fwrt->trans, + &IWL_EFI_WIFI_BT_GUID, + IWL_UEFI_DSBR_NAME, "DSBR", + sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_DSBR_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSBR revision:%d\n", + data->revision); + goto out; + } + *value = data->config; + IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from UEFI value: 0x%x\n", + *value); +out: + kfree(data); + return ret; +} diff --git a/fw/uefi.h b/fw/uefi.h index 1f8884ca8997..0c8943a8bd01 100644 --- a/fw/uefi.h +++ b/fw/uefi.h @@ -22,6 +22,8 @@ #define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" #define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg" #define IWL_UEFI_WBEM_NAME L"UefiCnvWlanWBEM" +#define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing" +#define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR" #define IWL_SGOM_MAP_SIZE 339 @@ -32,12 +34,15 @@ #define IWL_UEFI_WGDS_REVISION 3 #define IWL_UEFI_MIN_PPAG_REV 1 #define IWL_UEFI_MAX_PPAG_REV 3 -#define IWL_UEFI_WTAS_REVISION 1 +#define IWL_UEFI_MIN_WTAS_REVISION 1 +#define IWL_UEFI_MAX_WTAS_REVISION 2 #define IWL_UEFI_SPLC_REVISION 0 #define IWL_UEFI_WRDD_REVISION 0 #define IWL_UEFI_ECKV_REVISION 0 #define IWL_UEFI_WBEM_REVISION 0 #define IWL_UEFI_DSM_REVISION 4 +#define IWL_UEFI_PUNCTURING_REVISION 0 +#define IWL_UEFI_DSBR_REVISION 1 struct pnvm_sku_package { u8 rev; @@ -149,8 +154,6 @@ struct uefi_cnv_var_splc { u32 default_pwr_limit; } __packed; -#define UEFI_MCC_CHINA 0x434e - /* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI * @revision: the revision of the table * @mcc: country identifier as defined in ISO/IEC 3166-1 Alpha 2 code @@ -194,6 +197,39 @@ struct uefi_cnv_wlan_wbem_data { u32 wbem_320mhz_per_mcc; } __packed; +enum iwl_uefi_cnv_puncturing_flags { + IWL_UEFI_CNV_PUNCTURING_USA_EN_MSK = BIT(0), + IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK = BIT(1), +}; + +#define IWL_UEFI_PUNCTURING_REV0_MASK (IWL_UEFI_CNV_PUNCTURING_USA_EN_MSK | \ + IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK) +/** + * struct uefi_cnv_var_puncturing_data - controlling channel + * puncturing for few countries. + * @revision: the revision of the table + * @puncturing: enablement of channel puncturing per mcc + * see &enum iwl_uefi_cnv_puncturing_flags. + */ +struct uefi_cnv_var_puncturing_data { + u8 revision; + u32 puncturing; +} __packed; + +/** + * struct uefi_cnv_wlan_dsbr_data - BIOS STEP configuration information + * @revision: the revision of the table + * @config: STEP configuration flags: + * bit 8, switch to URM depending on FW setting + * bit 9, switch to URM + * + * Platform information for STEP configuration/workarounds. + */ +struct uefi_cnv_wlan_dsbr_data { + u8 revision; + u32 config; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -224,6 +260,8 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); int iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -320,5 +358,17 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, { return 0; } + +static inline +int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt) +{ + return 0; +} + +static inline +int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #endif /* __iwl_fw_uefi__ */ diff --git a/iwl-config.h b/iwl-config.h index b2abd4fd1944..2b6a80142aba 100644 --- a/iwl-config.h +++ b/iwl-config.h @@ -38,6 +38,7 @@ enum iwl_device_family { IWL_DEVICE_FAMILY_AX210, IWL_DEVICE_FAMILY_BZ, IWL_DEVICE_FAMILY_SC, + IWL_DEVICE_FAMILY_DR, }; /* @@ -102,6 +103,10 @@ enum iwl_nvm_type { #define ANT_ABC (ANT_A | ANT_B | ANT_C) +#define IWL_FW_AND_PNVM(pfx, api) \ + MODULE_FIRMWARE(pfx "-" __stringify(api) ".ucode"); \ + MODULE_FIRMWARE(pfx ".pnvm") + static inline u8 num_of_ant(u8 mask) { return !!((mask) & ANT_A) + @@ -424,6 +429,8 @@ struct iwl_cfg { #define IWL_CFG_MAC_TYPE_SC2 0x49 #define IWL_CFG_MAC_TYPE_SC2F 0x4A #define IWL_CFG_MAC_TYPE_BZ_W 0x4B +#define IWL_CFG_MAC_TYPE_BR 0x4C +#define IWL_CFG_MAC_TYPE_DR 0x4D #define IWL_CFG_RF_TYPE_TH 0x105 #define IWL_CFG_RF_TYPE_TH1 0x108 @@ -434,6 +441,7 @@ struct iwl_cfg { #define IWL_CFG_RF_TYPE_GF 0x10D #define IWL_CFG_RF_TYPE_FM 0x112 #define IWL_CFG_RF_TYPE_WH 0x113 +#define IWL_CFG_RF_TYPE_PE 0x114 #define IWL_CFG_RF_ID_TH 0x1 #define IWL_CFG_RF_ID_TH1 0x1 @@ -504,7 +512,10 @@ extern const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg; extern const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg; extern const struct iwl_cfg_trans_params iwl_ma_trans_cfg; extern const struct iwl_cfg_trans_params iwl_bz_trans_cfg; +extern const struct iwl_cfg_trans_params iwl_gl_trans_cfg; extern const struct iwl_cfg_trans_params iwl_sc_trans_cfg; +extern const struct iwl_cfg_trans_params iwl_dr_trans_cfg; +extern const struct iwl_cfg_trans_params iwl_br_trans_cfg; extern const char iwl9162_name[]; extern const char iwl9260_name[]; extern const char iwl9260_1_name[]; @@ -544,11 +555,14 @@ extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_bz_name[]; extern const char iwl_fm_name[]; +extern const char iwl_wh_name[]; extern const char iwl_gl_name[]; extern const char iwl_mtp_name[]; extern const char iwl_sc_name[]; extern const char iwl_sc2_name[]; extern const char iwl_sc2f_name[]; +extern const char iwl_dr_name[]; +extern const char iwl_br_name[]; #if IS_ENABLED(CONFIG_IWLDVM) extern const struct iwl_cfg iwl5300_agn_cfg; extern const struct iwl_cfg iwl5100_agn_cfg; @@ -647,7 +661,6 @@ extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long; extern const struct iwl_cfg iwl_cfg_ma; extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0; -extern const struct iwl_cfg iwl_cfg_so_a0_ms_a0; extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_bz; @@ -656,6 +669,8 @@ extern const struct iwl_cfg iwl_cfg_gl; extern const struct iwl_cfg iwl_cfg_sc; extern const struct iwl_cfg iwl_cfg_sc2; extern const struct iwl_cfg iwl_cfg_sc2f; +extern const struct iwl_cfg iwl_cfg_dr; +extern const struct iwl_cfg iwl_cfg_br; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/iwl-context-info-gen3.h b/iwl-context-info-gen3.h index 5b62933134cf..cd25a1b9f2ff 100644 --- a/iwl-context-info-gen3.h +++ b/iwl-context-info-gen3.h @@ -76,7 +76,17 @@ enum iwl_prph_scratch_flags { IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE = BIT(29), }; -/* +/** + * enum iwl_prph_scratch_ext_flags - PRPH scratch control ext flags + * @IWL_PRPH_SCRATCH_EXT_URM_FW: switch to URM mode based on fw setting + * @IWL_PRPH_SCRATCH_EXT_URM_PERM: switch to permanent URM mode + */ +enum iwl_prph_scratch_ext_flags { + IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4), + IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5), +}; + +/** * struct iwl_prph_scratch_version - version structure * @mac_id: SKU and revision id * @version: prph scratch information version id @@ -90,17 +100,18 @@ struct iwl_prph_scratch_version { __le16 reserved; } __packed; /* PERIPH_SCRATCH_VERSION_S */ -/* +/** * struct iwl_prph_scratch_control - control structure * @control_flags: context information flags see &enum iwl_prph_scratch_flags - * @reserved: reserved + * @control_flags_ext: context information for extended flags, + * see &enum iwl_prph_scratch_ext_flags */ struct iwl_prph_scratch_control { __le32 control_flags; - __le32 reserved; + __le32 control_flags_ext; } __packed; /* PERIPH_SCRATCH_CONTROL_S */ -/* +/** * struct iwl_prph_scratch_pnvm_cfg - PNVM scratch * @pnvm_base_addr: PNVM start address * @pnvm_size: the size of the PNVM image in bytes @@ -120,7 +131,8 @@ struct iwl_prph_scratch_pnvm_cfg { struct iwl_prph_scrath_mem_desc_addr_array { __le64 mem_descs[IPC_DRAM_MAP_ENTRY_NUM_MAX]; } __packed; /* PERIPH_SCRATCH_MEM_DESC_ADDR_ARRAY_S_VER_1 */ -/* + +/** * struct iwl_prph_scratch_hwm_cfg - hwm config * @hwm_base_addr: hwm start address * @hwm_size: hwm size in DWs @@ -132,7 +144,7 @@ struct iwl_prph_scratch_hwm_cfg { __le32 debug_token_config; } __packed; /* PERIPH_SCRATCH_HWM_CFG_S */ -/* +/** * struct iwl_prph_scratch_rbd_cfg - RBDs configuration * @free_rbd_addr: default queue free RB CB base address * @reserved: reserved @@ -142,10 +154,11 @@ struct iwl_prph_scratch_rbd_cfg { __le32 reserved; } __packed; /* PERIPH_SCRATCH_RBD_CFG_S */ -/* +/** * struct iwl_prph_scratch_uefi_cfg - prph scratch reduce power table * @base_addr: reduce power table address * @size: the size of the entire power table image + * @reserved: (reserved) */ struct iwl_prph_scratch_uefi_cfg { __le64 base_addr; @@ -153,7 +166,7 @@ struct iwl_prph_scratch_uefi_cfg { __le32 reserved; } __packed; /* PERIPH_SCRATCH_UEFI_CFG_S */ -/* +/** * struct iwl_prph_scratch_step_cfg - prph scratch step configuration * @mbx_addr_0: [0:7] revision, * [8:15] cnvi_to_cnvr length, @@ -167,13 +180,14 @@ struct iwl_prph_scratch_step_cfg { __le32 mbx_addr_1; } __packed; -/* +/** * struct iwl_prph_scratch_ctrl_cfg - prph scratch ctrl and config * @version: version information of context info and HW * @control: control flags of FH configurations * @pnvm_cfg: ror configuration * @hwm_cfg: hwm configuration * @rbd_cfg: default RX queue configuration + * @reduce_power_cfg: UEFI power reduction table * @step_cfg: step configuration */ struct iwl_prph_scratch_ctrl_cfg { @@ -186,7 +200,7 @@ struct iwl_prph_scratch_ctrl_cfg { struct iwl_prph_scratch_step_cfg step_cfg; } __packed; /* PERIPH_SCRATCH_CTRL_CFG_S */ -/* +/** * struct iwl_prph_scratch - peripheral scratch mapping * @ctrl_cfg: control and configuration of prph scratch * @dram: firmware images addresses in DRAM @@ -202,7 +216,7 @@ struct iwl_prph_scratch { struct iwl_context_info_dram dram; } __packed; /* PERIPH_SCRATCH_S */ -/* +/** * struct iwl_prph_info - peripheral information * @boot_stage_mirror: reflects the value in the Boot Stage CSR register * @ipc_status_mirror: reflects the value in the IPC Status CSR register @@ -216,7 +230,7 @@ struct iwl_prph_info { __le32 reserved; } __packed; /* PERIPH_INFO_S */ -/* +/** * struct iwl_context_info_gen3 - device INIT configuration * @version: version of the context information * @size: size of context information in DWs diff --git a/iwl-context-info.h b/iwl-context-info.h index 1a1321db137c..dfd44fabf237 100644 --- a/iwl-context-info.h +++ b/iwl-context-info.h @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020, 2022 Intel Corporation + * Copyright (C) 2018-2020, 2022, 2024 Intel Corporation */ #ifndef __iwl_context_info_file_h__ #define __iwl_context_info_file_h__ -/* maximmum number of DRAM map entries supported by FW */ +/* maximum number of DRAM map entries supported by FW */ #define IWL_MAX_DRAM_ENTRY 64 #define CSR_CTXT_INFO_BA 0x40 @@ -53,11 +53,12 @@ enum iwl_context_info_flags { IWL_CTXT_INFO_RB_SIZE_32K = 0xe, }; -/* +/** * struct iwl_context_info_version - version structure * @mac_id: SKU and revision id * @version: context information version id * @size: the size of the context information in DWs + * @reserved: (reserved) */ struct iwl_context_info_version { __le16 mac_id; @@ -66,16 +67,17 @@ struct iwl_context_info_version { __le16 reserved; } __packed; -/* +/** * struct iwl_context_info_control - version structure * @control_flags: context information flags see &enum iwl_context_info_flags + * @reserved: (reserved) */ struct iwl_context_info_control { __le32 control_flags; __le32 reserved; } __packed; -/* +/** * struct iwl_context_info_dram - images DRAM map * each entry in the map represents a DRAM chunk of up to 32 KB * @umac_img: UMAC image DRAM map @@ -88,7 +90,7 @@ struct iwl_context_info_dram { __le64 virtual_img[IWL_MAX_DRAM_ENTRY]; } __packed; -/* +/** * struct iwl_context_info_rbd_cfg - RBDs configuration * @free_rbd_addr: default queue free RB CB base address * @used_rbd_addr: default queue used RB CB base address @@ -100,10 +102,11 @@ struct iwl_context_info_rbd_cfg { __le64 status_wr_ptr; } __packed; -/* +/** * struct iwl_context_info_hcmd_cfg - command queue configuration * @cmd_queue_addr: address of command queue * @cmd_queue_size: number of entries + * @reserved: (reserved) */ struct iwl_context_info_hcmd_cfg { __le64 cmd_queue_addr; @@ -111,10 +114,11 @@ struct iwl_context_info_hcmd_cfg { u8 reserved[7]; } __packed; -/* +/** * struct iwl_context_info_dump_cfg - Core Dump configuration * @core_dump_addr: core dump (debug DRAM address) start address * @core_dump_size: size, in DWs + * @reserved: (reserved) */ struct iwl_context_info_dump_cfg { __le64 core_dump_addr; @@ -122,10 +126,11 @@ struct iwl_context_info_dump_cfg { __le32 reserved; } __packed; -/* +/** * struct iwl_context_info_pnvm_cfg - platform NVM data configuration * @platform_nvm_addr: Platform NVM data start address * @platform_nvm_size: size in DWs + * @reserved: (reserved) */ struct iwl_context_info_pnvm_cfg { __le64 platform_nvm_addr; @@ -133,11 +138,12 @@ struct iwl_context_info_pnvm_cfg { __le32 reserved; } __packed; -/* +/** * struct iwl_context_info_early_dbg_cfg - early debug configuration for * dumping DRAM addresses * @early_debug_addr: early debug start address * @early_debug_size: size in DWs + * @reserved: (reserved) */ struct iwl_context_info_early_dbg_cfg { __le64 early_debug_addr; @@ -145,16 +151,20 @@ struct iwl_context_info_early_dbg_cfg { __le32 reserved; } __packed; -/* +/** * struct iwl_context_info - device INIT configuration * @version: version information of context info and HW * @control: control flags of FH configurations + * @reserved0: (reserved) * @rbd_cfg: default RX queue configuration * @hcmd_cfg: command queue configuration + * @reserved1: (reserved) * @dump_cfg: core dump data * @edbg_cfg: early debug configuration * @pnvm_cfg: platform nvm configuration + * @reserved2: (reserved) * @dram: firmware image addresses in DRAM + * @reserved3: (reserved) */ struct iwl_context_info { struct iwl_context_info_version version; diff --git a/iwl-csr.h b/iwl-csr.h index 98563757ce2c..be9e464c9b7b 100644 --- a/iwl-csr.h +++ b/iwl-csr.h @@ -167,13 +167,15 @@ #define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) #define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) -#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) -#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ -#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ -#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) -#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ +#define CSR_HW_IF_CONFIG_REG_HAP_WAKE 0x00080000 +/* NOTE: EEPROM_OWN_SEM is no longer defined for new HW */ +#define CSR_HW_IF_CONFIG_REG_EEPROM_OWN_SEM 0x00200000 +#define CSR_HW_IF_CONFIG_REG_PCI_OWN_SET 0x00400000 +#define CSR_HW_IF_CONFIG_REG_IAMT_UP 0x01000000 +#define CSR_HW_IF_CONFIG_REG_ME_OWN 0x02000000 +#define CSR_HW_IF_CONFIG_REG_WAKE_ME 0x08000000 +#define CSR_HW_IF_CONFIG_REG_WAKE_ME_PCIE_OWNER_EN 0x10000000 +#define CSR_HW_IF_CONFIG_REG_PERSISTENCE 0x40000000 #define CSR_MBOX_SET_REG_OS_ALIVE BIT(5) @@ -351,7 +353,6 @@ enum { #define CSR_HW_RF_ID_TYPE_HRCDB (0x00109F00) #define CSR_HW_RF_ID_TYPE_GF (0x0010D000) #define CSR_HW_RF_ID_TYPE_GF4 (0x0010E000) -#define CSR_HW_RF_ID_TYPE_MS (0x00111000) #define CSR_HW_RF_ID_TYPE_FM (0x00112000) #define CSR_HW_RF_ID_TYPE_WP (0x00113000) diff --git a/iwl-drv.c b/iwl-drv.c index aaaabd67f959..352b6e73e08f 100644 --- a/iwl-drv.c +++ b/iwl-drv.c @@ -22,6 +22,7 @@ #include "iwl-modparams.h" #include "fw/api/alive.h" #include "fw/api/mac.h" +#include "fw/api/mac-cfg.h" /****************************************************************************** * @@ -137,8 +138,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) memset(&drv->fw, 0, sizeof(drv->fw)); } -static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, - struct fw_sec *sec) +static int iwl_alloc_fw_desc(struct fw_desc *desc, struct fw_sec *sec) { void *data; @@ -318,17 +318,6 @@ struct iwl_firmware_pieces { size_t n_mem_tlv; }; -/* - * These functions are just to extract uCode section data from the pieces - * structure. - */ -static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec) -{ - return &pieces->img[type].sec[sec]; -} - static void alloc_sec_data(struct iwl_firmware_pieces *pieces, enum iwl_ucode_type type, int sec) @@ -389,22 +378,18 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, /* * Gets uCode section from tlv. */ -static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, - const void *data, enum iwl_ucode_type type, - int size) +static int iwl_store_ucode_sec(struct fw_img_parsing *img, + const void *data, int size) { - struct fw_img_parsing *img; struct fw_sec *sec; const struct fw_sec_parsing *sec_parse; size_t alloc_size; - if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) - return -1; + if (WARN_ON(!img || !data)) + return -EINVAL; sec_parse = (const struct fw_sec_parsing *)data; - img = &pieces->img[type]; - alloc_size = sizeof(*img->sec) * (img->sec_counter + 1); sec = krealloc(img->sec, alloc_size, GFP_KERNEL); if (!sec) @@ -900,18 +885,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_SEC_RT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_REGULAR], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SEC_INIT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_INIT], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SEC_WOWLAN: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_WOWLAN], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_DEF_CALIB: @@ -932,18 +917,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, FW_PHY_CFG_RX_CHAIN_POS; break; case IWL_UCODE_TLV_SECURE_SEC_RT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_REGULAR], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SECURE_SEC_INIT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_INIT], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_WOWLAN], + tlv_data, tlv_len); drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_NUM_OF_CPU: @@ -1110,9 +1095,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } case IWL_UCODE_TLV_SEC_RT_USNIFFER: *usniffer_images = true; - iwl_store_ucode_sec(pieces, tlv_data, - IWL_UCODE_REGULAR_USNIFFER, - tlv_len); + iwl_store_ucode_sec(&pieces->img[IWL_UCODE_REGULAR_USNIFFER], + tlv_data, tlv_len); break; case IWL_UCODE_TLV_PAGING: if (tlv_len != sizeof(u32)) @@ -1197,7 +1181,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(*fseq_ver)) goto invalid_tlv_len; - IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n", + IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n", fseq_ver->version); } break; @@ -1205,7 +1189,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; if (le32_to_cpup((const __le32 *)tlv_data) > - IWL_MVM_STATION_COUNT_MAX) { + IWL_STATION_COUNT_MAX) { IWL_ERR(drv, "%d is an invalid number of station\n", le32_to_cpup((const __le32 *)tlv_data)); @@ -1214,6 +1198,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, capa->num_stations = le32_to_cpup((const __le32 *)tlv_data); break; + case IWL_UCODE_TLV_FW_NUM_LINKS: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + if (le32_to_cpup((const __le32 *)tlv_data) > + IWL_FW_MAX_LINK_ID + 1) { + IWL_ERR(drv, + "%d is an invalid number of links\n", + le32_to_cpup((const __le32 *)tlv_data)); + goto tlv_error; + } + capa->num_links = + le32_to_cpup((const __le32 *)tlv_data); + break; case IWL_UCODE_TLV_FW_NUM_BEACONS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; @@ -1337,26 +1334,31 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, return -EINVAL; } -static int iwl_alloc_ucode(struct iwl_drv *drv, - struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type) +static int iwl_alloc_ucode_mem(struct fw_img *out, struct fw_img_parsing *img) { - int i; struct fw_desc *sec; - sec = kcalloc(pieces->img[type].sec_counter, sizeof(*sec), GFP_KERNEL); + sec = kcalloc(img->sec_counter, sizeof(*sec), GFP_KERNEL); if (!sec) return -ENOMEM; - drv->fw.img[type].sec = sec; - drv->fw.img[type].num_sec = pieces->img[type].sec_counter; - for (i = 0; i < pieces->img[type].sec_counter; i++) - if (iwl_alloc_fw_desc(drv, &sec[i], get_sec(pieces, type, i))) + out->sec = sec; + out->num_sec = img->sec_counter; + + for (int i = 0; i < out->num_sec; i++) + if (iwl_alloc_fw_desc(&sec[i], &img->sec[i])) return -ENOMEM; return 0; } +static int iwl_alloc_ucode(struct iwl_drv *drv, + struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type) +{ + return iwl_alloc_ucode_mem(&drv->fw.img[type], &pieces->img[type]); +} + static int validate_sec_sizes(struct iwl_drv *drv, struct iwl_firmware_pieces *pieces, const struct iwl_cfg *cfg) @@ -1413,26 +1415,39 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) const struct iwl_op_mode_ops *ops = op->ops; struct dentry *dbgfs_dir = NULL; struct iwl_op_mode *op_mode = NULL; + int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY; /* also protects start/stop from racing against each other */ lockdep_assert_held(&iwlwifi_opmode_table_mtx); + for (retry = 0; retry <= max_retry; retry++) { + #ifdef CONFIG_IWLWIFI_DEBUGFS - drv->dbgfs_op_mode = debugfs_create_dir(op->name, - drv->dbgfs_drv); - dbgfs_dir = drv->dbgfs_op_mode; + drv->dbgfs_op_mode = debugfs_create_dir(op->name, + drv->dbgfs_drv); + dbgfs_dir = drv->dbgfs_op_mode; #endif - op_mode = ops->start(drv->trans, drv->trans->cfg, - &drv->fw, dbgfs_dir); - if (op_mode) - return op_mode; + op_mode = ops->start(drv->trans, drv->trans->cfg, + &drv->fw, dbgfs_dir); + + if (!IS_ERR(op_mode)) + return op_mode; + + if (test_bit(STATUS_TRANS_DEAD, &drv->trans->status)) + break; #ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(drv->dbgfs_op_mode); - drv->dbgfs_op_mode = NULL; + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; #endif + if (PTR_ERR(op_mode) != -ETIMEDOUT) + break; + + IWL_ERR(drv, "retry init count %d\n", retry); + } + return NULL; } @@ -1479,7 +1494,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; - fw->ucode_capa.num_stations = IWL_MVM_STATION_COUNT_MAX; + fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX; fw->ucode_capa.num_beacons = 1; /* dump all fw memory areas by default */ fw->dbg.dump_mask = 0xffffffff; @@ -1934,6 +1949,7 @@ module_init(iwl_drv_init); static void __exit iwl_drv_exit(void) { iwl_pci_unregister_driver(); + iwl_trans_free_restart_list(); #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(iwl_dbgfs_root); diff --git a/iwl-drv.h b/iwl-drv.h index 1549ff429549..854957bdf79d 100644 --- a/iwl-drv.h +++ b/iwl-drv.h @@ -85,7 +85,7 @@ void iwl_drv_stop(struct iwl_drv *drv); * everything is built-in, then we can avoid that. */ #ifdef CONFIG_IWLWIFI_OPMODE_MODULAR -#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_NS_GPL(sym, IWLWIFI) +#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_NS_GPL(sym, "IWLWIFI") #else #define IWL_EXPORT_SYMBOL(sym) #endif @@ -98,6 +98,9 @@ void iwl_drv_stop(struct iwl_drv *drv); #define VISIBLE_IF_IWLWIFI_KUNIT static #endif +/* max retry for init flow */ +#define IWL_MAX_INIT_RETRY 2 + #define FW_NAME_PRE_BUFSIZE 64 struct iwl_trans; const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf); @@ -526,5 +526,5 @@ void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr, if (interrupts_enabled) iwl_trans_interrupts(trans, true); - iwl_trans_fw_error(trans, false); + iwl_trans_fw_error(trans, IWL_ERR_TYPE_NMI_FORCED); } diff --git a/iwl-nvm-parse.c b/iwl-nvm-parse.c index d902121da009..9f7e013252fe 100644 --- a/iwl-nvm-parse.c +++ b/iwl-nvm-parse.c @@ -141,8 +141,10 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { /** * enum iwl_nvm_channel_flags - channel flags in NVM * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo - * @NVM_CHANNEL_IBSS: usable as an IBSS channel - * @NVM_CHANNEL_ACTIVE: active scanning allowed + * @NVM_CHANNEL_IBSS: usable as an IBSS channel and deprecated + * when %IWL_NVM_SBANDS_FLAGS_LAR enabled. + * @NVM_CHANNEL_ACTIVE: active scanning allowed and allows IBSS + * when %IWL_NVM_SBANDS_FLAGS_LAR enabled. * @NVM_CHANNEL_RADAR: radar detection required * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS diff --git a/iwl-op-mode.h b/iwl-op-mode.h index 8ef5ed2db051..34eca1a568ea 100644 --- a/iwl-op-mode.h +++ b/iwl-op-mode.h @@ -45,6 +45,55 @@ struct iwl_cfg; */ /** + * enum iwl_fw_error_type - FW error types/sources + * @IWL_ERR_TYPE_IRQ: "normal" FW error through an IRQ + * @IWL_ERR_TYPE_NMI_FORCED: NMI was forced by driver + * @IWL_ERR_TYPE_RESET_HS_TIMEOUT: reset handshake timed out, + * any debug collection must happen synchronously as + * the device will be shut down + * @IWL_ERR_TYPE_CMD_QUEUE_FULL: command queue was full + */ +enum iwl_fw_error_type { + IWL_ERR_TYPE_IRQ, + IWL_ERR_TYPE_NMI_FORCED, + IWL_ERR_TYPE_RESET_HS_TIMEOUT, + IWL_ERR_TYPE_CMD_QUEUE_FULL, +}; + +/** + * enum iwl_fw_error_context - error dump context + * @IWL_ERR_CONTEXT_WORKER: regular from worker context, + * opmode must acquire locks and must also check + * for @IWL_ERR_CONTEXT_ABORT after acquiring locks + * @IWL_ERR_CONTEXT_FROM_OPMODE: context is in a call + * originating from the opmode, e.g. while resetting + * or stopping the device, so opmode must not acquire + * any locks + * @IWL_ERR_CONTEXT_ABORT: after lock acquisition, indicates + * that the dump already happened via another callback + * (currently only while stopping the device) via the + * @IWL_ERR_CONTEXT_FROM_OPMODE context, and this call + * must be aborted + */ +enum iwl_fw_error_context { + IWL_ERR_CONTEXT_WORKER, + IWL_ERR_CONTEXT_FROM_OPMODE, + IWL_ERR_CONTEXT_ABORT, +}; + +/** + * struct iwl_fw_error_dump_mode - error dump mode for callback + * @type: The reason for the dump, per &enum iwl_fw_error_type. + * @context: The context for the dump, may also indicate this + * call needs to be skipped. This MUST be checked before + * and after acquiring any locks in the op-mode! + */ +struct iwl_fw_error_dump_mode { + enum iwl_fw_error_type type; + enum iwl_fw_error_context context; +}; + +/** * struct iwl_op_mode_ops - op_mode specific operations * * The op_mode exports its ops so that external components can start it and @@ -77,10 +126,11 @@ struct iwl_cfg; * reclaimed by the op_mode. This can happen when the driver is freed and * there are Tx packets pending in the transport layer. * Must be atomic - * @nic_error: error notification. Must be atomic and must be called with BH - * disabled, unless the sync parameter is true. - * @cmd_queue_full: Called when the command queue gets full. Must be atomic and - * called with BH disabled. + * @nic_error: error notification. Must be atomic, the op mode should handle + * the error (e.g. abort notification waiters) and print the error if + * applicable + * @dump_error: NIC error dump collection (can sleep, synchronous) + * @sw_reset: (maybe) initiate a software reset, return %true if started * @nic_config: configure NIC, called before firmware is started. * May sleep * @wimax_active: invoked when WiMax becomes active. May sleep @@ -104,8 +154,12 @@ struct iwl_op_mode_ops { void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb); - void (*nic_error)(struct iwl_op_mode *op_mode, bool sync); - void (*cmd_queue_full)(struct iwl_op_mode *op_mode); + void (*nic_error)(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type); + void (*dump_error)(struct iwl_op_mode *op_mode, + struct iwl_fw_error_dump_mode *mode); + bool (*sw_reset)(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type); void (*nic_config)(struct iwl_op_mode *op_mode); void (*wimax_active)(struct iwl_op_mode *op_mode); void (*time_point)(struct iwl_op_mode *op_mode, @@ -177,14 +231,19 @@ static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode, op_mode->ops->free_skb(op_mode, skb); } -static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode, bool sync) +static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) { - op_mode->ops->nic_error(op_mode, sync); + op_mode->ops->nic_error(op_mode, type); } -static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode) +static inline void iwl_op_mode_dump_error(struct iwl_op_mode *op_mode, + struct iwl_fw_error_dump_mode *mode) { - op_mode->ops->cmd_queue_full(op_mode); + might_sleep(); + + if (op_mode->ops->dump_error) + op_mode->ops->dump_error(op_mode, mode); } static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode) diff --git a/iwl-prph.h b/iwl-prph.h index dc171c29eb7b..23b2009fbb28 100644 --- a/iwl-prph.h +++ b/iwl-prph.h @@ -381,6 +381,10 @@ enum { #define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938 #define CNVI_SCU_SEQ_DATA_DW9 0xA27488 +#define CNVI_SCU_REG_FOR_ECO_1 0xA26EF8 +#define CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN BIT(4) +#define CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT BIT(5) + #define CNVI_PMU_STEP_FLOW 0xA2D588 #define CNVI_PMU_STEP_FLOW_FORCE_URM BIT(2) @@ -458,6 +462,7 @@ enum { #define REG_CRF_ID_TYPE_GF 0x410 #define REG_CRF_ID_TYPE_FM 0x910 #define REG_CRF_ID_TYPE_WHP 0xA10 +#define REG_CRF_ID_TYPE_PE 0xA30 #define HPM_DEBUG 0xA03440 #define PERSISTENCE_BIT BIT(12) diff --git a/iwl-trans.c b/iwl-trans.c index 3c9d91496c82..47854a36413e 100644 --- a/iwl-trans.c +++ b/iwl-trans.c @@ -6,6 +6,7 @@ */ #include <linux/kernel.h> #include <linux/bsearch.h> +#include <linux/list.h> #include "fw/api/tx.h" #include "iwl-trans.h" @@ -16,13 +17,200 @@ #include "pcie/internal.h" #include "iwl-context-info-gen3.h" +struct iwl_trans_dev_restart_data { + struct list_head list; + unsigned int restart_count; + time64_t last_error; + char name[]; +}; + +static LIST_HEAD(restart_data_list); +static DEFINE_SPINLOCK(restart_data_lock); + +static struct iwl_trans_dev_restart_data * +iwl_trans_get_restart_data(struct device *dev) +{ + struct iwl_trans_dev_restart_data *tmp, *data = NULL; + const char *name = dev_name(dev); + + spin_lock(&restart_data_lock); + list_for_each_entry(tmp, &restart_data_list, list) { + if (strcmp(tmp->name, name)) + continue; + data = tmp; + break; + } + spin_unlock(&restart_data_lock); + + if (data) + return data; + + data = kzalloc(struct_size(data, name, strlen(name) + 1), GFP_ATOMIC); + if (!data) + return NULL; + + strcpy(data->name, name); + spin_lock(&restart_data_lock); + list_add_tail(&data->list, &restart_data_list); + spin_unlock(&restart_data_lock); + + return data; +} + +static void iwl_trans_inc_restart_count(struct device *dev) +{ + struct iwl_trans_dev_restart_data *data; + + data = iwl_trans_get_restart_data(dev); + if (data) { + data->last_error = ktime_get_boottime_seconds(); + data->restart_count++; + } +} + +void iwl_trans_free_restart_list(void) +{ + struct iwl_trans_dev_restart_data *tmp; + + while ((tmp = list_first_entry_or_null(&restart_data_list, + typeof(*tmp), list))) { + list_del(&tmp->list); + kfree(tmp); + } +} + +struct iwl_trans_reprobe { + struct device *dev; + struct work_struct work; +}; + +static void iwl_trans_reprobe_wk(struct work_struct *wk) +{ + struct iwl_trans_reprobe *reprobe; + + reprobe = container_of(wk, typeof(*reprobe), work); + + if (device_reprobe(reprobe->dev)) + dev_err(reprobe->dev, "reprobe failed!\n"); + put_device(reprobe->dev); + kfree(reprobe); + module_put(THIS_MODULE); +} + +#define IWL_TRANS_RESET_OK_TIME 180 /* seconds */ + +static enum iwl_reset_mode +iwl_trans_determine_restart_mode(struct iwl_trans *trans) +{ + struct iwl_trans_dev_restart_data *data; + enum iwl_reset_mode at_least = 0; + unsigned int index; + static const enum iwl_reset_mode escalation_list[] = { + IWL_RESET_MODE_SW_RESET, + IWL_RESET_MODE_REPROBE, + IWL_RESET_MODE_REPROBE, + IWL_RESET_MODE_FUNC_RESET, + /* FIXME: add TOP reset */ + IWL_RESET_MODE_PROD_RESET, + /* FIXME: add TOP reset */ + IWL_RESET_MODE_PROD_RESET, + /* FIXME: add TOP reset */ + IWL_RESET_MODE_PROD_RESET, + }; + + if (trans->restart.during_reset) + at_least = IWL_RESET_MODE_REPROBE; + + data = iwl_trans_get_restart_data(trans->dev); + if (!data) + return at_least; + + if (ktime_get_boottime_seconds() - data->last_error >= + IWL_TRANS_RESET_OK_TIME) + data->restart_count = 0; + + index = data->restart_count; + if (index >= ARRAY_SIZE(escalation_list)) + index = ARRAY_SIZE(escalation_list) - 1; + + return max(at_least, escalation_list[index]); +} + +#define IWL_TRANS_RESET_DELAY (HZ * 60) + +static void iwl_trans_restart_wk(struct work_struct *wk) +{ + struct iwl_trans *trans = container_of(wk, typeof(*trans), restart.wk); + struct iwl_trans_reprobe *reprobe; + enum iwl_reset_mode mode; + + if (!trans->op_mode) + return; + + /* might have been scheduled before marked as dead, re-check */ + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return; + + iwl_op_mode_dump_error(trans->op_mode, &trans->restart.mode); + + /* + * If the opmode stopped the device while we were trying to dump and + * reset, then we'll have done the dump already (synchronized by the + * opmode lock that it will acquire in iwl_op_mode_dump_error()) and + * managed that via trans->restart.mode. + * Additionally, make sure that in such a case we won't attempt to do + * any resets now, since it's no longer requested. + */ + if (!test_and_clear_bit(STATUS_RESET_PENDING, &trans->status)) + return; + + if (!iwlwifi_mod_params.fw_restart) + return; + + mode = iwl_trans_determine_restart_mode(trans); + + iwl_trans_inc_restart_count(trans->dev); + + switch (mode) { + case IWL_RESET_MODE_SW_RESET: + IWL_ERR(trans, "Device error - SW reset\n"); + iwl_trans_opmode_sw_reset(trans, trans->restart.mode.type); + break; + case IWL_RESET_MODE_REPROBE: + IWL_ERR(trans, "Device error - reprobe!\n"); + + /* + * get a module reference to avoid doing this while unloading + * anyway and to avoid scheduling a work with code that's + * being removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(trans, "Module is being unloaded - abort\n"); + return; + } + + reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); + if (!reprobe) { + module_put(THIS_MODULE); + return; + } + reprobe->dev = get_device(trans->dev); + INIT_WORK(&reprobe->work, iwl_trans_reprobe_wk); + schedule_work(&reprobe->work); + break; + default: + iwl_trans_pcie_reset(trans, mode); + break; + } +} + struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, const struct iwl_cfg_trans_params *cfg_trans) { struct iwl_trans *trans; #ifdef CONFIG_LOCKDEP - static struct lock_class_key __key; + static struct lock_class_key __sync_cmd_key; #endif trans = devm_kzalloc(dev, sizeof(*trans) + priv_size, GFP_KERNEL); @@ -33,12 +221,14 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, #ifdef CONFIG_LOCKDEP lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", - &__key, 0); + &__sync_cmd_key, 0); #endif trans->dev = dev; trans->num_rx_queues = 1; + INIT_WORK(&trans->restart.wk, iwl_trans_restart_wk); + return trans; } @@ -81,6 +271,7 @@ int iwl_trans_init(struct iwl_trans *trans) void iwl_trans_free(struct iwl_trans *trans) { + cancel_work_sync(&trans->restart.wk); kmem_cache_destroy(trans->dev_cmd_pool); } @@ -212,6 +403,8 @@ void iwl_trans_op_mode_leave(struct iwl_trans *trans) iwl_trans_pcie_op_mode_leave(trans); + cancel_work_sync(&trans->restart.wk); + trans->op_mode = NULL; trans->state = IWL_TRANS_NO_FW; @@ -391,6 +584,34 @@ void iwl_trans_stop_device(struct iwl_trans *trans) { might_sleep(); + /* + * See also the comment in iwl_trans_restart_wk(). + * + * When the opmode stops the device while a reset is pending, the + * worker (iwl_trans_restart_wk) might not have run yet or, more + * likely, will be blocked on the opmode lock. Due to the locking, + * we can't just flush the worker. + * + * If this is the case, then the test_and_clear_bit() ensures that + * the worker won't attempt to do anything after the stop. + * + * The trans->restart.mode is a handshake with the opmode, we set + * the context there to ABORT so that when the worker can finally + * acquire the lock in the opmode, the code there won't attempt to + * do any dumps. Since we'd really like to have the dump though, + * also do it inline here (with the opmode locks already held), + * but use a separate mode struct to avoid races. + */ + if (test_and_clear_bit(STATUS_RESET_PENDING, &trans->status)) { + struct iwl_fw_error_dump_mode mode; + + mode = trans->restart.mode; + mode.context = IWL_ERR_CONTEXT_FROM_OPMODE; + trans->restart.mode.context = IWL_ERR_CONTEXT_ABORT; + + iwl_op_mode_dump_error(trans->op_mode, &mode); + } + if (trans->trans_cfg->gen2) iwl_trans_pcie_gen2_stop_device(trans); else diff --git a/iwl-trans.h b/iwl-trans.h index 0ef48effeefb..f6234065dbdd 100644 --- a/iwl-trans.h +++ b/iwl-trans.h @@ -300,6 +300,10 @@ enum iwl_d3_status { * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation * @STATUS_SUPPRESS_CMD_ERROR_ONCE: suppress "FW error in SYNC CMD" once, * e.g. for testing + * @STATUS_IN_SW_RESET: device is undergoing reset, cleared by opmode + * via iwl_trans_finish_sw_reset() + * @STATUS_RESET_PENDING: reset worker was scheduled, but didn't dump + * the firmware state yet */ enum iwl_trans_status { STATUS_SYNC_HCMD_ACTIVE, @@ -311,6 +315,8 @@ enum iwl_trans_status { STATUS_FW_ERROR, STATUS_TRANS_DEAD, STATUS_SUPPRESS_CMD_ERROR_ONCE, + STATUS_IN_SW_RESET, + STATUS_RESET_PENDING, }; static inline int @@ -322,7 +328,6 @@ iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size) case IWL_AMSDU_4K: return get_order(4 * 1024); case IWL_AMSDU_8K: - return get_order(8 * 1024); case IWL_AMSDU_12K: return get_order(16 * 1024); default: @@ -386,7 +391,6 @@ struct iwl_dump_sanitize_ops { * @cmd_queue: the index of the command queue. * Must be set before start_fw. * @cmd_fifo: the fifo for host commands - * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue. * @no_reclaim_cmds: Some devices erroneously don't set the * SEQ_RX_FRAME bit on some notifications, this is the * list of such notifications to filter. Max length is @@ -412,7 +416,6 @@ struct iwl_trans_config { u8 cmd_queue; u8 cmd_fifo; - unsigned int cmd_q_wdg_timeout; const u8 *no_reclaim_cmds; unsigned int n_no_reclaim_cmds; @@ -630,8 +633,6 @@ struct iwl_pc_data { * @n_dest_reg: num of reg_ops in %dbg_dest_tlv * @rec_on: true iff there is a fw debug recording currently active * @dest_tlv: points to the destination TLV for debug - * @conf_tlv: array of pointers to configuration TLVs for debug - * @trigger_tlv: array of pointers to triggers TLVs for debug * @lmac_error_event_table: addrs of lmacs error tables * @umac_error_event_table: addr of umac error table * @tcm_error_event_table: address(es) of TCM error table(s) @@ -666,8 +667,6 @@ struct iwl_trans_debug { bool rec_on; const struct iwl_fw_dbg_dest_tlv_v1 *dest_tlv; - const struct iwl_fw_dbg_conf_tlv *conf_tlv[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv * const *trigger_tlv; u32 lmac_error_event_table[2]; u32 umac_error_event_table; @@ -879,7 +878,16 @@ struct iwl_txq { * @reduced_cap_sku: reduced capability supported SKU * @no_160: device not supporting 160 MHz * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz + * @restart: restart worker data + * @restart.wk: restart worker + * @restart.mode: reset/restart error mode information + * @restart.during_reset: error occurred during previous software reset + * @me_recheck_wk: worker to recheck WiAMT/CSME presence + * @me_present: WiAMT/CSME is detected as present (1), not present (0) + * or unknown (-1, so can still use it as a boolean safely) * @trans_specific: data for the specific transport this is allocated for/with + * @dsbr_urm_fw_dependent: switch to URM based on fw settings + * @dsbr_urm_permanent: switch to URM permanently */ struct iwl_trans { bool csme_own; @@ -904,6 +912,9 @@ struct iwl_trans { bool reduced_cap_sku; u8 no_160:1, step_urm:1; + u8 dsbr_urm_fw_dependent:1, + dsbr_urm_permanent:1; + u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; bool pm_support; @@ -946,6 +957,15 @@ struct iwl_trans { struct iwl_dma_ptr invalid_tx_cmd; + struct { + struct work_struct wk; + struct iwl_fw_error_dump_mode mode; + bool during_reset; + } restart; + + struct delayed_work me_recheck_wk; + s8 me_present; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[] __aligned(sizeof(void *)); @@ -1076,12 +1096,13 @@ int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs, void iwl_trans_debugfs_cleanup(struct iwl_trans *trans); #endif -#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \ - do { \ - if (__builtin_constant_p(bufsize)) \ - BUILD_BUG_ON((bufsize) % sizeof(u32)); \ - iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\ - } while (0) +#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \ + ({ \ + if (__builtin_constant_p(bufsize)) \ + BUILD_BUG_ON((bufsize) % sizeof(u32)); \ + iwl_trans_read_mem(trans, addr, buf, \ + (bufsize) / sizeof(u32)); \ + }) int iwl_trans_write_imr_mem(struct iwl_trans *trans, u32 dst_addr, u64 src_addr, u32 byte_cnt); @@ -1121,7 +1142,28 @@ bool _iwl_trans_grab_nic_access(struct iwl_trans *trans); void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans); -static inline void iwl_trans_fw_error(struct iwl_trans *trans, bool sync) +static inline void iwl_trans_schedule_reset(struct iwl_trans *trans, + enum iwl_fw_error_type type) +{ + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return; + + trans->restart.mode.type = type; + trans->restart.mode.context = IWL_ERR_CONTEXT_WORKER; + + set_bit(STATUS_RESET_PENDING, &trans->status); + + /* + * keep track of whether or not this happened while resetting, + * by the timer the worker runs it might have finished + */ + trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET, + &trans->status); + queue_work(system_unbound_wq, &trans->restart.wk); +} + +static inline void iwl_trans_fw_error(struct iwl_trans *trans, + enum iwl_fw_error_type type) { if (WARN_ON_ONCE(!trans->op_mode)) return; @@ -1129,10 +1171,24 @@ static inline void iwl_trans_fw_error(struct iwl_trans *trans, bool sync) /* prevent double restarts due to the same erroneous FW */ if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) { trans->state = IWL_TRANS_NO_FW; - iwl_op_mode_nic_error(trans->op_mode, sync); + iwl_op_mode_nic_error(trans->op_mode, type); + iwl_trans_schedule_reset(trans, type); } } +static inline void iwl_trans_opmode_sw_reset(struct iwl_trans *trans, + enum iwl_fw_error_type type) +{ + if (WARN_ON_ONCE(!trans->op_mode)) + return; + + set_bit(STATUS_IN_SW_RESET, &trans->status); + + if (!trans->op_mode->ops->sw_reset || + !trans->op_mode->ops->sw_reset(trans->op_mode, type)) + clear_bit(STATUS_IN_SW_RESET, &trans->status); +} + static inline bool iwl_trans_fw_running(struct iwl_trans *trans) { return trans->state == IWL_TRANS_FW_ALIVE; @@ -1165,6 +1221,11 @@ static inline bool iwl_trans_dbg_ini_valid(struct iwl_trans *trans) void iwl_trans_interrupts(struct iwl_trans *trans, bool enable); +static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans) +{ + clear_bit(STATUS_IN_SW_RESET, &trans->status); +} + /***************************************************** * transport helper functions *****************************************************/ @@ -1179,12 +1240,27 @@ static inline bool iwl_trans_is_hw_error_value(u32 val) return ((val & ~0xf) == 0xa5a5a5a0) || ((val & ~0xf) == 0x5a5a5a50); } +void iwl_trans_free_restart_list(void); + /***************************************************** * PCIe handling *****************************************************/ int __must_check iwl_pci_register_driver(void); void iwl_pci_unregister_driver(void); -void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan); + +/* Note: order matters */ +enum iwl_reset_mode { + /* upper level modes: */ + IWL_RESET_MODE_SW_RESET, + IWL_RESET_MODE_REPROBE, + /* PCIE level modes: */ + IWL_RESET_MODE_REMOVE_ONLY, + IWL_RESET_MODE_RESCAN, + IWL_RESET_MODE_FUNC_RESET, + IWL_RESET_MODE_PROD_RESET, +}; + +void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); diff --git a/iwl-utils.c b/iwl-utils.c new file mode 100644 index 000000000000..b14ec98e28b6 --- /dev/null +++ b/iwl-utils.c @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#include <net/gso.h> +#include <linux/ieee80211.h> +#include <net/gso.h> +#include <net/ip.h> + +#include "iwl-drv.h" +#include "iwl-utils.h" + +#ifdef CONFIG_INET +int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, + netdev_features_t netdev_flags, + struct sk_buff_head *mpdus_skbs) +{ + struct sk_buff *tmp, *next; + struct ieee80211_hdr *hdr = (void *)skb->data; + char cb[sizeof(skb->cb)]; + u16 i = 0; + unsigned int tcp_payload_len; + unsigned int mss = skb_shinfo(skb)->gso_size; + bool ipv4 = (skb->protocol == htons(ETH_P_IP)); + bool qos = ieee80211_is_data_qos(hdr->frame_control); + u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; + + skb_shinfo(skb)->gso_size = num_subframes * mss; + memcpy(cb, skb->cb, sizeof(cb)); + + next = skb_gso_segment(skb, netdev_flags); + skb_shinfo(skb)->gso_size = mss; + skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; + + if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM) + return -ENOMEM; + + if (WARN_ONCE(IS_ERR(next), + "skb_gso_segment error: %d\n", (int)PTR_ERR(next))) + return PTR_ERR(next); + + if (next) + consume_skb(skb); + + skb_list_walk_safe(next, tmp, next) { + memcpy(tmp->cb, cb, sizeof(tmp->cb)); + /* + * Compute the length of all the data added for the A-MSDU. + * This will be used to compute the length to write in the TX + * command. We have: SNAP + IP + TCP for n -1 subframes and + * ETH header for n subframes. + */ + tcp_payload_len = skb_tail_pointer(tmp) - + skb_transport_header(tmp) - + tcp_hdrlen(tmp) + tmp->data_len; + + if (ipv4) + ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); + + if (tcp_payload_len > mss) { + skb_shinfo(tmp)->gso_size = mss; + skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 : + SKB_GSO_TCPV6; + } else { + if (qos) { + u8 *qc; + + if (ipv4) + ip_send_check(ip_hdr(tmp)); + + qc = ieee80211_get_qos_ctl((void *)tmp->data); + *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + skb_shinfo(tmp)->gso_size = 0; + } + + skb_mark_not_on_list(tmp); + __skb_queue_tail(mpdus_skbs, tmp); + i++; + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_tx_tso_segment); +#endif /* CONFIG_INET */ diff --git a/iwl-utils.h b/iwl-utils.h new file mode 100644 index 000000000000..8f1f11d06fbe --- /dev/null +++ b/iwl-utils.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_utils_h__ +#define __iwl_utils_h__ + +#include <net/cfg80211.h> + +#ifdef CONFIG_INET +/** + * iwl_tx_tso_segment - Segments a TSO packet into subframes for A-MSDU. + * @skb: buffer to segment. + * @num_subframes: number of subframes to create. + * @netdev_flags: netdev feature flags. + * @mpdus_skbs: list to hold the segmented subframes. + * + * This function segments a large TCP packet into subframes. + * subframes are added to the mpdus_skbs list + * + * Returns: 0 on success and negative value on failure. + */ +int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, + netdev_features_t netdev_flags, + struct sk_buff_head *mpdus_skbs); +#else +static inline +int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, + netdev_features_t netdev_flags, + struct sk_buff_head *mpdus_skbs) +{ + WARN_ON(1); + + return -1; +} +#endif /* CONFIG_INET */ + +static inline +u32 iwl_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) +{ + struct ieee80211_mgmt *mgmt = (void *)beacon; + const u8 *ie; + + if (WARN_ON_ONCE(frame_size <= (mgmt->u.beacon.variable - beacon))) + return 0; + + frame_size -= mgmt->u.beacon.variable - beacon; + + ie = cfg80211_find_ie(eid, mgmt->u.beacon.variable, frame_size); + if (!ie) + return 0; + + return ie - beacon; +} + +#endif /* __iwl_utils_h__ */ diff --git a/mvm/binding.c b/mvm/binding.c index 458b97930059..58e9a940024d 100644 --- a/mvm/binding.c +++ b/mvm/binding.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2012-2014, 2020 Intel Corporation * Copyright (C) 2016 Intel Deutschland GmbH - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022, 2024 Intel Corporation */ #include <net/mac80211.h> #include "fw-api.h" @@ -158,9 +158,8 @@ int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) ret = iwl_mvm_binding_update(mvm, vif, mvmvif->deflink.phy_ctxt, false); - if (!ret) - if (iwl_mvm_sf_update(mvm, vif, true)) - IWL_ERR(mvm, "Failed to update SF state\n"); + if (!ret && iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); return ret; } diff --git a/mvm/coex.c b/mvm/coex.c index ad3e14a0d043..21641d41a958 100644 --- a/mvm/coex.c +++ b/mvm/coex.c @@ -208,7 +208,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, } struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif *notif; + struct iwl_bt_coex_prof_old_notif *notif; struct iwl_mvm *mvm; struct ieee80211_chanctx_conf *primary; struct ieee80211_chanctx_conf *secondary; @@ -266,10 +266,26 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); bool have_wifi_loss_rate = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, - BT_PROFILE_NOTIFICATION, 0) > 4; + BT_PROFILE_NOTIFICATION, 0) > 4 || + iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP, + PROFILE_NOTIF, 0) >= 1; + u8 wifi_loss_mid_high_rssi; + u8 wifi_loss_low_rssi; u8 wifi_loss_rate; - if (mvm->last_bt_notif.wifi_loss_low_rssi == BT_OFF) + if (iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP, + PROFILE_NOTIF, 0) >= 1) { + /* For now, we consider 2.4 GHz band / ANT_A only */ + wifi_loss_mid_high_rssi = + mvm->last_bt_wifi_loss.wifi_loss_mid_high_rssi[PHY_BAND_24][0]; + wifi_loss_low_rssi = + mvm->last_bt_wifi_loss.wifi_loss_low_rssi[PHY_BAND_24][0]; + } else { + wifi_loss_mid_high_rssi = mvm->last_bt_notif.wifi_loss_mid_high_rssi; + wifi_loss_low_rssi = mvm->last_bt_notif.wifi_loss_low_rssi; + } + + if (wifi_loss_low_rssi == BT_OFF) return true; if (primary) @@ -286,20 +302,20 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, * we will get an update on this and exit eSR. */ if (!link_rssi) - wifi_loss_rate = mvm->last_bt_notif.wifi_loss_mid_high_rssi; + wifi_loss_rate = wifi_loss_mid_high_rssi; else if (mvmvif->esr_active) /* RSSI needs to get really low to disable eSR... */ wifi_loss_rate = link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ? - mvm->last_bt_notif.wifi_loss_low_rssi : - mvm->last_bt_notif.wifi_loss_mid_high_rssi; + wifi_loss_low_rssi : + wifi_loss_mid_high_rssi; else /* ...And really high before we enable it back */ wifi_loss_rate = link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ? - mvm->last_bt_notif.wifi_loss_low_rssi : - mvm->last_bt_notif.wifi_loss_mid_high_rssi; + wifi_loss_low_rssi : + wifi_loss_mid_high_rssi; return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH; } @@ -509,6 +525,32 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id); } +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_coex_notif_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + + lockdep_assert_held(&mvm->mutex); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + for_each_vif_active_link(vif, link_conf, link_id) { + struct ieee80211_chanctx_conf *chanctx_conf = + rcu_dereference_check(link_conf->chanctx_conf, + lockdep_is_held(&mvm->mutex)); + + if ((!chanctx_conf || + chanctx_conf->def.chan->band != NL80211_BAND_2GHZ)) + continue; + + iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id); + } +} + static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) { struct iwl_bt_iterator_data data = { @@ -591,11 +633,11 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) } } -void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) +void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + struct iwl_bt_coex_prof_old_notif *notif = (void *)pkt->data; IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); @@ -612,6 +654,22 @@ void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, iwl_mvm_bt_coex_notif_handle(mvm); } +void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + const struct iwl_rx_packet *pkt = rxb_addr(rxb); + const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data; + + lockdep_assert_held(&mvm->mutex); + + mvm->last_bt_wifi_loss = *notif; + + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_coex_notif_iterator, + mvm); +} + void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event_data rssi_event) { @@ -628,7 +686,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * Rssi update while not associated - can happen since the statistics * are handled asynchronously */ - if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA) + if (mvmvif->deflink.ap_sta_id == IWL_INVALID_STA) return; /* No BT - reports should be disabled */ diff --git a/mvm/constants.h b/mvm/constants.h index c4c1e67b9ac7..776600ddaea6 100644 --- a/mvm/constants.h +++ b/mvm/constants.h @@ -16,6 +16,10 @@ #define IWL_MVM_BT_COEX_WIFI_LOSS_THRESH 0 #define IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC 30 #define IWL_MVM_TPT_COUNT_WINDOW_SEC 5 +#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5 +#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH 15 +#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 11 +#define IWL_MVM_LOW_RSSI_MLO_SCAN_THRESH -72 #define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) #define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) @@ -55,7 +59,6 @@ #define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 #define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 #define IWL_MVM_TOF_IS_RESPONDER 0 -#define IWL_MVM_HW_CSUM_DISABLE 0 #define IWL_MVM_ADWELL_ENABLE 1 #define IWL_MVM_ADWELL_MAX_BUDGET 0 #define IWL_MVM_TCM_LOAD_MEDIUM_THRESH 10 /* percentage */ @@ -109,7 +112,7 @@ #define IWL_MVM_FTM_INITIATOR_SECURE_LTF false #define IWL_MVM_FTM_RESP_NDP_SUPPORT true #define IWL_MVM_FTM_RESP_LMR_FEEDBACK_SUPPORT true -#define IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 5 +#define IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 7 #define IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR 1000 #define IWL_MVM_D3_DEBUG false #define IWL_MVM_USE_TWT true @@ -125,7 +128,6 @@ #define IWL_MVM_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT 60 /* in seconds */ #define IWL_MVM_MIN_BEACON_INTERVAL_TU 16 #define IWL_MVM_AUTO_EML_ENABLE true -#define IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH 7 #define IWL_MVM_HIGH_RSSI_THRESH_20MHZ -67 #define IWL_MVM_LOW_RSSI_THRESH_20MHZ -71 @@ -922,7 +922,7 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) static int iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct iwl_wowlan_config_cmd_v6 *wowlan_config_cmd, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, struct ieee80211_sta *ap_sta) { @@ -948,7 +948,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret); } - iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd); + if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_CONFIGURATION, 0) < 7) + iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd); if (wowlan->disconnect) wowlan_config_cmd->wakeup_filter |= @@ -1122,7 +1123,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, static int iwl_mvm_wowlan_config(struct iwl_mvm *mvm, struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct iwl_wowlan_config_cmd_v6 *wowlan_config_cmd_v6, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, struct iwl_mvm_vif_link_info *mvm_link, struct ieee80211_sta *ap_sta) @@ -1131,7 +1132,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); - mvm->offload_tid = wowlan_config_cmd->offloading_tid; + mvm->offload_tid = wowlan_config_cmd_v6->offloading_tid; if (!unified_image) { ret = iwl_mvm_switch_to_d3(mvm); @@ -1147,9 +1148,26 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, if (ret) return ret; - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, - sizeof(*wowlan_config_cmd), - wowlan_config_cmd); + if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_CONFIGURATION, 0) > 6) { + struct iwl_wowlan_config_cmd wowlan_config_cmd = { + .wakeup_filter = wowlan_config_cmd_v6->wakeup_filter, + .wowlan_ba_teardown_tids = + wowlan_config_cmd_v6->wowlan_ba_teardown_tids, + .is_11n_connection = + wowlan_config_cmd_v6->is_11n_connection, + .offloading_tid = wowlan_config_cmd_v6->offloading_tid, + .flags = wowlan_config_cmd_v6->flags, + .sta_id = wowlan_config_cmd_v6->sta_id, + }; + + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, + sizeof(wowlan_config_cmd), + &wowlan_config_cmd); + } else { + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, + sizeof(*wowlan_config_cmd_v6), + wowlan_config_cmd_v6); + } if (ret) return ret; @@ -1288,7 +1306,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, goto out_noreset; } - if (mvm_link->ap_sta_id == IWL_MVM_INVALID_STA) { + if (mvm_link->ap_sta_id == IWL_INVALID_STA) { /* if we're not associated, this must be netdetect */ if (!wowlan->nd_config) { ret = 1; @@ -1302,7 +1320,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm->net_detect = true; } else { - struct iwl_wowlan_config_cmd wowlan_config_cmd = { + struct iwl_wowlan_config_cmd_v6 wowlan_config_cmd = { .offloading_tid = 0, }; @@ -1377,13 +1395,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret < 0) { iwl_mvm_free_nd(mvm); - if (!unified_image) { - if (mvm->fw_restart > 0) { - mvm->fw_restart--; - ieee80211_restart_hw(mvm->hw); - } - } - clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); } out_noreset: @@ -1398,7 +1409,9 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) iwl_mvm_pause_tcm(mvm, true); + mutex_lock(&mvm->mutex); iwl_fw_runtime_suspend(&mvm->fwrt); + mutex_unlock(&mvm->mutex); return __iwl_mvm_suspend(hw, wowlan, false); } @@ -1423,6 +1436,7 @@ struct iwl_wowlan_status_data { u16 non_qos_seq_ctr; u16 qos_seq_ctr[8]; u8 tid_tear_down; + u8 tid_offloaded_tx; struct { /* including RX MIC key for TKIP */ @@ -2472,17 +2486,62 @@ static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status, static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, struct iwl_wowlan_info_notif *data, struct iwl_wowlan_status_data *status, - u32 len, bool has_mlo_keys) + u32 len) { - u32 i; - u32 expected_len = sizeof(*data); + u32 expected_len = sizeof(*data) + + data->num_mlo_link_keys * sizeof(status->mlo_keys[0]); - if (!data) { - IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n"); + if (len < expected_len) { + IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); status = NULL; return; } + if (mvm->fast_resume) + return; + + iwl_mvm_convert_key_counters_v5(status, &data->gtk[0].sc); + iwl_mvm_convert_gtk_v3(status, data->gtk); + iwl_mvm_convert_igtk(status, &data->igtk[0]); + iwl_mvm_convert_bigtk(status, data->bigtk); + status->replay_ctr = le64_to_cpu(data->replay_ctr); + status->pattern_number = le16_to_cpu(data->pattern_number); + status->tid_offloaded_tx = data->tid_offloaded_tx; + if (IWL_FW_CHECK(mvm, + data->tid_offloaded_tx >= + ARRAY_SIZE(status->qos_seq_ctr), + "tid_offloaded_tx is out of bound %d\n", + data->tid_offloaded_tx)) + data->tid_offloaded_tx = 0; + status->qos_seq_ctr[data->tid_offloaded_tx] = + le16_to_cpu(data->qos_seq_ctr); + status->wakeup_reasons = le32_to_cpu(data->wakeup_reasons); + status->num_of_gtk_rekeys = + le32_to_cpu(data->num_of_gtk_rekeys); + status->received_beacons = le32_to_cpu(data->received_beacons); + status->tid_tear_down = data->tid_tear_down; + + if (data->num_mlo_link_keys) { + status->num_mlo_keys = data->num_mlo_link_keys; + if (IWL_FW_CHECK(mvm, + status->num_mlo_keys > WOWLAN_MAX_MLO_KEYS, + "Too many mlo keys: %d, max %d\n", + status->num_mlo_keys, WOWLAN_MAX_MLO_KEYS)) + status->num_mlo_keys = WOWLAN_MAX_MLO_KEYS; + memcpy(status->mlo_keys, data->mlo_gtks, + status->num_mlo_keys * sizeof(status->mlo_keys[0])); + } +} + +static void +iwl_mvm_parse_wowlan_info_notif_v4(struct iwl_mvm *mvm, + struct iwl_wowlan_info_notif_v4 *data, + struct iwl_wowlan_status_data *status, + u32 len, bool has_mlo_keys) +{ + u32 i; + u32 expected_len = sizeof(*data); + if (has_mlo_keys) expected_len += (data->num_mlo_link_keys * sizeof(status->mlo_keys[0])); @@ -2531,12 +2590,6 @@ iwl_mvm_parse_wowlan_info_notif_v2(struct iwl_mvm *mvm, { u32 i; - if (!data) { - IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n"); - status = NULL; - return; - } - if (len < sizeof(*data)) { IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); status = NULL; @@ -2744,6 +2797,10 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int link_id = vif->active_links ? __ffs(vif->active_links) : 0; struct iwl_mvm_vif_link_info *mvm_link = mvmvif->link[link_id]; + int wowlan_info_ver = iwl_fw_lookup_notif_ver(mvm->fw, + PROT_OFFLOAD_GROUP, + WOWLAN_INFO_NOTIFICATION, + IWL_FW_CMD_VER_UNKNOWN); if (WARN_ON(!mvm_link)) goto out_unlock; @@ -2758,11 +2815,14 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, if (!mvm_ap_sta) goto out_unlock; - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = status->qos_seq_ctr[i]; - /* firmware stores last-used value, we store next value */ - seq += 0x10; - mvm_ap_sta->tid_data[i].seq_number = seq; + /* firmware stores last-used value, we store next value */ + if (wowlan_info_ver >= 5) { + mvm_ap_sta->tid_data[status->tid_offloaded_tx].seq_number = + status->qos_seq_ctr[status->tid_offloaded_tx] + 0x10; + } else { + for (i = 0; i < IWL_MAX_TID_COUNT; i++) + mvm_ap_sta->tid_data[i].seq_number = + status->qos_seq_ctr[i] + 0x10; } if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { @@ -2869,6 +2929,7 @@ static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm, int idx) { int i; + int n_channels = 0; if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { @@ -2877,7 +2938,7 @@ static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm, for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; i++) if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[match->n_channels++] = + match->channels[n_channels++] = mvm->nd_channels[i]->center_freq; } else { struct iwl_scan_offload_profile_match_v1 *matches = @@ -2885,9 +2946,11 @@ static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm, for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 * 8; i++) if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[match->n_channels++] = + match->channels[n_channels++] = mvm->nd_channels[i]->center_freq; } + /* We may have ended up with fewer channels than we allocated. */ + match->n_channels = n_channels; } /** @@ -2968,6 +3031,8 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, GFP_KERNEL); if (!net_detect || !n_matches) goto out_report_nd; + net_detect->n_matches = n_matches; + n_matches = 0; for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) { struct cfg80211_wowlan_nd_match *match; @@ -2981,8 +3046,9 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, GFP_KERNEL); if (!match) goto out_report_nd; + match->n_channels = n_channels; - net_detect->matches[net_detect->n_matches++] = match; + net_detect->matches[n_matches++] = match; /* We inverted the order of the SSIDs in the scan * request, so invert the index here. @@ -2997,6 +3063,8 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, iwl_mvm_query_set_freqs(mvm, d3_data->nd_results, match, i); } + /* We may have fewer matches than we allocated. */ + net_detect->n_matches = n_matches; out_report_nd: wakeup.net_detect = net_detect; @@ -3024,55 +3092,50 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, ieee80211_resume_disconnect(vif); } -static bool iwl_mvm_rt_status(struct iwl_trans *trans, u32 base, u32 *err_id) -{ - struct error_table_start { - /* cf. struct iwl_error_event_table */ - u32 valid; - __le32 err_id; - } err_info; - - if (!base) - return false; - - iwl_trans_read_mem_bytes(trans, base, - &err_info, sizeof(err_info)); - if (err_info.valid && err_id) - *err_id = le32_to_cpu(err_info.err_id); - - return !!err_info.valid; -} +enum rt_status { + FW_ALIVE, + FW_NEEDS_RESET, + FW_ERROR, +}; -static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static enum rt_status iwl_mvm_check_rt_status(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { u32 err_id; /* check for lmac1 error */ - if (iwl_mvm_rt_status(mvm->trans, - mvm->trans->dbg.lmac_error_event_table[0], - &err_id)) { - if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { - struct cfg80211_wowlan_wakeup wakeup = { - .rfkill_release = true, - }; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.lmac_error_event_table[0], + &err_id)) { + if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) { + IWL_WARN(mvm, "Rfkill was toggled during suspend\n"); + if (vif) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + } + + return FW_NEEDS_RESET; } - return true; + return FW_ERROR; } /* check if we have lmac2 set and check for error */ - if (iwl_mvm_rt_status(mvm->trans, - mvm->trans->dbg.lmac_error_event_table[1], NULL)) - return true; + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.lmac_error_event_table[1], + NULL)) + return FW_ERROR; /* check for umac error */ - if (iwl_mvm_rt_status(mvm->trans, - mvm->trans->dbg.umac_error_event_table, NULL)) - return true; + if (iwl_fwrt_read_err_table(mvm->trans, + mvm->trans->dbg.umac_error_event_table, + NULL)) + return FW_ERROR; - return false; + return FW_ALIVE; } /* @@ -3090,7 +3153,7 @@ iwl_mvm_choose_query_wakeup_reasons(struct iwl_mvm *mvm, /* if FW uses status notification, status shouldn't be NULL here */ if (!d3_data->status) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA : + u8 sta_id = mvm->net_detect ? IWL_INVALID_STA : mvmvif->deflink.ap_sta_id; /* bug - FW with MLO has status notification */ @@ -3262,13 +3325,19 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, iwl_mvm_parse_wowlan_info_notif_v2(mvm, notif_v2, d3_data->status, len); + } else if (wowlan_info_ver < 5) { + struct iwl_wowlan_info_notif_v4 *notif = + (void *)pkt->data; + + iwl_mvm_parse_wowlan_info_notif_v4(mvm, notif, + d3_data->status, len, + wowlan_info_ver > 3); } else { struct iwl_wowlan_info_notif *notif = (void *)pkt->data; iwl_mvm_parse_wowlan_info_notif(mvm, notif, - d3_data->status, len, - wowlan_info_ver > 3); + d3_data->status, len); } d3_data->notif_received |= IWL_D3_NOTIF_WOWLAN_INFO; @@ -3435,6 +3504,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D0I3_END_FIRST); bool resume_notif_based = iwl_mvm_d3_resume_notif_based(mvm); + enum rt_status rt_status; bool keep = false; mutex_lock(&mvm->mutex); @@ -3458,13 +3528,19 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); - if (iwl_mvm_check_rt_status(mvm, vif)) { + rt_status = iwl_mvm_check_rt_status(mvm, vif); + if (rt_status != FW_ALIVE) { set_bit(STATUS_FW_ERROR, &mvm->trans->status); - iwl_mvm_dump_nic_error_log(mvm); - iwl_dbg_tlv_time_point(&mvm->fwrt, - IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); - iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, - false, 0); + if (rt_status == FW_ERROR) { + IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, + NULL); + iwl_fw_dbg_collect_desc(&mvm->fwrt, + &iwl_dump_desc_assert, + false, 0); + } ret = 1; goto err; } @@ -3610,8 +3686,6 @@ void iwl_mvm_fast_suspend(struct iwl_mvm *mvm) IWL_ERR(mvm, "fast suspend: couldn't send D3_CONFIG_CMD %d\n", ret); - WARN_ON(iwl_mvm_power_update_mac(mvm)); - ret = iwl_trans_d3_suspend(mvm->trans, false, false); if (ret) IWL_ERR(mvm, "fast suspend: trans_d3_suspend failed %d\n", ret); @@ -3623,6 +3697,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) .notif_expected = IWL_D3_NOTIF_D3_END_NOTIF, }; + enum rt_status rt_status; int ret; lockdep_assert_held(&mvm->mutex); @@ -3632,23 +3707,37 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) mvm->last_reset_or_resume_time_jiffies = jiffies; iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); - if (iwl_mvm_check_rt_status(mvm, NULL)) { + rt_status = iwl_mvm_check_rt_status(mvm, NULL); + if (rt_status != FW_ALIVE) { set_bit(STATUS_FW_ERROR, &mvm->trans->status); - iwl_mvm_dump_nic_error_log(mvm); - iwl_dbg_tlv_time_point(&mvm->fwrt, - IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); - iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, - false, 0); - return -ENODEV; + if (rt_status == FW_ERROR) { + IWL_ERR(mvm, + "iwl_mvm_check_rt_status failed, device is gone during suspend\n"); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, + NULL); + iwl_fw_dbg_collect_desc(&mvm->fwrt, + &iwl_dump_desc_assert, + false, 0); + } + mvm->trans->state = IWL_TRANS_NO_FW; + ret = -ENODEV; + + goto out; } ret = iwl_mvm_d3_notif_wait(mvm, &d3_data); + + if (ret) { + IWL_ERR(mvm, "Couldn't get the d3 notif %d\n", ret); + mvm->trans->state = IWL_TRANS_NO_FW; + } + +out: clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; mvm->fast_resume = false; - if (ret) - IWL_ERR(mvm, "Couldn't get the d3 notif %d\n", ret); - return ret; } @@ -3768,7 +3857,6 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) } const struct file_operations iwl_dbgfs_d3_test_ops = { - .llseek = no_llseek, .open = iwl_mvm_d3_test_open, .read = iwl_mvm_d3_test_read, .release = iwl_mvm_d3_test_release, diff --git a/mvm/debugfs-vif.c b/mvm/debugfs-vif.c index 25f07e00db42..fbe4e4a50852 100644 --- a/mvm/debugfs-vif.c +++ b/mvm/debugfs-vif.c @@ -221,7 +221,7 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, mvmvif->deflink.queue_params[i].uapsd); if (vif->type == NL80211_IFTYPE_STATION && - ap_sta_id != IWL_MVM_INVALID_STA) { + ap_sta_id != IWL_INVALID_STA) { struct iwl_mvm_sta *mvm_sta; mvm_sta = iwl_mvm_sta_from_staid_protected(mvm, ap_sta_id); @@ -463,11 +463,13 @@ static ssize_t iwl_dbgfs_os_device_timediff_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) +static ssize_t +iwl_dbgfs_low_latency_write_handle(struct wiphy *wiphy, struct file *file, + char *buf, size_t count, void *data) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *vif = data; u8 value; int ret; @@ -484,12 +486,28 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, return count; } -static ssize_t -iwl_dbgfs_low_latency_force_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_low_latency_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; + char buf[10] = {}; + + return wiphy_locked_debugfs_write(mvm->hw->wiphy, file, + buf, sizeof(buf), user_buf, count, + iwl_dbgfs_low_latency_write_handle, + vif); +} + +static ssize_t +iwl_dbgfs_low_latency_force_write_handle(struct wiphy *wiphy, struct file *file, + char *buf, size_t count, void *data) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *vif = data; u8 value; int ret; @@ -517,6 +535,22 @@ iwl_dbgfs_low_latency_force_write(struct ieee80211_vif *vif, char *buf, return count; } +static ssize_t +iwl_dbgfs_low_latency_force_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[10] = {}; + + return wiphy_locked_debugfs_write(mvm->hw->wiphy, file, + buf, sizeof(buf), user_buf, count, + iwl_dbgfs_low_latency_force_write_handle, + vif); +} + static ssize_t iwl_dbgfs_low_latency_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -831,8 +865,20 @@ MVM_DEBUGFS_READ_FILE_OPS(mac_params); MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt); 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(low_latency_force, 10); + +static const struct file_operations iwl_dbgfs_low_latency_ops = { + .write = iwl_dbgfs_low_latency_write, + .read = iwl_dbgfs_low_latency_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static const struct file_operations iwl_dbgfs_low_latency_force_ops = { + .write = iwl_dbgfs_low_latency_force_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32); diff --git a/mvm/debugfs.c b/mvm/debugfs.c index 91ca830a7b60..55d035b896e9 100644 --- a/mvm/debugfs.c +++ b/mvm/debugfs.c @@ -16,6 +16,7 @@ #include "debugfs.h" #include "iwl-modparams.h" #include "iwl-drv.h" +#include "iwl-utils.h" #include "fw/error-dump.h" #include "fw/api/phy-ctxt.h" @@ -462,7 +463,6 @@ static ssize_t iwl_dbgfs_amsdu_len_write(struct ieee80211_link_sta *link_sta, if (amsdu_len) { mvm_link_sta->orig_amsdu_len = link_sta->agg.max_amsdu_len; link_sta->agg.max_amsdu_len = amsdu_len; - link_sta->agg.max_amsdu_len = amsdu_len; for (i = 0; i < ARRAY_SIZE(link_sta->agg.max_tid_amsdu_len); i++) link_sta->agg.max_tid_amsdu_len[i] = amsdu_len; } else { @@ -537,43 +537,12 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, return ret ?: count; } -static ssize_t iwl_dbgfs_fw_ver_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char *buff, *pos, *endpos; - static const size_t bufsz = 1024; - int ret; - - buff = kmalloc(bufsz, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - pos = buff; - endpos = pos + bufsz; - - pos += scnprintf(pos, endpos - pos, "FW id: %s\n", - mvm->fwrt.fw->fw_version); - pos += scnprintf(pos, endpos - pos, "FW: %s\n", - mvm->fwrt.fw->human_readable); - pos += scnprintf(pos, endpos - pos, "Device: %s\n", - mvm->fwrt.trans->name); - pos += scnprintf(pos, endpos - pos, "Bus: %s\n", - mvm->fwrt.dev->bus->name); - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); - kfree(buff); - - return ret; -} - static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_mvm_tas_status_resp tas_rsp; - struct iwl_mvm_tas_status_resp *rsp = &tas_rsp; + struct iwl_mvm_tas_status_resp *rsp = NULL; static const size_t bufsz = 1024; char *buff, *pos, *endpos; const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { @@ -609,6 +578,10 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, if (!iwl_mvm_firmware_running(mvm)) return -ENODEV; + if (iwl_fw_lookup_notif_ver(mvm->fw, DEBUG_GROUP, GET_TAS_STATUS, + 0) != 3) + return -EOPNOTSUPP; + mutex_lock(&mvm->mutex); ret = iwl_mvm_send_cmd(mvm, &hcmd); mutex_unlock(&mvm->mutex); @@ -659,6 +632,14 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, rsp->tas_fw_version); pos += scnprintf(pos, endpos - pos, "Is UHB enabled for USA?: %s\n", rsp->is_uhb_for_usa_enable ? "True" : "False"); + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT)) + pos += scnprintf(pos, endpos - pos, + "Is UHB enabled for CANADA?: %s\n", + rsp->uhb_allowed_flags & + TAS_UHB_ALLOWED_CANADA ? "True" : "False"); + pos += scnprintf(pos, endpos - pos, "Current MCC: 0x%x\n", le16_to_cpu(rsp->curr_mcc)); @@ -1159,10 +1140,6 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, mutex_lock(&mvm->mutex); - /* allow one more restart that we're provoking here */ - if (mvm->fw_restart >= 0) - mvm->fw_restart++; - if (count == 6 && !strcmp(buf, "nolog\n")) { set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status); set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mvm->trans->status); @@ -1409,9 +1386,9 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len) if (iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) >= 14) { - u32 offset = iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_S1G_TWT, - beacon->len); + u32 offset = iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len); beacon_cmd.btwt_offset = cpu_to_le32(offset); } @@ -1495,22 +1472,6 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, return ret ?: count; } -static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - if (count == 0) - return 0; - - iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER, - NULL); - - iwl_fw_dbg_collect(&mvm->fwrt, FW_DBG_TRIGGER_USER, buf, - (count - 1), NULL); - - return count; -} - static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) @@ -1518,6 +1479,13 @@ static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm, if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) return -EOPNOTSUPP; + /* + * If the firmware is not running, silently succeed since there is + * no data to clear. + */ + if (!iwl_mvm_firmware_running(mvm)) + return count; + mutex_lock(&mvm->mutex); iwl_fw_dbg_clear_monitor_buf(&mvm->fwrt); mutex_unlock(&mvm->mutex); @@ -1964,14 +1932,12 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); MVM_DEBUGFS_READ_FILE_OPS(fw_system_stats); -MVM_DEBUGFS_READ_FILE_OPS(fw_ver); MVM_DEBUGFS_READ_FILE_OPS(phy_integration_ver); MVM_DEBUGFS_READ_FILE_OPS(tas_get_status); 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(fw_dbg_conf, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64); MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_clear, 64); MVM_DEBUGFS_WRITE_FILE_OPS(dbg_time_point, 64); MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl, @@ -2164,7 +2130,6 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm) MVM_DEBUGFS_ADD_FILE(force_ctkill, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(stations, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, 0600); - MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(fw_system_stats, mvm->debugfs_dir, 0400); @@ -2173,7 +2138,6 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm) MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, 0600); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, 0600); MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, 0600); - MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(fw_dbg_clear, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(dbg_time_point, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200); diff --git a/mvm/ftm-initiator.c b/mvm/ftm-initiator.c index afd90a52d4ec..b26141c30c61 100644 --- a/mvm/ftm-initiator.c +++ b/mvm/ftm-initiator.c @@ -559,12 +559,12 @@ static int iwl_mvm_ftm_set_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, #ifdef CONFIG_IWLWIFI_DEBUGFS if (mvmvif->ftm_unprotected) { - *sta_id = IWL_MVM_INVALID_STA; + *sta_id = IWL_INVALID_STA; *flags &= ~cpu_to_le32(IWL_INITIATOR_AP_FLAGS_PMF); } #endif } else { - *sta_id = IWL_MVM_INVALID_STA; + *sta_id = IWL_INVALID_STA; } return 0; @@ -772,6 +772,7 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_ftm_iter_data target; target.bssid = bssid; + target.cipher = cipher; ieee80211_iter_keys(mvm->hw, vif, iter, &target); } else { memcpy(tk, entry->tk, sizeof(entry->tk)); @@ -1062,6 +1063,8 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, IWL_FW_CMD_VER_UNKNOWN); switch (cmd_ver) { + case 15: + /* Version 15 has the same struct as 14 */ case 14: err = iwl_mvm_ftm_start_v14(mvm, vif, req); break; diff --git a/mvm/ftm-responder.c b/mvm/ftm-responder.c index e4caa362f597..e6e468e81ab3 100644 --- a/mvm/ftm-responder.c +++ b/mvm/ftm-responder.c @@ -131,7 +131,7 @@ iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - if (cmd_ver == 10) { + if (cmd_ver >= 10) { cmd.band = iwl_mvm_phy_band_from_nl80211(chandef->chan->band); } @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -422,6 +422,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, /* if reached this point, Alive notification was received */ iwl_mei_alive_notif(true); + iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); + ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait, &mvm->fw->ucode_capa); if (ret) { @@ -430,8 +432,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return ret; } - iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); - /* * Note: all the queues are enabled as part of the interface * initialization, but in firmware restart scenarios they @@ -642,7 +642,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) /* if we needed reset then fail here, but notify and remove */ if (mvm->fw_product_reset) { iwl_mei_alive_notif(false); - iwl_trans_pcie_remove(mvm->trans, true); + iwl_trans_pcie_reset(mvm->trans, + IWL_RESET_MODE_RESCAN); } goto error; @@ -863,7 +864,10 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { u32 cmd_id = REDUCE_TX_POWER_CMD; - struct iwl_dev_tx_power_cmd cmd = { + struct iwl_dev_tx_power_cmd_v3_v8 cmd = { + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + }; + struct iwl_dev_tx_power_cmd cmd_v9_v10 = { .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; __le16 *per_chain; @@ -871,8 +875,19 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) u16 len = 0; u32 n_subbands; u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3); + void *cmd_data = &cmd; - if (cmd_ver >= 7) { + if (cmd_ver == 10) { + len = sizeof(cmd_v9_v10.v10); + n_subbands = IWL_NUM_SUB_BANDS_V2; + per_chain = &cmd_v9_v10.v10.per_chain[0][0][0]; + cmd_v9_v10.v10.flags = + cpu_to_le32(mvm->fwrt.reduced_power_flags); + } else if (cmd_ver == 9) { + len = sizeof(cmd_v9_v10.v9); + n_subbands = IWL_NUM_SUB_BANDS_V1; + per_chain = &cmd_v9_v10.v9.per_chain[0][0]; + } else if (cmd_ver >= 7) { len = sizeof(cmd.v7); n_subbands = IWL_NUM_SUB_BANDS_V2; per_chain = cmd.v7.per_chain[0][0]; @@ -899,9 +914,14 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) per_chain = cmd.v3.per_chain[0][0]; } - /* all structs have the same common part, add it */ + /* all structs have the same common part, add its length */ len += sizeof(cmd.common); + if (cmd_ver < 9) + len += sizeof(cmd.per_band); + else + cmd_data = &cmd_v9_v10; + ret = iwl_sar_fill_profile(&mvm->fwrt, per_chain, IWL_NUM_CHAIN_TABLES, n_subbands, prof_a, prof_b); @@ -913,7 +933,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) iwl_mei_set_power_limit(per_chain); IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); - return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, cmd_data); } int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) @@ -1074,36 +1094,40 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) return iwl_mvm_ppag_send_cmd(mvm); } -static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc) +static bool +iwl_mvm_add_to_tas_block_list(u16 *list, u8 *size, u16 mcc) { - int i; - u32 size = le32_to_cpu(*le_size); - /* Verify that there is room for another country */ - if (size >= IWL_WTAS_BLACK_LIST_MAX) + if (*size >= IWL_WTAS_BLACK_LIST_MAX) return false; - for (i = 0; i < size; i++) { - if (list[i] == cpu_to_le32(mcc)) + for (u8 i = 0; i < *size; i++) { + if (list[i] == mcc) return true; } - list[size++] = cpu_to_le32(mcc); - *le_size = cpu_to_le32(size); + list[*size++] = mcc; return true; } static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); - int ret; + int fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + struct iwl_tas_selection_data selection_data = {}; + struct iwl_tas_config_cmd_v2_v4 cmd_v2_v4 = {}; + struct iwl_tas_config_cmd cmd_v5 = {}; struct iwl_tas_data data = {}; - struct iwl_tas_config_cmd cmd = {}; - int cmd_size, fw_ver; + void *cmd_data = &cmd_v2_v4; + int cmd_size; + int ret; BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) != IWL_WTAS_BLACK_LIST_MAX); - BUILD_BUG_ON(ARRAY_SIZE(cmd.common.block_list_array) != + BUILD_BUG_ON(ARRAY_SIZE(cmd_v2_v4.common.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + BUILD_BUG_ON(ARRAY_SIZE(cmd_v5.block_list_array) != IWL_WTAS_BLACK_LIST_MAX); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { @@ -1119,7 +1143,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - if (ret == 0) + if (ret == 0 && fw_ver < 5) return; if (!iwl_is_tas_approved()) { @@ -1142,27 +1166,49 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); } - fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); - - memcpy(&cmd.common, &data, sizeof(struct iwl_tas_config_cmd_common)); - - /* Set v3 or v4 specific parts. will be trunctated for fw_ver < 3 */ - if (fw_ver == 4) { - cmd.v4.override_tas_iec = data.override_tas_iec; - cmd.v4.enable_tas_iec = data.enable_tas_iec; - cmd.v4.usa_tas_uhb_allowed = data.usa_tas_uhb_allowed; + if (fw_ver < 5) { + selection_data = iwl_parse_tas_selection(data.tas_selection, + data.table_revision); + cmd_v2_v4.common.block_list_size = + cpu_to_le32(data.block_list_size); + for (u8 i = 0; i < data.block_list_size; i++) + cmd_v2_v4.common.block_list_array[i] = + cpu_to_le32(data.block_list_array[i]); + } + + if (fw_ver == 5) { + cmd_size = sizeof(cmd_v5); + cmd_data = &cmd_v5; + cmd_v5.block_list_size = cpu_to_le16(data.block_list_size); + for (u16 i = 0; i < data.block_list_size; i++) + cmd_v5.block_list_array[i] = + cpu_to_le16(data.block_list_array[i]); + cmd_v5.tas_config_info.table_source = data.table_source; + cmd_v5.tas_config_info.table_revision = data.table_revision; + cmd_v5.tas_config_info.value = cpu_to_le32(data.tas_selection); + } else if (fw_ver == 4) { + cmd_size = sizeof(cmd_v2_v4.common) + sizeof(cmd_v2_v4.v4); + cmd_v2_v4.v4.override_tas_iec = selection_data.override_tas_iec; + cmd_v2_v4.v4.enable_tas_iec = selection_data.enable_tas_iec; + cmd_v2_v4.v4.usa_tas_uhb_allowed = + selection_data.usa_tas_uhb_allowed; + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT) && + selection_data.canada_tas_uhb_allowed) + cmd_v2_v4.v4.uhb_allowed_flags = TAS_UHB_ALLOWED_CANADA; + } else if (fw_ver == 3) { + cmd_size = sizeof(cmd_v2_v4.common) + sizeof(cmd_v2_v4.v3); + cmd_v2_v4.v3.override_tas_iec = + cpu_to_le16(selection_data.override_tas_iec); + cmd_v2_v4.v3.enable_tas_iec = + cpu_to_le16(selection_data.enable_tas_iec); + } else if (fw_ver == 2) { + cmd_size = sizeof(cmd_v2_v4.common); } else { - cmd.v3.override_tas_iec = cpu_to_le16(data.override_tas_iec); - cmd.v3.enable_tas_iec = cpu_to_le16(data.enable_tas_iec); + return; } - cmd_size = sizeof(struct iwl_tas_config_cmd_common); - if (fw_ver >= 3) - /* v4 is the same size as v3 */ - cmd_size += sizeof(struct iwl_tas_config_cmd_v3); - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, cmd_data); if (ret < 0) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } @@ -1288,8 +1334,8 @@ static void iwl_mvm_disconnect_iterator(void *data, u8 *mac, void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) { u32 error_log_size = mvm->fw->ucode_capa.error_log_size; + u32 status = 0; int ret; - u32 resp; struct iwl_fw_error_recovery_cmd recovery_cmd = { .flags = cpu_to_le32(flags), @@ -1297,7 +1343,6 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) }; struct iwl_host_cmd host_cmd = { .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD), - .flags = CMD_WANT_SKB, .data = {&recovery_cmd, }, .len = {sizeof(recovery_cmd), }, }; @@ -1317,7 +1362,7 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) recovery_cmd.buf_size = cpu_to_le32(error_log_size); } - ret = iwl_mvm_send_cmd(mvm, &host_cmd); + ret = iwl_mvm_send_cmd_status(mvm, &host_cmd, &status); kfree(mvm->error_recovery_buf); mvm->error_recovery_buf = NULL; @@ -1328,11 +1373,10 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) /* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */ if (flags & ERROR_RECOVERY_UPDATE_DB) { - resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data); - if (resp) { + if (status) { IWL_ERR(mvm, "Failed to send recovery cmd blob was invalid %d\n", - resp); + status); ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_disconnect_iterator, @@ -1384,6 +1428,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) int ret, i; struct ieee80211_supported_band *sband = NULL; + lockdep_assert_wiphy(mvm->hw->wiphy); lockdep_assert_held(&mvm->mutex); ret = iwl_trans_start_hw(mvm->trans); @@ -1464,10 +1509,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) RCU_INIT_POINTER(mvm->fw_id_to_link_sta[i], NULL); } - for (i = 0; i < IWL_MVM_FW_MAX_LINK_ID + 1; i++) + for (i = 0; i < IWL_FW_MAX_LINK_ID + 1; i++) RCU_INIT_POINTER(mvm->link_id_to_link_conf[i], NULL); - mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; + mvm->tdls_cs.peer.sta_id = IWL_INVALID_STA; /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); @@ -1603,6 +1648,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) { int ret, i; + lockdep_assert_wiphy(mvm->hw->wiphy); lockdep_assert_held(&mvm->mutex); ret = iwl_trans_start_hw(mvm->trans); diff --git a/mvm/link.c b/mvm/link.c index a9929aa49913..851869c0bd50 100644 --- a/mvm/link.c +++ b/mvm/link.c @@ -12,12 +12,14 @@ HOW(BLOCKED_FW) \ HOW(BLOCKED_NON_BSS) \ HOW(BLOCKED_ROC) \ + HOW(BLOCKED_TMP_NON_BSS) \ HOW(EXIT_MISSED_BEACON) \ HOW(EXIT_LOW_RSSI) \ HOW(EXIT_COEX) \ HOW(EXIT_BANDWIDTH) \ HOW(EXIT_CSA) \ - HOW(EXIT_LINK_USAGE) + HOW(EXIT_LINK_USAGE) \ + HOW(EXIT_FAIL_ENTRY) static const char *const iwl_mvm_esr_states_names[] = { #define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x, @@ -233,10 +235,15 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, WARN_ON_ONCE(active == link_info->active); /* When deactivating a link session protection should - * be stopped + * be stopped. Also let the firmware know if we can't Tx. */ - if (!active && vif->type == NL80211_IFTYPE_STATION) + if (!active && vif->type == NL80211_IFTYPE_STATION) { iwl_mvm_stop_session_protection(mvm, vif); + if (link_info->csa_block_tx) { + cmd.block_tx = 1; + link_info->csa_block_tx = false; + } + } } cmd.link_id = cpu_to_le32(link_info->fw_link_id); @@ -258,7 +265,7 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); - iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf, + iwl_mvm_set_fw_basic_rates(mvm, vif, link_info, &cmd.cck_rates, &cmd.ofdm_rates); cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble); @@ -293,6 +300,17 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, (link_conf->uora_ocw_range >> 3) & 0x7; } + /* ap_sta may be NULL if we're disconnecting */ + if (changes & LINK_CONTEXT_MODIFY_HE_PARAMS && mvmvif->ap_sta) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_check(mvmvif->ap_sta, link_id); + + if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he && + link_sta->he_cap.he_cap_elem.mac_cap_info[5] & + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX) + cmd.ul_mu_data_disable = 1; + } + /* TODO how to set ndp_fdbk_buff_th_exp? */ if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id], @@ -343,7 +361,8 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, send_cmd: cmd.modify_mask = cpu_to_le32(changes); cmd.flags = cpu_to_le32(flags); - cmd.flags_mask = cpu_to_le32(flags_mask); + if (cmd_ver < 6) + cmd.flags_mask = cpu_to_le32(flags_mask); cmd.spec_link_id = link_conf->link_id; if (cmd_ver < 2) cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac); @@ -393,9 +412,8 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE); - if (!ret) - if (iwl_mvm_sf_update(mvm, vif, true)) - IWL_ERR(mvm, "Failed to update SF state\n"); + if (!ret && iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); return ret; } @@ -743,9 +761,8 @@ bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif, iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false)) return false; - if (a->chandef->width != b->chandef->width || - !(a->chandef->chan->band == NL80211_BAND_6GHZ && - b->chandef->chan->band == NL80211_BAND_5GHZ)) + if (a->chandef->chan->band == b->chandef->chan->band || + a->chandef->width != b->chandef->width) ret |= IWL_MVM_ESR_EXIT_BANDWIDTH; if (ret) { @@ -1148,3 +1165,14 @@ void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (!mvmvif->esr_disable_reason) iwl_mvm_esr_unblocked(mvm, vif); } + +void iwl_mvm_init_link(struct iwl_mvm_vif_link_info *link) +{ + link->bcast_sta.sta_id = IWL_INVALID_STA; + link->mcast_sta.sta_id = IWL_INVALID_STA; + link->ap_sta_id = IWL_INVALID_STA; + + for (int r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) + link->smps_requests[r] = + IEEE80211_SMPS_AUTOMATIC; +} diff --git a/mvm/mac-ctxt.c b/mvm/mac-ctxt.c index dfcc96f18b4f..6b06732441c3 100644 --- a/mvm/mac-ctxt.c +++ b/mvm/mac-ctxt.c @@ -12,6 +12,7 @@ #include "fw-api.h" #include "mvm.h" #include "time-event.h" +#include "iwl-utils.h" const u8 iwl_mvm_ac_to_tx_fifo[] = { IWL_MVM_TX_FIFO_VO, @@ -216,7 +217,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) .preferred_tsf = NUM_TSF_IDS, .found_vif = false, }; - int ret, i; + int ret; lockdep_assert_held(&mvm->mutex); @@ -298,9 +299,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->time_event_data.id = TE_MAX; mvmvif->roc_activity = ROC_NUM_ACTIVITIES; - mvmvif->deflink.bcast_sta.sta_id = IWL_MVM_INVALID_STA; - mvmvif->deflink.mcast_sta.sta_id = IWL_MVM_INVALID_STA; - mvmvif->deflink.ap_sta_id = IWL_MVM_INVALID_STA; + iwl_mvm_init_link(&mvmvif->deflink); /* No need to allocate data queues to P2P Device MAC and NAN.*/ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) @@ -316,9 +315,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->deflink.cab_queue = IWL_MVM_DQA_GCAST_QUEUE; } - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) - mvmvif->deflink.smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; - return 0; exit_fail: @@ -413,19 +409,18 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, } void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf, + struct iwl_mvm_vif_link_info *link_info, __le32 *cck_rates, __le32 *ofdm_rates) { - struct ieee80211_chanctx_conf *chanctx; + struct iwl_mvm_phy_ctxt *phy_ctxt; u8 cck_ack_rates = 0, ofdm_ack_rates = 0; + enum nl80211_band band = NL80211_BAND_2GHZ; - rcu_read_lock(); - chanctx = rcu_dereference(link_conf->chanctx_conf); - iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band - : NL80211_BAND_2GHZ, - &cck_ack_rates, &ofdm_ack_rates); + phy_ctxt = link_info->phy_ctxt; + if (phy_ctxt && phy_ctxt->channel) + band = phy_ctxt->channel->band; - rcu_read_unlock(); + iwl_mvm_ack_rates(mvm, vif, band, &cck_ack_rates, &ofdm_ack_rates); *cck_rates = cpu_to_le32((u32)cck_ack_rates); *ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); @@ -563,7 +558,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, else eth_broadcast_addr(cmd->bssid_addr); - iwl_mvm_set_fw_basic_rates(mvm, vif, &vif->bss_conf, &cmd->cck_rates, + iwl_mvm_set_fw_basic_rates(mvm, vif, &mvmvif->deflink, &cmd->cck_rates, &cmd->ofdm_rates); cmd->cck_short_preamble = @@ -874,23 +869,6 @@ void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, } } -u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) -{ - struct ieee80211_mgmt *mgmt = (void *)beacon; - const u8 *ie; - - if (WARN_ON_ONCE(frame_size <= (mgmt->u.beacon.variable - beacon))) - return 0; - - frame_size -= mgmt->u.beacon.variable - beacon; - - ie = cfg80211_find_ie(eid, mgmt->u.beacon.variable, frame_size); - if (!ie) - return 0; - - return ie - beacon; -} - u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, struct ieee80211_tx_info *info, struct ieee80211_vif *vif) @@ -1084,22 +1062,23 @@ static int iwl_mvm_mac_ctxt_send_beacon_v7(struct iwl_mvm *mvm, beacon->data, beacon->len); beacon_cmd.csa_offset = - cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_CHANNEL_SWITCH, - beacon->len)); + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); beacon_cmd.ecsa_offset = - cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_EXT_CHANSWITCH_ANN, - beacon->len)); + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); return iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, sizeof(beacon_cmd)); } bool iwl_mvm_enable_fils(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) { - if (IWL_MVM_DISABLE_AP_FILS) + if (vif->type != NL80211_IFTYPE_AP || IWL_MVM_DISABLE_AP_FILS) return false; if (cfg80211_channel_is_psc(ctx->def.chan)) @@ -1128,7 +1107,7 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm, ctx = rcu_dereference(link_conf->chanctx_conf); channel = ieee80211_frequency_to_channel(ctx->def.chan->center_freq); WARN_ON(channel == 0); - if (iwl_mvm_enable_fils(mvm, ctx)) { + if (iwl_mvm_enable_fils(mvm, vif, ctx)) { flags |= iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) > 10 ? IWL_MAC_BEACON_FILS : @@ -1157,20 +1136,20 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm, beacon->data, beacon->len); beacon_cmd.csa_offset = - cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_CHANNEL_SWITCH, - beacon->len)); + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); beacon_cmd.ecsa_offset = - cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_EXT_CHANSWITCH_ANN, - beacon->len)); + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); if (vif->type == NL80211_IFTYPE_AP && iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) >= 14) beacon_cmd.btwt_offset = - cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, - WLAN_EID_S1G_TWT, - beacon->len)); + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len)); return iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, sizeof(beacon_cmd)); @@ -1528,7 +1507,7 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); if (!iwl_mvm_is_short_beacon_notif_supported(mvm)) { - struct iwl_mvm_tx_resp *beacon_notify_hdr = + struct iwl_tx_resp *beacon_notify_hdr = &beacon_v5->beacon_notify_hdr; if (unlikely(pkt_len < sizeof(*beacon_v5))) @@ -1586,11 +1565,11 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, } } -void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) +static void +iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm, + const struct iwl_missed_beacons_notif *mb, + struct iwl_rx_packet *pkt) { - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacons_notif *mb = (void *)pkt->data; struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig; struct iwl_fw_dbg_trigger_tlv *trigger; u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx; @@ -1604,13 +1583,24 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, u8 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, MISSED_BEACONS_NOTIFICATION, 0); + u8 new_notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, + MISSED_BEACONS_NOTIF, 0); + struct ieee80211_bss_conf *bss_conf; + + /* If the firmware uses the new notification (from MAC_CONF_GROUP), + * refer to that notification's version. + * Note that the new notification from MAC_CONF_GROUP starts from + * version 5. + */ + if (new_notif_ver) + notif_ver = new_notif_ver; /* before version four the ID in the notification refers to mac ID */ if (notif_ver < 4) { vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, false); + bss_conf = &vif->bss_conf; } else { - struct ieee80211_bss_conf *bss_conf = - iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, id, false); + bss_conf = iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, id, false); if (!bss_conf) return; @@ -1620,13 +1610,11 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, } IWL_DEBUG_INFO(mvm, - "missed bcn %s_id=%u, consecutive=%u (%u, %u, %u)\n", + "missed bcn %s_id=%u, consecutive=%u (%u)\n", notif_ver < 4 ? "mac" : "link", id, le32_to_cpu(mb->consec_missed_beacons), - le32_to_cpu(mb->consec_missed_beacons_since_last_rx), - le32_to_cpu(mb->num_recvd_beacons), - le32_to_cpu(mb->num_expected_beacons)); + le32_to_cpu(mb->consec_missed_beacons_since_last_rx)); if (!vif) return; @@ -1656,15 +1644,41 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, "missed_beacons:%d, missed_beacons_since_rx:%d\n", rx_missed_bcon, rx_missed_bcon_since_rx); } - } else if (rx_missed_bcon >= IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH && - link_id >= 0 && hweight16(vif->active_links) > 1) { - iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_MISSED_BEACON, - iwl_mvm_get_other_link(vif, link_id)); + } else if (link_id >= 0 && hweight16(vif->active_links) > 1) { + u32 bss_param_ch_cnt_link_id = + bss_conf->bss_param_ch_cnt_link_id; + u32 scnd_lnk_bcn_lost = 0; + + if (notif_ver >= 5 && + !IWL_FW_CHECK(mvm, + le32_to_cpu(mb->other_link_id) == IWL_MVM_FW_LINK_ID_INVALID, + "No data for other link id but we are in EMLSR active_links: 0x%x\n", + vif->active_links)) + scnd_lnk_bcn_lost = + le32_to_cpu(mb->consec_missed_beacons_other_link); + + /* Exit EMLSR if we lost more than + * IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links + * OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH on any link. + * OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED + * and the link's bss_param_ch_count has changed. + */ + if ((rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && + scnd_lnk_bcn_lost >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) || + rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH || + (bss_param_ch_cnt_link_id != link_id && + rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) + iwl_mvm_exit_esr(mvm, vif, + IWL_MVM_ESR_EXIT_MISSED_BEACON, + iwl_mvm_get_primary_link(vif)); } else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) { if (!iwl_mvm_has_new_tx_api(mvm)) ieee80211_beacon_loss(vif); else ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); + + /* try to switch links, no-op if we don't have MLO */ + iwl_mvm_int_mlo_scan(mvm, vif); } iwl_dbg_tlv_time_point(&mvm->fwrt, @@ -1687,6 +1701,31 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, iwl_fw_dbg_collect_trig(&mvm->fwrt, trigger, NULL); } +void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + iwl_mvm_handle_missed_beacons_notif(mvm, (const void *)pkt->data, pkt); +} + +void iwl_mvm_rx_missed_beacons_notif_legacy(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + const struct iwl_missed_beacons_notif_v4 *mb_v4 = + (const void *)pkt->data; + struct iwl_missed_beacons_notif mb = { + .link_id = mb_v4->link_id, + .consec_missed_beacons = mb_v4->consec_missed_beacons, + .consec_missed_beacons_since_last_rx = + mb_v4->consec_missed_beacons_since_last_rx, + .other_link_id = cpu_to_le32(IWL_MVM_FW_LINK_ID_INVALID), + }; + + iwl_mvm_handle_missed_beacons_notif(mvm, &mb, pkt); +} + void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { @@ -1713,7 +1752,7 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, data = sb_v2->data; } else { - struct iwl_stored_beacon_notif_v3 *sb_v3 = (void *)pkt->data; + struct iwl_stored_beacon_notif *sb_v3 = (void *)pkt->data; if (pkt_len < struct_size(sb_v3, data, size)) return; @@ -1918,7 +1957,7 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, if (csa_err_mask & (CS_ERR_COUNT_ERROR | CS_ERR_LONG_DELAY_AFTER_CS | CS_ERR_TX_BLOCK_TIMER_EXPIRED)) - ieee80211_channel_switch_disconnect(vif, true); + ieee80211_channel_switch_disconnect(vif); rcu_read_unlock(); } diff --git a/mvm/mac80211.c b/mvm/mac80211.c index 625ccf566e1c..af6644b7e95f 100644 --- a/mvm/mac80211.c +++ b/mvm/mac80211.c @@ -165,12 +165,8 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, mvm->lar_regdom_set = true; mvm->mcc_src = src_id; - /* Some kind of regulatory mess means we need to currently disallow - * puncturing in the US and Canada. Do that here, at least until we - * figure out the new chanctx APIs for puncturing. - */ - if (resp->mcc == cpu_to_le16(IWL_MCC_US) || - resp->mcc == cpu_to_le16(IWL_MCC_CANADA)) + if (!iwl_puncturing_is_allowed_in_bios(mvm->bios_enable_puncturing, + le16_to_cpu(resp->mcc))) ieee80211_hw_set(mvm->hw, DISALLOW_PUNCTURING); else __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mvm->hw->flags); @@ -639,10 +635,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) NL80211_FEATURE_LOW_PRIORITY_SCAN | NL80211_FEATURE_P2P_GO_OPPPS | NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | - NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_STATIC_SMPS | NL80211_FEATURE_SUPPORTS_WMM_ADMISSION; + /* when firmware supports RLC/SMPS offload, do not set these + * driver features, since it's no longer supported by driver. + */ + if (!iwl_mvm_has_rlc_offload(mvm)) + hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS | + NL80211_FEATURE_DYNAMIC_SMPS; + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)) hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; @@ -838,20 +839,10 @@ void iwl_mvm_mac_tx(struct ieee80211_hw *hw, if (ieee80211_is_mgmt(hdr->frame_control)) sta = NULL; - /* If there is no sta, and it's not offchannel - send through AP */ + /* this shouldn't even happen: just drop */ if (!sta && info->control.vif->type == NL80211_IFTYPE_STATION && - !offchannel) { - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(info->control.vif); - u8 ap_sta_id = READ_ONCE(mvmvif->deflink.ap_sta_id); - - if (ap_sta_id < mvm->fw->ucode_capa.num_stations) { - /* mac80211 holds rcu read lock */ - sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]); - if (IS_ERR_OR_NULL(sta)) - goto drop; - } - } + !offchannel) + goto drop; if (tmp_sta && !sta && link_id != IEEE80211_LINK_UNSPECIFIED && !ieee80211_is_probe_resp(hdr->frame_control)) { @@ -1118,7 +1109,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; for_each_mvm_vif_valid_link(mvmvif, link_id) { - mvmvif->link[link_id]->ap_sta_id = IWL_MVM_INVALID_STA; + mvmvif->link[link_id]->ap_sta_id = IWL_INVALID_STA; mvmvif->link[link_id]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; mvmvif->link[link_id]->phy_ctxt = NULL; mvmvif->link[link_id]->active = 0; @@ -1162,7 +1153,7 @@ static void iwl_mvm_cleanup_sta_iterator(void *data, struct ieee80211_sta *sta) * Delete the stale data to avoid issues later on. */ iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, - link_id, false); + link_id); } } } @@ -1241,11 +1232,12 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm) mvm->nvm_data = NULL; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* fast_resume will be cleared by iwl_mvm_fast_resume */ fast_resume = mvm->fast_resume; if (fast_resume) { + iwl_mvm_mei_device_state(mvm, true); ret = iwl_mvm_fast_resume(mvm); if (ret) { iwl_mvm_stop_device(mvm); @@ -1263,7 +1255,7 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm) set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); } } -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) { /* @@ -1302,21 +1294,22 @@ int iwl_mvm_mac_start(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + int retry, max_retry = 0; mutex_lock(&mvm->mutex); /* we are starting the mac not in error flow, and restart is enabled */ if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) && - iwlwifi_mod_params.fw_restart) { - /* - * This will prevent mac80211 recovery flows to trigger during - * init failures - */ - set_bit(IWL_MVM_STATUS_STARTING, &mvm->status); - } + iwlwifi_mod_params.fw_restart) + max_retry = IWL_MAX_INIT_RETRY; + + for (retry = 0; retry <= max_retry; retry++) { + ret = __iwl_mvm_mac_start(mvm); + if (ret != -ETIMEDOUT) + break; - ret = __iwl_mvm_mac_start(mvm); - clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status); + IWL_ERR(mvm, "mac start retry %d\n", retry); + } mutex_unlock(&mvm->mutex); @@ -1345,6 +1338,13 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) * of packets the FW sent out, so we must reconnect. */ iwl_mvm_teardown_tdls_peers(mvm); + + IWL_INFO(mvm, "restart completed\n"); + iwl_trans_finish_sw_reset(mvm->trans); + + /* no need to lock, adding in parallel would schedule too */ + if (!list_empty(&mvm->add_stream_txqs)) + schedule_work(&mvm->add_stream_wk); } void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, @@ -1378,10 +1378,13 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend) iwl_mvm_rm_aux_sta(mvm); if (suspend && - mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { iwl_mvm_fast_suspend(mvm); - else + /* From this point on, we won't touch the device */ + iwl_mvm_mei_device_state(mvm, false); + } else { iwl_mvm_stop_device(mvm); + } iwl_mvm_async_handlers_purge(mvm); /* async_handlers_list is empty and will stay empty: HW is stopped */ @@ -1475,24 +1478,46 @@ struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) return NULL; } -int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, + struct ieee80211_bss_conf *link_conf, s16 tx_power) { u32 cmd_id = REDUCE_TX_POWER_CMD; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(link_conf->vif); + u32 mac_id = mvmvif->id; int len; - struct iwl_dev_tx_power_cmd cmd = { - .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), - .common.mac_context_id = - cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), - .common.pwr_restriction = cpu_to_le16(8 * tx_power), + struct iwl_dev_tx_power_cmd_v3_v8 cmd = { + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK), + .common.link_id = cpu_to_le32(mac_id), }; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); + struct iwl_dev_tx_power_cmd cmd_v9_v10; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3); + u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ? + IWL_DEV_MAX_TX_POWER : 8 * tx_power; + void *cmd_data = &cmd; + + cmd.common.pwr_restriction = cpu_to_le16(u_tx_power); + + if (cmd_ver > 8) { + u32 link_id; + + if (WARN_ON(!mvmvif->link[link_conf->link_id])) + return -ENODEV; + + link_id = mvmvif->link[link_conf->link_id]->fw_link_id; - if (tx_power == IWL_DEFAULT_MAX_TX_POWER) - cmd.common.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); + /* Those fields sit on the same place for v9 and v10 */ + cmd_v9_v10.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK); + cmd_v9_v10.common.link_id = cpu_to_le32(link_id); + cmd_v9_v10.common.pwr_restriction = cpu_to_le16(u_tx_power); + cmd_data = &cmd_v9_v10; + } - if (cmd_ver == 8) + if (cmd_ver == 10) + len = sizeof(cmd_v9_v10.v10); + else if (cmd_ver == 9) + len = sizeof(cmd_v9_v10.v9); + else if (cmd_ver == 8) len = sizeof(cmd.v8); else if (cmd_ver == 7) len = sizeof(cmd.v7); @@ -1507,10 +1532,14 @@ int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, else len = sizeof(cmd.v3); - /* all structs have the same common part, add it */ + /* all structs have the same common part, add its length */ len += sizeof(cmd.common); - return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); + if (cmd_ver < 9) + len += sizeof(cmd.per_band); + + return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, cmd_data); + } static void iwl_mvm_post_csa_tx(void *data, struct ieee80211_sta *sta) @@ -1722,6 +1751,21 @@ static void iwl_mvm_unblock_esr_tpt(struct wiphy *wiphy, struct wiphy_work *wk) iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_TPT); } +static void iwl_mvm_unblock_esr_tmp_non_bss(struct wiphy *wiphy, + struct wiphy_work *wk) +{ + struct iwl_mvm_vif *mvmvif = + container_of(wk, struct iwl_mvm_vif, + unblock_esr_tmp_non_bss_wk.work); + struct iwl_mvm *mvm = mvmvif->mvm; + struct ieee80211_vif *vif = + container_of((void *)mvmvif, struct ieee80211_vif, drv_priv); + + mutex_lock(&mvm->mutex); + iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_TMP_NON_BSS); + mutex_unlock(&mvm->mutex); +} + void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif) { lockdep_assert_held(&mvm->mutex); @@ -1740,6 +1784,9 @@ void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif) wiphy_work_init(&mvmvif->unblock_esr_tpt_wk, iwl_mvm_unblock_esr_tpt); + + wiphy_delayed_work_init(&mvmvif->unblock_esr_tmp_non_bss_wk, + iwl_mvm_unblock_esr_tmp_non_bss); } static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, @@ -1761,6 +1808,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mvmvif->deflink.active = 0; mvmvif->link[0] = &mvmvif->deflink; + vif->driver_flags = IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC; + ret = iwl_mvm_set_link_mapping(mvm, vif, &vif->bss_conf); if (ret) goto out; @@ -1890,6 +1939,8 @@ void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, &mvmvif->mlo_int_scan_wk); wiphy_work_cancel(mvm->hw->wiphy, &mvmvif->unblock_esr_tpt_wk); + wiphy_delayed_work_cancel(mvm->hw->wiphy, + &mvmvif->unblock_esr_tmp_non_bss_wk); cancel_delayed_work_sync(&mvmvif->csa_work); } @@ -1961,7 +2012,6 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = NULL; } - iwl_mvm_unset_link_mapping(mvm, vif, &vif->bss_conf); iwl_mvm_mac_ctxt_remove(mvm, vif); RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); @@ -1970,6 +2020,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->monitor_on = false; out: + iwl_mvm_unset_link_mapping(mvm, vif, &vif->bss_conf); if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { iwl_mvm_dealloc_int_sta(mvm, &mvmvif->deflink.mcast_sta); @@ -2912,7 +2963,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_MVM_SMPS_REQ_PROT, IEEE80211_SMPS_DYNAMIC, 0); } - } else if (mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { + } else if (mvmvif->deflink.ap_sta_id != IWL_INVALID_STA) { iwl_mvm_mei_host_disassociated(mvm); /* * If update fails - SF might be running in associated @@ -2924,33 +2975,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, &mvm->status), "Failed to update SF upon disassociation\n"); - /* - * If we get an assert during the connection (after the - * station has been added, but before the vif is set - * to associated), mac80211 will re-add the station and - * then configure the vif. Since the vif is not - * associated, we would remove the station here and - * this would fail the recovery. - */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - /* first remove remaining keys */ - iwl_mvm_sec_key_remove_ap(mvm, vif, - &mvmvif->deflink, 0); - - /* - * Remove AP station now that - * the MAC is unassoc - */ - ret = iwl_mvm_rm_sta_id(mvm, vif, - mvmvif->deflink.ap_sta_id); - if (ret) - IWL_ERR(mvm, - "failed to remove AP station\n"); - - mvmvif->deflink.ap_sta_id = IWL_MVM_INVALID_STA; - } - /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) @@ -3294,7 +3318,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_TXPOWER) { IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", bss_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); + iwl_mvm_set_tx_power(mvm, bss_conf, bss_conf->txpower); } } @@ -3405,7 +3429,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, */ break; case STA_NOTIFY_AWAKE: - if (WARN_ON(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON(mvmsta->deflink.sta_id == IWL_INVALID_STA)) break; if (txqs) @@ -3485,6 +3509,8 @@ void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); unsigned int link_id; + lockdep_assert_wiphy(mvm->hw->wiphy); + /* * This is called before mac80211 does RCU synchronisation, * so here we already invalidate our internal RCU-protected @@ -3868,7 +3894,7 @@ iwl_mvm_sta_state_notexist_to_none(struct iwl_mvm *mvm, if (sta->tdls && (vif->p2p || - iwl_mvm_tdls_sta_count(mvm, NULL) == IWL_MVM_TDLS_STA_COUNT || + iwl_mvm_tdls_sta_count(mvm, NULL) == IWL_TDLS_STA_COUNT || iwl_mvm_phy_ctx_count(mvm) > 1)) { IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); return -EBUSY; @@ -4066,10 +4092,8 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, &mvmvif->mlo_int_scan_wk); wiphy_work_cancel(mvm->hw->wiphy, &mvmvif->unblock_esr_tpt_wk); - - /* No need for the periodic statistics anymore */ - if (ieee80211_vif_is_mld(vif) && mvmvif->esr_active) - iwl_mvm_request_periodic_system_statistics(mvm, false); + wiphy_delayed_work_cancel(mvm->hw->wiphy, + &mvmvif->unblock_esr_tmp_non_bss_wk); } return 0; @@ -4227,8 +4251,9 @@ int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) } void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u32 changed) + struct ieee80211_link_sta *link_sta, u32 changed) { + struct ieee80211_sta *sta = link_sta->sta; struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); if (changed & (IEEE80211_RC_BW_CHANGED | @@ -4956,34 +4981,46 @@ int iwl_mvm_cancel_roc(struct ieee80211_hw *hw, return 0; } -struct iwl_mvm_ftm_responder_iter_data { - bool responder; +struct iwl_mvm_chanctx_usage_data { + struct iwl_mvm *mvm; struct ieee80211_chanctx_conf *ctx; + bool use_def; }; -static void iwl_mvm_ftm_responder_chanctx_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_chanctx_usage_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) { - struct iwl_mvm_ftm_responder_iter_data *data = _data; + struct iwl_mvm_chanctx_usage_data *data = _data; + struct ieee80211_bss_conf *link_conf; + int link_id; - if (rcu_access_pointer(vif->bss_conf.chanctx_conf) == data->ctx && - vif->type == NL80211_IFTYPE_AP && vif->bss_conf.ftmr_params) - data->responder = true; + for_each_vif_active_link(vif, link_conf, link_id) { + if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) + continue; + + if (iwl_mvm_enable_fils(data->mvm, vif, data->ctx)) + data->use_def = true; + + if (vif->type == NL80211_IFTYPE_AP && link_conf->ftmr_params) + data->use_def = true; + } } -bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) +struct cfg80211_chan_def * +iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx) { - struct iwl_mvm_ftm_responder_iter_data data = { - .responder = false, + struct iwl_mvm_chanctx_usage_data data = { + .mvm = mvm, .ctx = ctx, + .use_def = false, }; ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_ftm_responder_chanctx_iter, - &data); - return data.responder; + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_chanctx_usage_iter, + &data); + + return data.use_def ? &ctx->def : &ctx->min_def; } static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, @@ -5058,7 +5095,7 @@ void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | IEEE80211_CHANCTX_CHANGE_RX_CHAINS | IEEE80211_CHANCTX_CHANGE_RADAR | - IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)), + IEEE80211_CHANCTX_CHANGE_MIN_DEF)), "Cannot change PHY. Ref=%d, changed=0x%X\n", phy_ctxt->ref, changed)) return; @@ -5066,7 +5103,7 @@ void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, guard(mvm)(mvm); /* we are only changing the min_width, may be a noop */ - if (changed == IEEE80211_CHANCTX_CHANGE_MIN_WIDTH) { + if (changed == IEEE80211_CHANCTX_CHANGE_MIN_DEF) { if (phy_ctxt->width == def->width) return; @@ -5367,7 +5404,7 @@ out_reassign: out_restart: /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); + iwl_force_nmi(mvm->trans); return ret; } @@ -5403,7 +5440,7 @@ out_reassign: out_restart: /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); + iwl_force_nmi(mvm->trans); return ret; } @@ -6230,7 +6267,7 @@ void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, guard(mvm)(mvm); - if (mvmvif->deflink.ap_sta_id != mvmsta->deflink.sta_id) + if (sta != mvmvif->ap_sta) return; if (iwl_mvm_request_statistics(mvm, false)) @@ -6553,7 +6590,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .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, + .link_sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, diff --git a/mvm/mld-key.c b/mvm/mld-key.c index 8a38fc4b0b0f..ef0be44207e1 100644 --- a/mvm/mld-key.c +++ b/mvm/mld-key.c @@ -144,7 +144,7 @@ static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw, if (sta != data->sta || key->link_id >= 0) return; - err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, sizeof(cmd), &cmd); + err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); if (err) data->err = err; @@ -162,8 +162,8 @@ int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, .new_sta_mask = new_sta_mask, }; - ieee80211_iter_keys_rcu(mvm->hw, vif, iwl_mvm_mld_update_sta_key, - &data); + ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key, + &data); return data.err; } @@ -396,13 +396,13 @@ void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION || - link->ap_sta_id == IWL_MVM_INVALID_STA)) + link->ap_sta_id == IWL_INVALID_STA)) return; if (!sec_key_ver) return; - ieee80211_iter_keys_rcu(mvm->hw, vif, - iwl_mvm_sec_key_remove_ap_iter, - (void *)(uintptr_t)link_id); + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_sec_key_remove_ap_iter, + (void *)(uintptr_t)link_id); } diff --git a/mvm/mld-mac80211.c b/mvm/mld-mac80211.c index 3c99396ad369..341a2a7a49ec 100644 --- a/mvm/mld-mac80211.c +++ b/mvm/mld-mac80211.c @@ -18,6 +18,8 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, mvmvif->mvm = mvm; + vif->driver_flags |= IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC; + /* Not much to do here. The stack will not allow interface * types or combinations that we didn't advertise, so we * don't really have to check the types. @@ -41,8 +43,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, /* reset deflink MLO parameters */ mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; mvmvif->deflink.active = 0; - /* the first link always points to the default one */ - mvmvif->link[0] = &mvmvif->deflink; ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); if (ret) @@ -60,9 +60,19 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, IEEE80211_VIF_SUPPORTS_CQM_RSSI; } - ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); - if (ret) - goto out_free_bf; + /* We want link[0] to point to the default link, unless we have MLO and + * in this case this will be modified later by .change_vif_links() + * If we are in the restart flow with an MLD connection, we will wait + * to .change_vif_links() to setup the links. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + !ieee80211_vif_is_mld(vif)) { + mvmvif->link[0] = &mvmvif->deflink; + + ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); + if (ret) + goto out_free_bf; + } /* Save a pointer to p2p device vif, so it can later be used to * update the p2p device MAC when a GO is started/stopped @@ -200,32 +210,6 @@ static unsigned int iwl_mvm_mld_count_active_links(struct iwl_mvm_vif *mvmvif) return n_active; } -static void iwl_mvm_restart_mpdu_count(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif) -{ - struct ieee80211_sta *ap_sta = mvmvif->ap_sta; - struct iwl_mvm_sta *mvmsta; - - lockdep_assert_held(&mvm->mutex); - - if (!ap_sta) - return; - - mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); - if (!mvmsta->mpdu_counters) - return; - - for (int q = 0; q < mvm->trans->num_rx_queues; q++) { - spin_lock_bh(&mvmsta->mpdu_counters[q].lock); - memset(mvmsta->mpdu_counters[q].per_link, 0, - sizeof(mvmsta->mpdu_counters[q].per_link)); - mvmsta->mpdu_counters[q].window_start = jiffies; - spin_unlock_bh(&mvmsta->mpdu_counters[q].lock); - } - - IWL_DEBUG_STATS(mvm, "MPDU counters are cleared\n"); -} - static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -259,15 +243,8 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, else mvmvif->primary_link = __ffs(vif->active_links); - /* Needed for tracking RSSI */ - iwl_mvm_request_periodic_system_statistics(mvm, true); - - /* - * Restart the MPDU counters and the counting window, so when the - * statistics arrive (which is where we look at the counters) we - * will be at the end of the window. - */ - iwl_mvm_restart_mpdu_count(mvm, mvmvif); + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP, + NULL); return ret; } @@ -312,7 +289,6 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, ret = iwl_mvm_esr_mode_active(mvm, vif); if (ret) { IWL_ERR(mvm, "failed to activate ESR mode (%d)\n", ret); - iwl_mvm_request_periodic_system_statistics(mvm, false); goto out; } } @@ -328,23 +304,18 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, if (ret) goto out; - /* Initialize rate control for the AP station, since we might be - * doing a link switch here - we cannot initialize it before since - * this needs the phy context assigned (and in FW?), and we cannot - * do it later because it needs to be initialized as soon as we're - * able to TX on the link, i.e. when active. + /* + * if link switching (link not active yet) we'll activate it in + * firmware later on link-info change, which mac80211 guarantees + * for link switch after the stations are set up */ - if (mvmvif->ap_sta) { - struct ieee80211_link_sta *link_sta; - - rcu_read_lock(); - link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]); - - if (!WARN_ON_ONCE(!link_sta)) - iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta, - link_conf, link_sta, - phy_ctxt->channel->band); - rcu_read_unlock(); + if (ieee80211_vif_link_active(vif, link_conf->link_id)) { + ret = iwl_mvm_link_changed(mvm, vif, link_conf, + LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_RATES_INFO, + true); + if (ret) + goto out; } if (vif->type == NL80211_IFTYPE_STATION) @@ -352,14 +323,6 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, link_conf, false); - /* then activate */ - ret = iwl_mvm_link_changed(mvm, vif, link_conf, - LINK_CONTEXT_MODIFY_ACTIVE | - LINK_CONTEXT_MODIFY_RATES_INFO, - true); - if (ret) - goto out; - /* * Power state must be updated before quotas, * otherwise fw will complain. @@ -451,10 +414,8 @@ static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, break; } - iwl_mvm_request_periodic_system_statistics(mvm, false); - - /* Start a new counting window */ - iwl_mvm_restart_mpdu_count(mvm, mvmvif); + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_ESR_LINK_DOWN, + NULL); return ret; } @@ -769,6 +730,11 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id])) return; + /* not yet marked active in vif means during link switch */ + if (!ieee80211_vif_link_active(vif, link_conf->link_id) && + vif->cfg.assoc && mvmvif->link[link_conf->link_id]->phy_ctxt) + link_changes |= LINK_CONTEXT_MODIFY_ACTIVE; + has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; @@ -818,37 +784,13 @@ static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif) int i; for_each_mvm_vif_valid_link(mvmvif, i) { - if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA) + if (mvmvif->link[i]->ap_sta_id != IWL_INVALID_STA) return true; } return false; } -static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i, ret; - - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - return; - - for_each_mvm_vif_valid_link(mvmvif, i) { - struct iwl_mvm_vif_link_info *link = mvmvif->link[i]; - - if (!link) - continue; - - iwl_mvm_sec_key_remove_ap(mvm, vif, link, i); - ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id); - if (ret) - IWL_ERR(mvm, "failed to remove AP station\n"); - - link->ap_sta_id = IWL_MVM_INVALID_STA; - } -} - static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u64 changes) @@ -875,8 +817,13 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, if (vif->cfg.assoc) { mvmvif->session_prot_connection_loss = false; - /* clear statistics to get clean beacon counter */ + /* + * Clear statistics to get clean beacon counter, and ask for + * periodic statistics, as they are needed for link + * selection and RX OMI decisions. + */ iwl_mvm_request_statistics(mvm, true); + iwl_mvm_request_periodic_system_statistics(mvm, true); iwl_mvm_sf_update(mvm, vif, false); iwl_mvm_power_vif_assoc(mvm, vif); @@ -924,6 +871,8 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { iwl_mvm_mei_host_disassociated(mvm); + iwl_mvm_request_periodic_system_statistics(mvm, false); + /* If update fails - SF might be running in associated * mode while disassociated - which is forbidden. */ @@ -932,15 +881,6 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status), "Failed to update SF upon disassociation\n"); - - /* If we get an assert during the connection (after the - * station has been added, but before the vif is set - * to associated), mac80211 will re-add the station and - * then configure the vif. Since the vif is not - * associated, we would remove the station here and - * this would fail the recovery. - */ - iwl_mvm_mld_vif_delete_all_stas(mvm, vif); } iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); @@ -1032,7 +972,7 @@ static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_TXPOWER) { IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", link_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower); + iwl_mvm_set_tx_power(mvm, link_conf, link_conf->txpower); } } @@ -1163,8 +1103,6 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, int err, i; for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - int r; - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) break; @@ -1176,19 +1114,17 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, goto free; } - new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA; - new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA; - new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA; new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; - - for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) - new_link[i]->smps_requests[r] = - IEEE80211_SMPS_AUTOMATIC; + iwl_mvm_init_link(new_link[i]); } mutex_lock(&mvm->mutex); - if (old_links == 0) { + /* If we're in RESTART flow, the default link wasn't added in + * drv_add_interface(), and link[0] doesn't point to it. + */ + if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); if (err) goto out_err; @@ -1335,6 +1271,22 @@ iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw, else selected = primary; + /* + * remembers to tell the firmware that this link can't tx + * Note that this logic seems to be unrelated to esr, but it + * really is needed only when esr is active. When we have a + * single link, the firmware will handle all this on its own. + * In multi-link scenarios, we can learn about the CSA from + * another link and this logic is too complex for the firmware + * to track. + * Since we want to de-activate the link that got a CSA, we + * need to tell the firmware not to send any frame on that link + * as the firmware may not be aware that link is under a CSA + * with mode=1 (no Tx allowed). + */ + if (chsw->block_tx && mvmvif->link[chsw->link_id]) + mvmvif->link[chsw->link_id]->csa_block_tx = true; + iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected); mutex_unlock(&mvm->mutex); @@ -1354,6 +1306,36 @@ iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw, return ret; } +#define IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT (5 * HZ) + +static void iwl_mvm_mld_prep_add_interface(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm); + struct iwl_mvm_vif *mvmvif; + int ret; + + IWL_DEBUG_MAC80211(mvm, "prep_add_interface: type=%u\n", + type); + + if (IS_ERR_OR_NULL(bss_vif) || + !(type == NL80211_IFTYPE_AP || + type == NL80211_IFTYPE_P2P_GO || + type == NL80211_IFTYPE_P2P_CLIENT)) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(bss_vif); + ret = iwl_mvm_block_esr_sync(mvm, bss_vif, + IWL_MVM_ESR_BLOCKED_TMP_NON_BSS); + if (ret) + return; + + wiphy_delayed_work_queue(mvmvif->mvm->hw->wiphy, + &mvmvif->unblock_esr_tmp_non_bss_wk, + IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT); +} + const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .tx = iwl_mvm_mac_tx, .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -1379,7 +1361,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .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, + .link_sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mld_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, @@ -1450,4 +1432,5 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .change_sta_links = iwl_mvm_mld_change_sta_links, .can_activate_links = iwl_mvm_mld_can_activate_links, .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm, + .prep_add_interface = iwl_mvm_mld_prep_add_interface, }; diff --git a/mvm/mld-sta.c b/mvm/mld-sta.c index d5a204e52076..2f159024eeb8 100644 --- a/mvm/mld-sta.c +++ b/mvm/mld-sta.c @@ -46,7 +46,7 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm, - struct iwl_mvm_sta_cfg_cmd *cmd) + struct iwl_sta_cfg_cmd *cmd) { int ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), @@ -63,7 +63,7 @@ static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, const u8 *addr, int link_id) { - struct iwl_mvm_sta_cfg_cmd cmd; + struct iwl_sta_cfg_cmd cmd; lockdep_assert_held(&mvm->mutex); @@ -94,7 +94,7 @@ static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm, */ static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id) { - struct iwl_mvm_remove_sta_cmd rm_sta_cmd = { + struct iwl_remove_sta_cmd rm_sta_cmd = { .sta_id = cpu_to_le32(sta_id), }; int ret; @@ -146,7 +146,7 @@ int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm, unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout : mvm->trans->trans_cfg->base_params->wd_timeout; - if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(sta->sta_id == IWL_INVALID_STA)) return -ENOSPC; if (sta->type == STATION_TYPE_AUX) @@ -216,7 +216,7 @@ int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; const u8 *baddr = _baddr; unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); + iwl_mvm_get_wd_timeout(mvm, vif); u16 *queue; lockdep_assert_held(&mvm->mutex); @@ -254,7 +254,7 @@ int iwl_mvm_mld_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *msta = &mvm_link->mcast_sta; static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; const u8 *maddr = _maddr; - unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); + unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif); lockdep_assert_held(&mvm->mutex); @@ -346,7 +346,7 @@ static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - if (WARN_ON_ONCE(int_sta->sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(int_sta->sta_id == IWL_INVALID_STA)) return -EINVAL; if (flush) @@ -438,7 +438,7 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif_link_info *link_info = mvm_vif->link[link_conf->link_id]; - struct iwl_mvm_sta_cfg_cmd cmd = { + struct iwl_sta_cfg_cmd cmd = { .sta_id = cpu_to_le32(mvm_link_sta->sta_id), .station_type = cpu_to_le32(mvm_sta->sta_type), }; @@ -518,11 +518,12 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta, struct iwl_mvm_link_sta *mvm_sta_link, - unsigned int link_id, - bool is_in_fw) + unsigned int link_id) { - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id], - is_in_fw ? ERR_PTR(-EINVAL) : NULL); + lockdep_assert_wiphy(mvm->hw->wiphy); + lockdep_assert_held(&mvm->mutex); + + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id], NULL); RCU_INIT_POINTER(mvm->fw_id_to_link_sta[mvm_sta_link->sta_id], NULL); RCU_INIT_POINTER(mvm_sta->link[link_id], NULL); @@ -543,7 +544,7 @@ static void iwl_mvm_mld_sta_rm_all_sta_links(struct iwl_mvm *mvm, if (!link) continue; - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, link, link_id, false); + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, link, link_id); } } @@ -559,7 +560,10 @@ static int iwl_mvm_mld_alloc_sta_link(struct iwl_mvm *mvm, u32 sta_id = iwl_mvm_find_free_sta_id(mvm, ieee80211_vif_type_p2p(vif)); - if (sta_id == IWL_MVM_INVALID_STA) + lockdep_assert_wiphy(mvm->hw->wiphy); + lockdep_assert_held(&mvm->mutex); + + if (sta_id == IWL_INVALID_STA) return -ENOSPC; if (rcu_access_pointer(sta->link[link_id]) == &sta->deflink) { @@ -612,10 +616,10 @@ static void iwl_mvm_mld_set_ap_sta_id(struct ieee80211_sta *sta, struct iwl_mvm_link_sta *sta_link) { if (!sta->tdls) { - WARN_ON(vif_link->ap_sta_id != IWL_MVM_INVALID_STA); + WARN_ON(vif_link->ap_sta_id != IWL_INVALID_STA); vif_link->ap_sta_id = sta_link->sta_id; } else { - WARN_ON(vif_link->ap_sta_id == IWL_MVM_INVALID_STA); + WARN_ON(vif_link->ap_sta_id == IWL_INVALID_STA); } } @@ -631,6 +635,9 @@ static int iwl_mvm_alloc_sta_after_restart(struct iwl_mvm *mvm, int ret = -EINVAL; int sta_id; + lockdep_assert_wiphy(mvm->hw->wiphy); + lockdep_assert_held(&mvm->mutex); + /* First add an empty station since allocating a queue requires * a valid station. Since we need a link_id to allocate a station, * pick up the first valid one. @@ -686,7 +693,7 @@ int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, spin_lock_init(&mvm_sta->lock); - ret = iwl_mvm_sta_init(mvm, vif, sta, IWL_MVM_INVALID_STA, + ret = iwl_mvm_sta_init(mvm, vif, sta, IWL_INVALID_STA, STATION_TYPE_PEER); } else { ret = iwl_mvm_alloc_sta_after_restart(mvm, vif, sta); @@ -835,18 +842,11 @@ int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_link_sta *mvm_link_sta = rcu_dereference_protected(mvm_sta->link[link_id], lockdep_is_held(&mvm->mutex)); - bool stay_in_fw; + iwl_mvm_sta_del(mvm, vif, sta, link_sta); - stay_in_fw = iwl_mvm_sta_del(mvm, vif, sta, link_sta, &ret); - if (ret) - break; + ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_link_sta->sta_id); - if (!stay_in_fw) - ret = iwl_mvm_mld_rm_sta_from_fw(mvm, - mvm_link_sta->sta_id); - - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, - link_id, stay_in_fw); + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, link_id); } kfree(mvm_sta->mpdu_counters); mvm_sta->mpdu_counters = NULL; @@ -858,9 +858,10 @@ int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id) { int ret; + lockdep_assert_wiphy(mvm->hw->wiphy); lockdep_assert_held(&mvm->mutex); - if (WARN_ON(sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON(sta_id == IWL_INVALID_STA)) return 0; ret = iwl_mvm_mld_rm_sta_from_fw(mvm, sta_id); @@ -1064,6 +1065,7 @@ int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, unsigned int link_id; int ret; + lockdep_assert_wiphy(mvm->hw->wiphy); lockdep_assert_held(&mvm->mutex); for_each_set_bit(link_id, &old_links_long, @@ -1109,10 +1111,9 @@ int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, goto err; if (vif->type == NL80211_IFTYPE_STATION) - mvm_vif_link->ap_sta_id = IWL_MVM_INVALID_STA; + mvm_vif_link->ap_sta_id = IWL_INVALID_STA; - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id, - false); + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id); } for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { @@ -1182,6 +1183,9 @@ int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, link_sta_added_to_fw |= BIT(link_id); iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link); + + iwl_mvm_rs_rate_init(mvm, vif, sta, link_conf, link_sta, + link_conf->chanreq.oper.chan->band); } if (sta_mask_added) { @@ -1213,8 +1217,7 @@ err: rcu_dereference_protected(mvm_sta->link[link_id], lockdep_is_held(&mvm->mutex)); - iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id, - false); + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id); } return ret; diff --git a/mvm/mvm.h b/mvm/mvm.h index 22f48b66d79c..ee769da72e68 100644 --- a/mvm/mvm.h +++ b/mvm/mvm.h @@ -103,6 +103,7 @@ struct iwl_mvm_phy_ctxt { u32 center_freq1; bool rlc_disabled; u32 channel_load_by_us; + u32 channel_load_not_by_us; }; struct iwl_mvm_time_event_data { @@ -299,6 +300,7 @@ struct iwl_probe_resp_data { * @active: indicates the link is active in FW (for sanity checking) * @cab_queue: content-after-beacon (multicast) queue * @listen_lmac: indicates this link is allocated to the listen LMAC + * @csa_block_tx: we got CSA with mode=1 * @mcast_sta: multicast station * @phy_ctxt: phy context allocated to this link, if any * @bf_data: beacon filtering data @@ -324,6 +326,7 @@ struct iwl_mvm_vif_link_info { bool he_ru_2mhz_block; bool active; bool listen_lmac; + bool csa_block_tx; u16 cab_queue; /* Assigned while mac80211 has the link in a channel context, @@ -359,6 +362,9 @@ struct iwl_mvm_vif_link_info { * @IWL_MVM_ESR_BLOCKED_NON_BSS: An active non-BSS interface's link is * preventing EMLSR * @IWL_MVM_ESR_BLOCKED_ROC: remain-on-channel is preventing EMLSR + * @IWL_MVM_ESR_BLOCKED_TMP_NON_BSS: An expected active non-BSS interface's link + * is preventing EMLSR. This is a temporary blocking that is set when there + * is an indication that a non-BSS interface is to be added. * @IWL_MVM_ESR_EXIT_MISSED_BEACON: exited EMLSR due to missed beacons * @IWL_MVM_ESR_EXIT_LOW_RSSI: link is deactivated/not allowed for EMLSR * due to low RSSI. @@ -368,6 +374,7 @@ struct iwl_mvm_vif_link_info { * preventing the enablement of EMLSR * @IWL_MVM_ESR_EXIT_CSA: CSA happened, so exit EMLSR * @IWL_MVM_ESR_EXIT_LINK_USAGE: Exit EMLSR due to low tpt on secondary link + * @IWL_MVM_ESR_EXIT_FAIL_ENTRY: Exit EMLSR due to entry failure */ enum iwl_mvm_esr_state { IWL_MVM_ESR_BLOCKED_PREVENTION = 0x1, @@ -376,12 +383,14 @@ enum iwl_mvm_esr_state { IWL_MVM_ESR_BLOCKED_FW = 0x8, IWL_MVM_ESR_BLOCKED_NON_BSS = 0x10, IWL_MVM_ESR_BLOCKED_ROC = 0x20, + IWL_MVM_ESR_BLOCKED_TMP_NON_BSS = 0x40, IWL_MVM_ESR_EXIT_MISSED_BEACON = 0x10000, IWL_MVM_ESR_EXIT_LOW_RSSI = 0x20000, IWL_MVM_ESR_EXIT_COEX = 0x40000, IWL_MVM_ESR_EXIT_BANDWIDTH = 0x80000, IWL_MVM_ESR_EXIT_CSA = 0x100000, IWL_MVM_ESR_EXIT_LINK_USAGE = 0x200000, + IWL_MVM_ESR_EXIT_FAIL_ENTRY = 0x400000, }; #define IWL_MVM_BLOCK_ESR_REASONS 0xffff @@ -448,6 +457,8 @@ struct iwl_mvm_esr_exit { * @prevent_esr_done_wk: work that should be done when esr prevention ends. * @mlo_int_scan_wk: work for the internal MLO scan. * @unblock_esr_tpt_wk: work for unblocking EMLSR when tpt is high enough. + * @unblock_esr_tmp_non_bss_wk: work for removing the + * IWL_MVM_ESR_BLOCKED_TMP_NON_BSS blocking for EMLSR. * @roc_activity: currently running ROC activity for this vif (or * ROC_NUM_ACTIVITIES if no activity is running). * @session_prot_connection_loss: the connection was lost due to session @@ -508,7 +519,7 @@ struct iwl_mvm_vif { bool bf_enabled; bool ba_enabled; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* WoWLAN GTK rekey data */ struct { u8 kck[NL80211_KCK_EXT_LEN]; @@ -584,6 +595,7 @@ struct iwl_mvm_vif { struct wiphy_delayed_work prevent_esr_done_wk; struct wiphy_delayed_work mlo_int_scan_wk; struct wiphy_work unblock_esr_tpt_wk; + struct wiphy_delayed_work unblock_esr_tmp_non_bss_wk; struct iwl_mvm_vif_link_info deflink; struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -769,8 +781,6 @@ struct iwl_mvm_tcm { * @head_sn: reorder window head sn * @num_stored: number of mpdus stored in the buffer * @queue: queue of this reorder buffer - * @last_amsdu: track last ASMDU SN for duplication detection - * @last_sub_index: track ASMDU sub frame index for duplication detection * @valid: reordering is valid for this queue * @lock: protect reorder buffer internal state */ @@ -778,8 +788,6 @@ struct iwl_mvm_reorder_buffer { u16 head_sn; u16 num_stored; int queue; - u16 last_amsdu; - u8 last_sub_index; bool valid; spinlock_t lock; } ____cacheline_aligned_in_smp; @@ -1074,8 +1082,9 @@ struct iwl_mvm { /* data related to data path */ struct iwl_rx_phy_info last_phy_info; - struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT_MAX]; - struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_MVM_STATION_COUNT_MAX]; + struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_STATION_COUNT_MAX]; + /* note: fw_id_to_link_sta must be protected by wiphy and mvm mutexes */ + struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX]; u8 rx_ba_sessions; /* configured by mac80211 */ @@ -1164,10 +1173,8 @@ struct iwl_mvm { struct ieee80211_vif __rcu *vif_id_to_mac[NUM_MAC_INDEX_DRIVER]; - struct ieee80211_bss_conf __rcu *link_id_to_link_conf[IWL_MVM_FW_MAX_LINK_ID + 1]; + struct ieee80211_bss_conf __rcu *link_id_to_link_conf[IWL_FW_MAX_LINK_ID + 1]; - /* -1 for always, 0 for never, >0 for that many times */ - s8 fw_restart; u8 *error_recovery_buf; #ifdef CONFIG_IWLWIFI_LEDS @@ -1176,7 +1183,7 @@ struct iwl_mvm { struct ieee80211_vif *p2p_device_vif; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP struct wiphy_wowlan_support wowlan; int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; @@ -1200,8 +1207,11 @@ struct iwl_mvm { wait_queue_head_t rx_sync_waitq; - /* BT-Coex */ - struct iwl_bt_coex_profile_notif last_bt_notif; + /* BT-Coex - only one of those will be used */ + union { + struct iwl_bt_coex_prof_old_notif last_bt_notif; + struct iwl_bt_coex_profile_notif last_bt_wifi_loss; + }; struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; u8 bt_tx_prio; @@ -1365,6 +1375,7 @@ struct iwl_mvm { struct iwl_mvm_acs_survey *acs_survey; bool statistics_clear; + u32 bios_enable_puncturing; }; /* Extract MVM priv from op_mode and _hw */ @@ -1389,8 +1400,6 @@ DEFINE_GUARD(mvm, struct iwl_mvm *, mutex_lock(&_T->mutex), mutex_unlock(&_T->mu * @IWL_MVM_STATUS_IN_D3: in D3 (or at least about to go into it) * @IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE: suppress one error log * if this is set, when intentionally triggered - * @IWL_MVM_STATUS_STARTING: starting mac, - * used to disable restart flow while in STARTING state */ enum iwl_mvm_status { IWL_MVM_STATUS_HW_RFKILL, @@ -1402,7 +1411,6 @@ enum iwl_mvm_status { IWL_MVM_STATUS_FIRMWARE_RUNNING, IWL_MVM_STATUS_IN_D3, IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, - IWL_MVM_STATUS_STARTING, }; struct iwl_mvm_csme_conn_info { @@ -1578,8 +1586,7 @@ static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) { return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CSUM_SUPPORT) && - !IWL_MVM_HW_CSUM_DISABLE; + IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); } static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) @@ -1700,9 +1707,9 @@ static inline struct agg_tx_status * iwl_mvm_get_agg_status(struct iwl_mvm *mvm, void *tx_resp) { if (iwl_mvm_has_new_tx_api(mvm)) - return &((struct iwl_mvm_tx_resp *)tx_resp)->status; + return &((struct iwl_tx_resp *)tx_resp)->status; else - return ((struct iwl_mvm_tx_resp_v3 *)tx_resp)->status; + return ((struct iwl_tx_resp_v3 *)tx_resp)->status; } static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) @@ -1725,12 +1732,19 @@ static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_esr_supported(struct iwl_trans *trans) { - if ((CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM) && - !CSR_HW_RFID_IS_CDB(trans->hw_rf_id)) + if (CSR_HW_RFID_IS_CDB(trans->hw_rf_id)) + return false; + + switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + case IWL_CFG_RF_TYPE_FM: /* Step A doesn't support eSR */ return CSR_HW_RFID_STEP(trans->hw_rf_id); - - return false; + case IWL_CFG_RF_TYPE_WH: + case IWL_CFG_RF_TYPE_PE: + return true; + default: + return false; + } } static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm, @@ -1745,7 +1759,7 @@ static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm, if (iwl_mvm_is_esr_supported(trans) || (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM && CSR_HW_RFID_IS_CDB(trans->hw_rf_id))) - return IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM; + return IWL_FW_MAX_ACTIVE_LINKS_NUM; return 1; } @@ -1764,6 +1778,13 @@ static inline u8 iwl_mvm_mac_ac_to_tx_fifo(struct iwl_mvm *mvm, return iwl_mvm_ac_to_tx_fifo[ac]; } +static inline bool iwl_mvm_has_rlc_offload(struct iwl_mvm *mvm) +{ + return iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), + 0) >= 3; +} + struct iwl_rate_info { u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ @@ -1806,7 +1827,6 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type, u32 *gp2, u64 *boottime, ktime_t *realtime); u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); -u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size); /* Tx / Host Commands */ int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, @@ -2003,7 +2023,7 @@ int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf, + struct iwl_mvm_vif_link_info *link_info, __le32 *cck_rates, __le32 *ofdm_rates); void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -2062,6 +2082,8 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_missed_beacons_notif_legacy(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm, @@ -2085,6 +2107,7 @@ int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); u32 iwl_mvm_get_lmac_id(struct iwl_mvm *mvm, enum nl80211_band band); /* Links */ +void iwl_mvm_init_link(struct iwl_mvm_vif_link_info *link); int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -2291,7 +2314,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); extern const struct file_operations iwl_dbgfs_d3_test_ops; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_fast_suspend(struct iwl_mvm *mvm); @@ -2312,7 +2335,7 @@ static inline int iwl_mvm_fast_resume(struct iwl_mvm *mvm) } #endif void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd); + struct iwl_wowlan_config_cmd_v6 *cmd); int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool disable_offloading, @@ -2322,6 +2345,8 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, /* BT Coex */ int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm); +void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -2568,10 +2593,8 @@ void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed); -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q); + struct ieee80211_vif *vif); void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, const char *errmsg); void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, @@ -2898,7 +2921,7 @@ iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, bool more_data); int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value); void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u32 changed); + struct ieee80211_link_sta *link_sta, u32 changed); void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_prep_tx_info *info); @@ -2966,25 +2989,19 @@ void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, bool iwl_mvm_have_links_same_channel(struct iwl_mvm_vif *vif1, struct iwl_mvm_vif *vif2); bool iwl_mvm_vif_is_active(struct iwl_mvm_vif *mvmvif); -int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, + struct ieee80211_bss_conf *bss_conf, s16 tx_power); int iwl_mvm_set_hw_timestamp(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_set_hw_timestamp *hwts); int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, struct ieee80211_vif *vif); bool iwl_mvm_enable_fils(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx); -bool iwl_mvm_is_ftm_responder_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx); - -static inline struct cfg80211_chan_def * -iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx) -{ - bool use_def = iwl_mvm_is_ftm_responder_chanctx(mvm, ctx) || - iwl_mvm_enable_fils(mvm, ctx); - return use_def ? &ctx->def : &ctx->min_def; -} +struct cfg80211_chan_def * +iwl_mvm_chanctx_def(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx); void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif, u32 duration_ms, diff --git a/mvm/nvm.c b/mvm/nvm.c index 836ca22597bc..80ec59c58ae4 100644 --- a/mvm/nvm.c +++ b/mvm/nvm.c @@ -611,6 +611,7 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, char mcc[3]; struct ieee80211_regdomain *regd; int wgds_tbl_idx; + bool changed = false; lockdep_assert_held(&mvm->mutex); @@ -630,10 +631,15 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, IWL_DEBUG_LAR(mvm, "RX: received chub update mcc cmd (mcc '%s' src %d)\n", mcc, src); - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, &changed); if (IS_ERR_OR_NULL(regd)) return; + if (!changed) { + IWL_DEBUG_LAR(mvm, "RX: No change in the regulatory data\n"); + goto out; + } + wgds_tbl_idx = iwl_mvm_get_sar_geo_profile(mvm); if (wgds_tbl_idx < 1) IWL_DEBUG_INFO(mvm, @@ -644,5 +650,7 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, wgds_tbl_idx); regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + +out: kfree(regd); } diff --git a/mvm/offloading.c b/mvm/offloading.c index 1eb21fe861e5..15d4369678a2 100644 --- a/mvm/offloading.c +++ b/mvm/offloading.c @@ -10,7 +10,7 @@ #include "mvm.h" void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd) + struct iwl_wowlan_config_cmd_v6 *cmd) { int i; diff --git a/mvm/ops.c b/mvm/ops.c index b9daaffd9c7f..984f407f7027 100644 --- a/mvm/ops.c +++ b/mvm/ops.c @@ -34,7 +34,7 @@ #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(IWLWIFI); +MODULE_IMPORT_NS("IWLWIFI"); static const struct iwl_op_mode_ops iwl_mvm_ops; static const struct iwl_op_mode_ops iwl_mvm_ops_mq; @@ -159,6 +159,43 @@ static void iwl_mvm_rx_esr_mode_notif(struct iwl_mvm *mvm, iwl_mvm_get_primary_link(vif)); } +static void iwl_mvm_rx_esr_trans_fail_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_esr_trans_fail_notif *notif = (void *)pkt->data; + struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm); + u8 fw_link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *bss_conf; + + if (IS_ERR_OR_NULL(vif)) + return; + + IWL_DEBUG_INFO(mvm, "Failed to %s eSR on link %d, reason %d\n", + le32_to_cpu(notif->activation) ? "enter" : "exit", + le32_to_cpu(notif->link_id), + le32_to_cpu(notif->err_code)); + + /* we couldn't go back to single link, disconnect */ + if (!le32_to_cpu(notif->activation)) { + iwl_mvm_connection_loss(mvm, vif, "emlsr exit failed"); + return; + } + + bss_conf = iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, fw_link_id, false); + if (IWL_FW_CHECK(mvm, !bss_conf, + "FW reported failure to activate EMLSR on a non-existing link: %d\n", + fw_link_id)) + return; + + /* + * We failed to activate the second link and enter EMLSR, we need to go + * back to single link. + */ + iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_FAIL_ENTRY, + bss_conf->link_id); +} + static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { @@ -171,7 +208,8 @@ static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm, if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA)) return; - vif = iwl_mvm_get_vif_by_macid(mvm, notif->mac_id); + /* FIXME: should fetch the link and not the vif */ + vif = iwl_mvm_get_vif_by_macid(mvm, notif->link_id); if (!vif || vif->type != NL80211_IFTYPE_STATION) return; @@ -261,6 +299,12 @@ static void iwl_mvm_rx_thermal_dual_chain_req(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_thermal_dual_chain_request *req = (void *)pkt->data; + /* firmware is expected to handle that in RLC offload mode */ + if (IWL_FW_CHECK(mvm, iwl_mvm_has_rlc_offload(mvm), + "Got THERMAL_DUAL_CHAIN_REQUEST (0x%x) in RLC offload mode\n", + req->event)) + return; + /* * We could pass it to the iterator data, but also need to remember * it for new interfaces that are added while in this state. @@ -325,7 +369,7 @@ struct iwl_rx_handlers { */ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, RX_HANDLER_SYNC, - struct iwl_mvm_tx_resp), + struct iwl_tx_resp), RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, RX_HANDLER_SYNC, struct iwl_mvm_ba_notif), @@ -333,9 +377,12 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { iwl_mvm_tlc_update_notif, RX_HANDLER_SYNC, struct iwl_tlc_update_notif), - RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_old_notif, RX_HANDLER_ASYNC_LOCKED_WIPHY, - struct iwl_bt_coex_profile_notif), + struct iwl_bt_coex_prof_old_notif), + RX_HANDLER_GRP(BT_COEX_GROUP, PROFILE_NOTIF, iwl_mvm_rx_bt_coex_notif, + RX_HANDLER_ASYNC_LOCKED_WIPHY, + struct iwl_bt_coex_profile_notif), RX_HANDLER_NO_SIZE(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, RX_HANDLER_ASYNC_LOCKED), RX_HANDLER_NO_SIZE(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, @@ -362,7 +409,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_SYNC, struct iwl_time_event_notif), RX_HANDLER_GRP(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF, iwl_mvm_rx_session_protect_notif, RX_HANDLER_SYNC, - struct iwl_mvm_session_prot_notif), + struct iwl_session_prot_notif), RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, RX_HANDLER_ASYNC_LOCKED, struct iwl_mcc_chub_notif), @@ -385,10 +432,15 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC, struct iwl_umac_scan_iter_complete_notif), - RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, + RX_HANDLER(MISSED_BEACONS_NOTIFICATION, + iwl_mvm_rx_missed_beacons_notif_legacy, RX_HANDLER_ASYNC_LOCKED_WIPHY, - struct iwl_missed_beacons_notif), + struct iwl_missed_beacons_notif_v4), + RX_HANDLER_GRP(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF, + iwl_mvm_rx_missed_beacons_notif, + RX_HANDLER_ASYNC_LOCKED_WIPHY, + struct iwl_missed_beacons_notif), RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, RX_HANDLER_SYNC, struct iwl_error_resp), RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, @@ -472,6 +524,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(SCAN_GROUP, CHANNEL_SURVEY_NOTIF, iwl_mvm_rx_channel_survey_notif, RX_HANDLER_ASYNC_LOCKED, struct iwl_umac_scan_channel_survey_notif), + RX_HANDLER_GRP(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF, + iwl_mvm_rx_esr_trans_fail_notif, + RX_HANDLER_ASYNC_LOCKED_WIPHY, + struct iwl_esr_trans_fail_notif), }; #undef RX_HANDLER #undef RX_HANDLER_GRP @@ -602,6 +658,7 @@ static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(STA_REMOVE_CMD), HCMD_NAME(STA_DISABLE_TX_CMD), HCMD_NAME(ROC_CMD), + HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF), HCMD_NAME(ROC_NOTIF), HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF), HCMD_NAME(MISSED_VAP_NOTIF), @@ -713,6 +770,13 @@ static const struct iwl_hcmd_names iwl_mvm_regulatory_and_nvm_names[] = { HCMD_NAME(TAS_CONFIG), }; +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_bt_coex_names[] = { + HCMD_NAME(PROFILE_NOTIF), +}; + static const struct iwl_hcmd_arr iwl_mvm_groups[] = { [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), @@ -722,6 +786,7 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = { [DATA_PATH_GROUP] = HCMD_ARR(iwl_mvm_data_path_names), [SCAN_GROUP] = HCMD_ARR(iwl_mvm_scan_names), [LOCATION_GROUP] = HCMD_ARR(iwl_mvm_location_names), + [BT_COEX_GROUP] = HCMD_ARR(iwl_mvm_bt_coex_names), [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names), [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mvm_regulatory_and_nvm_names), @@ -1221,14 +1286,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, size_t scan_size; u32 min_backoff; struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused; + int err; /* - * We use IWL_MVM_STATION_COUNT_MAX to check the validity of the station + * We use IWL_STATION_COUNT_MAX 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_MAX); + IWL_STATION_COUNT_MAX); /******************************** * 1. Allocating and configuring HW data @@ -1238,7 +1304,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_has_mld_api(fw) ? &iwl_mvm_mld_hw_ops : &iwl_mvm_hw_ops); if (!hw) - return NULL; + return ERR_PTR(-ENOMEM); if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) max_agg = 512; @@ -1267,6 +1333,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_get_bios_tables(mvm); iwl_uefi_get_sgom_table(trans, &mvm->fwrt); iwl_uefi_get_step_table(trans); + iwl_bios_setup_step(trans, &mvm->fwrt); mvm->init_status = 0; @@ -1282,11 +1349,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - if (WARN_ON(trans->num_rx_queues > 1)) + if (WARN_ON(trans->num_rx_queues > 1)) { + err = -EINVAL; goto out_free; + } } - mvm->fw_restart = iwlwifi_mod_params.fw_restart ? -1 : 0; + mvm->bios_enable_puncturing = iwl_uefi_get_puncturing(&mvm->fwrt); if (iwl_mvm_has_new_tx_api(mvm)) { /* @@ -1359,8 +1428,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_fw_lookup_notif_ver(mvm->fw, LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF, 5); /* we only support up to version 9 */ - if (WARN_ON_ONCE(mvm->cmd_ver.range_resp > 9)) + if (WARN_ON_ONCE(mvm->cmd_ver.range_resp > 9)) { + err = -EINVAL; goto out_free; + } /* * Populate the state variables that the transport layer needs @@ -1386,10 +1457,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info, driver_data[2]); - /* Set a short watchdog for the command queue */ - trans_cfg.cmd_q_wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, false, true); - snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%.31s", fw->fw_version); @@ -1413,9 +1480,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->dbg.dest_tlv = mvm->fw->dbg.dest_tlv; trans->dbg.n_dest_reg = mvm->fw->dbg.n_dest_reg; - memcpy(trans->dbg.conf_tlv, mvm->fw->dbg.conf_tlv, - sizeof(trans->dbg.conf_tlv)); - trans->dbg.trigger_tlv = mvm->fw->dbg.trigger_tlv; trans->iml = mvm->fw->iml; trans->iml_len = mvm->fw->iml_len; @@ -1427,6 +1491,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->phy_db = iwl_phy_db_init(trans); if (!mvm->phy_db) { IWL_ERR(mvm, "Cannot init phy_db\n"); + err = -ENOMEM; goto out_free; } @@ -1439,13 +1504,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, scan_size = iwl_mvm_scan_size(mvm); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); - if (!mvm->scan_cmd) + if (!mvm->scan_cmd) { + err = -ENOMEM; goto out_free; + } mvm->scan_cmd_size = scan_size; /* invalidate ids to prevent accidental removal of sta_id 0 */ - mvm->aux_sta.sta_id = IWL_MVM_INVALID_STA; - mvm->snif_sta.sta_id = IWL_MVM_INVALID_STA; + mvm->aux_sta.sta_id = IWL_INVALID_STA; + mvm->snif_sta.sta_id = IWL_INVALID_STA; /* Set EBS as successful as long as not stated otherwise by the FW. */ mvm->last_ebs_successful = true; @@ -1469,7 +1536,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_mei_scan_filter_init(&mvm->mei_scan_filter); - if (iwl_mvm_start_get_nvm(mvm)) { + err = iwl_mvm_start_get_nvm(mvm); + if (err) { /* * Getting NVM failed while CSME is the owner, but we are * registered to MEI, we'll get the NVM later when it'll be @@ -1482,7 +1550,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, } - if (iwl_mvm_start_post_nvm(mvm)) + err = iwl_mvm_start_post_nvm(mvm); + if (err) goto out_thermal_exit; return op_mode; @@ -1502,7 +1571,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_trans_op_mode_leave(trans); ieee80211_free_hw(mvm->hw); - return NULL; + return ERR_PTR(err); } void iwl_mvm_stop_device(struct iwl_mvm *mvm) @@ -1937,27 +2006,62 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) ieee80211_free_txskb(mvm->hw, skb); } -struct iwl_mvm_reprobe { - struct device *dev; - struct work_struct work; -}; +static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); -static void iwl_mvm_reprobe_wk(struct work_struct *wk) + iwl_abort_notification_waits(&mvm->notif_wait); + iwl_dbg_tlv_del_timers(mvm->trans); + + if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL) + IWL_ERR(mvm, "Command queue full!\n"); + else if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) && + !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, + &mvm->status)) + iwl_mvm_dump_nic_error_log(mvm); + + /* + * This should be first thing before trying to collect any + * data to avoid endless loops if any HW error happens while + * collecting debug data. + * It might not actually be true that we'll restart, but the + * setting of the bit doesn't matter if we're going to be + * unbound either. + */ + if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) + set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); +} + +static void iwl_mvm_dump_error(struct iwl_op_mode *op_mode, + struct iwl_fw_error_dump_mode *mode) { - struct iwl_mvm_reprobe *reprobe; - - reprobe = container_of(wk, struct iwl_mvm_reprobe, work); - if (device_reprobe(reprobe->dev)) - dev_err(reprobe->dev, "reprobe failed!\n"); - put_device(reprobe->dev); - kfree(reprobe); - module_put(THIS_MODULE); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + /* if we come in from opmode we have the mutex held */ + if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) { + lockdep_assert_held(&mvm->mutex); + iwl_fw_error_collect(&mvm->fwrt); + } else { + mutex_lock(&mvm->mutex); + if (mode->context != IWL_ERR_CONTEXT_ABORT) + iwl_fw_error_collect(&mvm->fwrt); + mutex_unlock(&mvm->mutex); + } } -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) +static bool iwl_mvm_sw_reset(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) { - iwl_abort_notification_waits(&mvm->notif_wait); - iwl_dbg_tlv_del_timers(mvm->trans); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + /* + * If the firmware crashes while we're already considering it + * to be dead then don't ask for a restart, that cannot do + * anything useful anyway. + */ + if (!test_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status)) + return false; /* * This is a bit racy, but worst case we tell mac80211 about @@ -1972,52 +2076,11 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) iwl_mvm_report_scan_aborted(mvm); /* - * If we're restarting already, don't cycle restarts. * If INIT fw asserted, it will likely fail again. * If WoWLAN fw asserted, don't restart either, mac80211 * can't recover this since we're already half suspended. */ - if (!mvm->fw_restart && fw_error) { - iwl_fw_error_collect(&mvm->fwrt, false); - } else if (test_bit(IWL_MVM_STATUS_STARTING, - &mvm->status)) { - IWL_ERR(mvm, "Starting mac, retry will be triggered anyway\n"); - } else if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - struct iwl_mvm_reprobe *reprobe; - - IWL_ERR(mvm, - "Firmware error during reconfiguration - reprobe!\n"); - - /* - * get a module reference to avoid doing this while unloading - * anyway and to avoid scheduling a work with code that's - * being removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(mvm, "Module is being unloaded - abort\n"); - return; - } - - reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC); - if (!reprobe) { - module_put(THIS_MODULE); - return; - } - reprobe->dev = get_device(mvm->trans->dev); - INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); - schedule_work(&reprobe->work); - } else if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, - &mvm->status)) { - IWL_ERR(mvm, "HW restart already requested, but not started\n"); - } else if (mvm->fwrt.cur_fw_img == IWL_UCODE_REGULAR && - mvm->hw_registered && - !test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { - /* This should be first thing before trying to collect any - * data to avoid endless loops if any HW error happens while - * collecting debug data. - */ - set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); - + if (mvm->fwrt.cur_fw_img == IWL_UCODE_REGULAR && mvm->hw_registered) { if (mvm->fw->ucode_capa.error_log_size) { u32 src_size = mvm->fw->ucode_capa.error_log_size; u32 src_addr = mvm->fw->ucode_capa.error_log_addr; @@ -2032,57 +2095,18 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) } } - iwl_fw_error_collect(&mvm->fwrt, false); - - if (fw_error && mvm->fw_restart > 0) { - mvm->fw_restart--; - ieee80211_restart_hw(mvm->hw); - } else if (mvm->fwrt.trans->dbg.restart_required) { + if (mvm->fwrt.trans->dbg.restart_required) { IWL_DEBUG_INFO(mvm, "FW restart requested after debug collection\n"); mvm->fwrt.trans->dbg.restart_required = false; ieee80211_restart_hw(mvm->hw); + return true; } else if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) { ieee80211_restart_hw(mvm->hw); + return true; } } -} - -static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) && - !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, - &mvm->status)) - iwl_mvm_dump_nic_error_log(mvm); - - if (sync) { - iwl_fw_error_collect(&mvm->fwrt, true); - /* - * Currently, the only case for sync=true is during - * shutdown, so just stop in this case. If/when that - * changes, we need to be a bit smarter here. - */ - return; - } - /* - * If the firmware crashes while we're already considering it - * to be dead then don't ask for a restart, that cannot do - * anything useful anyway. - */ - if (!test_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status)) - return; - - iwl_mvm_nic_restart(mvm, false); -} - -static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - WARN_ON(1); - iwl_mvm_nic_restart(mvm, true); + return false; } static void iwl_op_mode_mvm_time_point(struct iwl_op_mode *op_mode, @@ -2094,6 +2118,7 @@ static void iwl_op_mode_mvm_time_point(struct iwl_op_mode *op_mode, iwl_dbg_tlv_time_point(&mvm->fwrt, tp_id, tp_data); } +#ifdef CONFIG_PM_SLEEP static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -2102,11 +2127,13 @@ static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; iwl_mvm_stop_device(mvm); -#ifdef CONFIG_PM mvm->fast_resume = false; -#endif mutex_unlock(&mvm->mutex); } +#else +static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) +{} +#endif #define IWL_MVM_COMMON_OPS \ /* these could be differentiated */ \ @@ -2115,7 +2142,8 @@ static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ .free_skb = iwl_mvm_free_skb, \ .nic_error = iwl_mvm_nic_error, \ - .cmd_queue_full = iwl_mvm_cmd_queue_full, \ + .dump_error = iwl_mvm_dump_error, \ + .sw_reset = iwl_mvm_sw_reset, \ .nic_config = iwl_mvm_nic_config, \ /* as we only register one, these MUST be common! */ \ .start = iwl_op_mode_mvm_start, \ diff --git a/mvm/phy-ctxt.c b/mvm/phy-ctxt.c index ce264b386029..5e7e2926be0c 100644 --- a/mvm/phy-ctxt.c +++ b/mvm/phy-ctxt.c @@ -31,7 +31,7 @@ u8 iwl_mvm_get_channel_width(const struct cfg80211_chan_def *chandef) /* * Maps the driver specific control channel position (relative to the center - * freq) definitions to the the fw values + * freq) definitions to the fw values */ u8 iwl_mvm_get_ctrl_pos(const struct cfg80211_chan_def *chandef) { @@ -159,7 +159,11 @@ int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, .phy_id = cpu_to_le32(ctxt->id), }; - if (ctxt->rlc_disabled) + /* From version 3, RLC is offloaded to firmware, so the driver no + * longer needs to send cmd.rlc, note that we are not using any + * other fields in the command - don't send it. + */ + if (iwl_mvm_has_rlc_offload(mvm) || ctxt->rlc_disabled) return 0; if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, diff --git a/mvm/power.c b/mvm/power.c index bc363e8427e4..a386b315e52f 100644 --- a/mvm/power.c +++ b/mvm/power.c @@ -567,7 +567,7 @@ struct iwl_power_vifs { bool monitor_active; }; -static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, +static void iwl_mvm_power_disable_pm_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -575,7 +575,7 @@ static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, mvmvif->pm_enabled = false; } -static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, +static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); diff --git a/mvm/rs-fw.c b/mvm/rs-fw.c index 05715e5af6ab..de5ac000272e 100644 --- a/mvm/rs-fw.c +++ b/mvm/rs-fw.c @@ -440,12 +440,6 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (!mvmsta) { - IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", - notif->sta_id); - goto out; - } - flags = le32_to_cpu(notif->flags); mvm_link_sta = rcu_dereference(mvmsta->link[link_sta->link_id]); @@ -615,11 +609,8 @@ void iwl_mvm_rs_fw_rate_init(struct iwl_mvm *mvm, int cmd_ver; int ret; - /* Enable external EHT LTF only for GL device and if there's - * mutual support by AP and client - */ - if (CSR_HW_REV_TYPE(mvm->trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && - sband_eht_cap && + /* Enable extra EHT LTF if there's mutual support by AP and client */ + if (sband_eht_cap && sband_eht_cap->eht_cap_elem.phy_cap_info[5] & IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && link_sta->eht_cap.has_eht && @@ -4,7 +4,7 @@ * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include "iwl-trans.h" @@ -560,7 +560,8 @@ static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig, struct iwl_mvm_vif_link_info *link_info, struct ieee80211_bss_conf *bss_conf) { - struct iwl_mvm *mvm = iwl_mvm_vif_from_mac80211(vif)->mvm; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; int thold = bss_conf->cqm_rssi_thold; int hyst = bss_conf->cqm_rssi_hyst; int last_event; @@ -625,6 +626,13 @@ static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig, if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) return; + /* We're not in EMLSR and our signal is bad, try to switch link maybe */ + if (sig < IWL_MVM_LOW_RSSI_MLO_SCAN_THRESH && !mvmvif->esr_active) { + iwl_mvm_int_mlo_scan(mvm, vif); + return; + } + + /* We are in EMLSR, check if we need to exit */ exit_esr_thresh = iwl_mvm_get_esr_rssi_thresh(mvm, &bss_conf->chanreq.oper, @@ -738,8 +746,8 @@ static void iwl_mvm_stats_energy_iter(void *_data, u8 *energy = _data; u32 sta_id = mvmsta->deflink.sta_id; - if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT_MAX, "sta_id %d >= %d", - sta_id, IWL_MVM_STATION_COUNT_MAX)) + if (WARN_ONCE(sta_id >= IWL_STATION_COUNT_MAX, "sta_id %d >= %d", + sta_id, IWL_STATION_COUNT_MAX)) return; if (energy[sta_id]) @@ -781,6 +789,8 @@ static void iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm, continue; mvm->phy_ctxts[i].channel_load_by_us = le32_to_cpu(per_phy[i].channel_load_by_us); + mvm->phy_ctxts[i].channel_load_not_by_us = + le32_to_cpu(per_phy[i].channel_load_not_by_us); } } @@ -954,6 +964,9 @@ iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm, #define SEC_LINK_MIN_TX 3000 #define SEC_LINK_MIN_RX 400 +/* Accept a ~20% short window to avoid issues due to jitter */ +#define IWL_MVM_TPT_MIN_COUNT_WINDOW (IWL_MVM_TPT_COUNT_WINDOW_SEC * HZ * 4 / 5) + static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) { struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm); @@ -963,6 +976,7 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) unsigned long sec_link_tx = 0, sec_link_rx = 0; u8 sec_link_tx_perc, sec_link_rx_perc; u8 sec_link; + bool skip = false; lockdep_assert_held(&mvm->mutex); @@ -991,7 +1005,7 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) spin_lock_bh(&mvmsta->mpdu_counters[q].lock); /* The link IDs that doesn't exist will contain 0 */ - for (int link = 0; link < IWL_MVM_FW_MAX_LINK_ID; link++) { + for (int link = 0; link < IWL_FW_MAX_LINK_ID; link++) { total_tx += mvmsta->mpdu_counters[q].per_link[link].tx; total_rx += mvmsta->mpdu_counters[q].per_link[link].rx; } @@ -1002,15 +1016,27 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) /* * In EMLSR we have statistics every 5 seconds, so we can reset * the counters upon every statistics notification. + * The FW sends the notification regularly, but it will be + * misaligned at the start. Skipping the measurement if it is + * short will synchronize us. */ + if (jiffies - mvmsta->mpdu_counters[q].window_start < + IWL_MVM_TPT_MIN_COUNT_WINDOW) + skip = true; + mvmsta->mpdu_counters[q].window_start = jiffies; memset(mvmsta->mpdu_counters[q].per_link, 0, sizeof(mvmsta->mpdu_counters[q].per_link)); spin_unlock_bh(&mvmsta->mpdu_counters[q].lock); } - IWL_DEBUG_STATS(mvm, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n", - total_tx, total_rx); + if (skip) { + IWL_DEBUG_INFO(mvm, "MPDU statistics window was short\n"); + return; + } + + IWL_DEBUG_INFO(mvm, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n", + total_tx, total_rx); /* If we don't have enough MPDUs - exit EMLSR */ if (total_tx < IWL_MVM_ENTER_ESR_TPT_THRESH && @@ -1020,6 +1046,9 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) return; } + IWL_DEBUG_INFO(mvm, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n", + sec_link, sec_link_tx, sec_link_rx); + /* Calculate the percentage of the secondary link TX/RX */ sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0; sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0; @@ -1039,7 +1068,7 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { - u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; + u8 average_energy[IWL_STATION_COUNT_MAX]; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_system_statistics_notif_oper *stats; int i; @@ -1098,7 +1127,7 @@ static void iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; + u8 average_energy[IWL_STATION_COUNT_MAX]; __le32 air_time[MAC_INDEX_AUX]; __le32 rx_bytes[MAC_INDEX_AUX]; __le32 flags = 0; diff --git a/mvm/rxmq.c b/mvm/rxmq.c index 1a210d0c22b3..14ea89f931bb 100644 --- a/mvm/rxmq.c +++ b/mvm/rxmq.c @@ -636,15 +636,21 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", baid, nssn); - if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || - baid >= ARRAY_SIZE(mvm->baid_map))) + if (IWL_FW_CHECK(mvm, + baid == IWL_RX_REORDER_DATA_INVALID_BAID || + baid >= ARRAY_SIZE(mvm->baid_map), + "invalid BAID from FW: %d\n", baid)) return; rcu_read_lock(); ba_data = rcu_dereference(mvm->baid_map[baid]); - if (WARN(!ba_data, "BAID %d not found in map\n", baid)) + if (!ba_data) { + IWL_DEBUG_RX(mvm, + "Got valid BAID %d but not allocated, invalid frame release!\n", + baid); goto out; + } /* pick any STA ID to find the pointer */ sta_id = ffs(ba_data->sta_mask) - 1; @@ -729,8 +735,6 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, bool last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; u8 tid = ieee80211_get_tid(hdr); - u8 sub_frame_idx = desc->amsdu_info & - IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; struct iwl_mvm_reorder_buf_entry *entries; u32 sta_mask; int index; @@ -775,9 +779,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, return false; } - rcu_read_lock(); sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1); - rcu_read_unlock(); if (IWL_FW_CHECK(mvm, tid != baid_data->tid || @@ -816,7 +818,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (!amsdu || last_subframe) buffer->head_sn = nssn; - /* No need to update AMSDU last SN - we are moving the head */ + spin_unlock_bh(&buffer->lock); return false; } @@ -833,7 +835,6 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, if (!amsdu || last_subframe) buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); - /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; } @@ -843,11 +844,6 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, __skb_queue_tail(&entries[index].frames, skb); buffer->num_stored++; - if (amsdu) { - buffer->last_amsdu = sn; - buffer->last_sub_index = sub_frame_idx; - } - /* * We cannot trust NSSN for AMSDU sub-frames that are not the last. * The reason is that NSSN advances on the first sub-frame, and may @@ -999,7 +995,7 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, */ u8 ru = le32_get_bits(phy_data->d1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK); u32 rate_n_flags = phy_data->rate_n_flags; - u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK_V1; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; u8 offs = 0; rx_status->bw = RATE_INFO_BW_HE_RU; @@ -1054,13 +1050,13 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, if (he_mu) he_mu->flags2 |= - le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1, + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags), IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); - else if (he_type == RATE_MCS_HE_TYPE_TRIG_V1) + else if (he_type == RATE_MCS_HE_TYPE_TRIG) he->data6 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) | - le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1, + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags), IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW); } @@ -2516,19 +2512,24 @@ void iwl_mvm_rx_bar_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_bar_frame_release *release = (void *)pkt->data; - unsigned int baid = le32_get_bits(release->ba_info, - IWL_BAR_FRAME_RELEASE_BAID_MASK); - unsigned int nssn = le32_get_bits(release->ba_info, - IWL_BAR_FRAME_RELEASE_NSSN_MASK); - unsigned int sta_id = le32_get_bits(release->sta_tid, - IWL_BAR_FRAME_RELEASE_STA_MASK); - unsigned int tid = le32_get_bits(release->sta_tid, - IWL_BAR_FRAME_RELEASE_TID_MASK); struct iwl_mvm_baid_data *baid_data; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + unsigned int baid, nssn, sta_id, tid; - if (unlikely(iwl_rx_packet_payload_len(pkt) < sizeof(*release))) + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*release), + "Unexpected frame release notif size %d (expected %zu)\n", + pkt_len, sizeof(*release))) return; + baid = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_BAID_MASK); + nssn = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_NSSN_MASK); + sta_id = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_STA_MASK); + tid = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_TID_MASK); + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || baid >= ARRAY_SIZE(mvm->baid_map))) return; @@ -2542,7 +2543,7 @@ void iwl_mvm_rx_bar_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, goto out; } - if (WARN(tid != baid_data->tid || sta_id > IWL_MVM_STATION_COUNT_MAX || + if (WARN(tid != baid_data->tid || sta_id > IWL_STATION_COUNT_MAX || !(baid_data->sta_mask & BIT(sta_id)), "baid 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n", baid, baid_data->sta_mask, baid_data->tid, sta_id, diff --git a/mvm/scan.c b/mvm/scan.c index 1cc9c426bb15..60bd9c7e5f03 100644 --- a/mvm/scan.c +++ b/mvm/scan.c @@ -462,7 +462,7 @@ static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) if (!ssid_list[i].len) break; if (ssid_list[i].len == ssid_len && - !memcmp(ssid_list->ssid, ssid, ssid_len)) + !memcmp(ssid_list[i].ssid, ssid, ssid_len)) return i; } return -1; @@ -1594,7 +1594,7 @@ iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, for (i = 0; i < n_channels; i++) { channel_cfg[i].flags = cpu_to_le32(flags); - channel_cfg[i].v1.channel_num = channels[i]->hw_value; + channel_cfg[i].channel_num = channels[i]->hw_value; if (iwl_mvm_is_scan_ext_chan_supported(mvm)) { enum nl80211_band band = channels[i]->band; @@ -1626,13 +1626,13 @@ iwl_mvm_umac_scan_cfg_channels_v4(struct iwl_mvm *mvm, &cp->channel_config[i]; cfg->flags = cpu_to_le32(flags); - cfg->v2.channel_num = channels[i]->hw_value; + cfg->channel_num = channels[i]->hw_value; cfg->v2.band = iwl_mvm_phy_band_from_nl80211(band); cfg->v2.iter_count = 1; cfg->v2.iter_interval = 0; iwl_mvm_scan_ch_add_n_aps_override(vif_type, - cfg->v2.channel_num, + cfg->channel_num, cfg->v2.band, bitmap, bitmap_n_entries); } @@ -1656,7 +1656,7 @@ iwl_mvm_umac_scan_cfg_channels_v7(struct iwl_mvm *mvm, u8 iwl_band = iwl_mvm_phy_band_from_nl80211(band); cfg->flags = cpu_to_le32(flags | n_aps_flag); - cfg->v2.channel_num = channels[i]->hw_value; + cfg->channel_num = channels[i]->hw_value; if (cfg80211_channel_is_psc(channels[i])) cfg->flags = 0; @@ -1774,7 +1774,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, &cp->channel_config[ch_cnt]; u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; - u8 j, k, n_s_ssids = 0, n_bssids = 0; + u8 k, n_s_ssids = 0, n_bssids = 0; u8 max_s_ssids, max_bssids; bool force_passive = false, found = false, allow_passive = true, unsolicited_probe_on_chan = false, psc_no_listen = false; @@ -1789,7 +1789,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, !params->n_6ghz_params && params->n_ssids) continue; - cfg->v1.channel_num = params->channels[i]->hw_value; + cfg->channel_num = params->channels[i]->hw_value; if (version < 17) cfg->v2.band = PHY_BAND_6; else @@ -1799,7 +1799,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, cfg->v5.iter_count = 1; cfg->v5.iter_interval = 0; - for (j = 0; j < params->n_6ghz_params; j++) { + for (u32 j = 0; j < params->n_6ghz_params; j++) { s8 tmp_psd_20; if (!(scan_6ghz_params[j].channel_idx == i)) @@ -1873,7 +1873,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, * SSID. * TODO: improve this logic */ - for (j = 0; j < params->n_6ghz_params; j++) { + for (u32 j = 0; j < params->n_6ghz_params; j++) { if (!(scan_6ghz_params[j].channel_idx == i)) continue; @@ -2477,7 +2477,7 @@ iwl_mvm_scan_umac_fill_ch_p_v7(struct iwl_mvm *mvm, if (!cfg80211_channel_is_psc(channel)) continue; - cfg->v5.channel_num = channel->hw_value; + cfg->channel_num = channel->hw_value; cfg->v5.iter_count = 1; cfg->v5.iter_interval = 0; @@ -3313,13 +3313,23 @@ void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, mvm->scan_start); } -static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) +static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type, bool *wait) { - struct iwl_umac_scan_abort cmd = {}; + struct iwl_umac_scan_abort abort_cmd = {}; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_ABORT_UMAC), + .len = { sizeof(abort_cmd), }, + .data = { &abort_cmd, }, + .flags = CMD_SEND_IN_RFKILL, + }; + int uid, ret; + u32 status = IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND; lockdep_assert_held(&mvm->mutex); + *wait = true; + /* We should always get a valid index here, because we already * checked that this type of scan was running in the generic * code. @@ -3328,17 +3338,28 @@ static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) if (WARN_ON_ONCE(uid < 0)) return uid; - cmd.uid = cpu_to_le32(uid); + abort_cmd.uid = cpu_to_le32(uid); IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); - ret = iwl_mvm_send_cmd_pdu(mvm, - WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_ABORT_UMAC), - CMD_SEND_IN_RFKILL, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); + + IWL_DEBUG_SCAN(mvm, "Scan abort: ret=%d, status=%u\n", ret, status); if (!ret) mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; - IWL_DEBUG_SCAN(mvm, "Scan abort: ret=%d\n", ret); + /* Handle the case that the FW is no longer familiar with the scan that + * is to be stopped. In such a case, it is expected that the scan + * complete notification was already received but not yet processed. + * In such a case, there is no need to wait for a scan complete + * notification and the flow should continue similar to the case that + * the scan was really aborted. + */ + if (status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND) { + mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; + *wait = false; + } + return ret; } @@ -3348,6 +3369,7 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC, SCAN_OFFLOAD_COMPLETE, }; int ret; + bool wait = true; lockdep_assert_held(&mvm->mutex); @@ -3359,7 +3381,7 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type); if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - ret = iwl_mvm_umac_scan_abort(mvm, type); + ret = iwl_mvm_umac_scan_abort(mvm, type, &wait); else ret = iwl_mvm_lmac_scan_abort(mvm); @@ -3367,6 +3389,10 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type); iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); return ret; + } else if (!wait) { + IWL_DEBUG_SCAN(mvm, "no need to wait for scan type %d\n", type); + iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); + return 0; } return iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, @@ -3451,7 +3477,7 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) * restart_hw, so do not report if FW is about to be * restarted. */ - if (!mvm->fw_restart) + if (!iwlwifi_mod_params.fw_restart) ieee80211_sched_scan_stopped(mvm->hw); mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; mvm->scan_uid_status[uid] = 0; @@ -3502,7 +3528,7 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) * restarted. */ if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && - !mvm->fw_restart) { + !iwlwifi_mod_params.fw_restart) { ieee80211_sched_scan_stopped(mvm->hw); mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; } @@ -3571,7 +3597,8 @@ static int iwl_mvm_int_mlo_scan_start(struct iwl_mvm *mvm, IWL_DEBUG_SCAN(mvm, "Starting Internal MLO scan: n_channels=%zu\n", n_channels); - if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) || + hweight16(vif->valid_links) == 1) return -EINVAL; size = struct_size(req, channels, n_channels); diff --git a/mvm/sta.c b/mvm/sta.c index 15e64d94d6ea..7a4844ec3c10 100644 --- a/mvm/sta.c +++ b/mvm/sta.c @@ -29,7 +29,7 @@ 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_MAX > 32); + BUILD_BUG_ON(IWL_STATION_COUNT_MAX > 32); WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); lockdep_assert_held(&mvm->mutex); @@ -47,7 +47,7 @@ int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, enum nl80211_iftype iftype) lockdep_is_held(&mvm->mutex))) return sta_id; } - return IWL_MVM_INVALID_STA; + return IWL_INVALID_STA; } /* Calculate the ampdu density and max size */ @@ -900,7 +900,7 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_tid(sta, tid); unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); + iwl_mvm_get_wd_timeout(mvm, mvmsta->vif); int queue = -1; lockdep_assert_held(&mvm->mutex); @@ -1080,7 +1080,7 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue) return; mvmsta = iwl_mvm_sta_from_mac80211(sta); - wdg_timeout = iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); + wdg_timeout = iwl_mvm_get_wd_timeout(mvm, mvmsta->vif); ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number); @@ -1216,7 +1216,7 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, * can be unshared and finding one (and only one) that can be * reused. * This function is also invoked as a sort of clean-up task, - * in which case @alloc_for_sta is IWL_MVM_INVALID_STA. + * in which case @alloc_for_sta is IWL_INVALID_STA. * * Returns the queue number, or -ENOSPC. */ @@ -1309,7 +1309,7 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta) rcu_read_unlock(); - if (free_queue >= 0 && alloc_for_sta != IWL_MVM_INVALID_STA) { + if (free_queue >= 0 && alloc_for_sta != IWL_INVALID_STA) { ret = iwl_mvm_free_inactive_queue(mvm, free_queue, queue_owner, alloc_for_sta); if (ret) @@ -1330,7 +1330,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, .frame_limit = IWL_FRAME_LIMIT, }; unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); + iwl_mvm_get_wd_timeout(mvm, mvmsta->vif); int queue = -1; u16 queue_tmp; unsigned long disable_agg_tids = 0; @@ -1520,9 +1520,14 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, add_stream_wk); - mutex_lock(&mvm->mutex); + guard(mvm)(mvm); - iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA); + /* will reschedule to run after restart */ + if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) || + test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + return; + + iwl_mvm_inactivity_check(mvm, IWL_INVALID_STA); while (!list_empty(&mvm->add_stream_txqs)) { struct iwl_mvm_txq *mvmtxq; @@ -1564,8 +1569,6 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) iwl_mvm_mac_itxq_xmit(mvm->hw, txq); local_bh_enable(); } - - mutex_unlock(&mvm->mutex); } static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, @@ -1580,7 +1583,7 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, return 0; /* run the general cleanup/unsharing of queues */ - iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA); + iwl_mvm_inactivity_check(mvm, IWL_INVALID_STA); /* Make sure we have free resources for this STA */ if (vif_type == NL80211_IFTYPE_STATION && !sta->tdls && @@ -1622,7 +1625,7 @@ void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); unsigned int wdg = - iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif, false, false); + iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif); int i; struct iwl_trans_txq_scd_cfg cfg = { .sta_id = mvm_sta->deflink.sta_id, @@ -1756,7 +1759,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * this function */ if (!mvm->mld_api_is_used) { - if (WARN_ON(sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON(sta_id == IWL_INVALID_STA)) return -EINVAL; mvm_sta->deflink.sta_id = sta_id; @@ -1865,7 +1868,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, else sta_id = mvm_sta->deflink.sta_id; - if (sta_id == IWL_MVM_INVALID_STA) + if (sta_id == IWL_INVALID_STA) return -ENOSPC; spin_lock_init(&mvm_sta->lock); @@ -1903,10 +1906,10 @@ update_fw: if (vif->type == NL80211_IFTYPE_STATION) { if (!sta->tdls) { - WARN_ON(mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA); + WARN_ON(mvmvif->deflink.ap_sta_id != IWL_INVALID_STA); mvmvif->deflink.ap_sta_id = sta_id; } else { - WARN_ON(mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA); + WARN_ON(mvmvif->deflink.ap_sta_id == IWL_INVALID_STA); } } @@ -2045,9 +2048,9 @@ int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm, * Returns if we're done with removing the station, either * with error or success */ -bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +void iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_link_sta *link_sta, int *ret) + struct ieee80211_link_sta *link_sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif_link_info *mvm_link = @@ -2063,39 +2066,13 @@ bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_is_held(&mvm->mutex)); sta_id = mvm_link_sta->sta_id; - /* If there is a TXQ still marked as reserved - free it */ - if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { - u8 reserved_txq = mvm_sta->reserved_queue; - enum iwl_mvm_queue_status *status; - - /* - * If no traffic has gone through the reserved TXQ - it - * is still marked as IWL_MVM_QUEUE_RESERVED, and - * should be manually marked as free again - */ - status = &mvm->queue_info[reserved_txq].status; - if (WARN((*status != IWL_MVM_QUEUE_RESERVED) && - (*status != IWL_MVM_QUEUE_FREE), - "sta_id %d reserved txq %d status %d", - sta_id, reserved_txq, *status)) { - *ret = -EINVAL; - return true; - } - - *status = IWL_MVM_QUEUE_FREE; - } - if (vif->type == NL80211_IFTYPE_STATION && mvm_link->ap_sta_id == sta_id) { - /* if associated - we can't remove the AP STA now */ - if (vif->cfg.assoc) - return true; - /* first remove remaining keys */ - iwl_mvm_sec_key_remove_ap(mvm, vif, mvm_link, 0); + iwl_mvm_sec_key_remove_ap(mvm, vif, mvm_link, + link_sta->link_id); - /* unassoc - go ahead - remove the AP STA now */ - mvm_link->ap_sta_id = IWL_MVM_INVALID_STA; + mvm_link->ap_sta_id = IWL_INVALID_STA; } /* @@ -2103,11 +2080,9 @@ bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * before the STA is removed. */ if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == sta_id)) { - mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; + mvm->tdls_cs.peer.sta_id = IWL_INVALID_STA; cancel_delayed_work(&mvm->tdls_cs.dwork); } - - return false; } int iwl_mvm_rm_sta(struct iwl_mvm *mvm, @@ -2143,8 +2118,27 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, iwl_mvm_disable_sta_queues(mvm, vif, sta); - if (iwl_mvm_sta_del(mvm, vif, sta, &sta->deflink, &ret)) - return ret; + /* If there is a TXQ still marked as reserved - free it */ + if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { + u8 reserved_txq = mvm_sta->reserved_queue; + enum iwl_mvm_queue_status *status; + + /* + * If no traffic has gone through the reserved TXQ - it + * is still marked as IWL_MVM_QUEUE_RESERVED, and + * should be manually marked as free again + */ + status = &mvm->queue_info[reserved_txq].status; + if (WARN((*status != IWL_MVM_QUEUE_RESERVED) && + (*status != IWL_MVM_QUEUE_FREE), + "sta_id %d reserved txq %d status %d", + mvm_sta->deflink.sta_id, reserved_txq, *status)) + return -EINVAL; + + *status = IWL_MVM_QUEUE_FREE; + } + + iwl_mvm_sta_del(mvm, vif, sta, &sta->deflink); ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->deflink.sta_id); RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->deflink.sta_id], NULL); @@ -2170,9 +2164,9 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, u8 type) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || - sta->sta_id == IWL_MVM_INVALID_STA) { + sta->sta_id == IWL_INVALID_STA) { sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); - if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(sta->sta_id == IWL_INVALID_STA)) return -ENOSPC; } @@ -2188,7 +2182,7 @@ void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); - sta->sta_id = IWL_MVM_INVALID_STA; + sta->sta_id = IWL_INVALID_STA; } static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue, @@ -2306,7 +2300,7 @@ int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_INVALID_STA)) return -EINVAL; iwl_mvm_disable_txq(mvm, NULL, mvm->snif_sta.sta_id, @@ -2324,7 +2318,7 @@ int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_INVALID_STA)) return -EINVAL; iwl_mvm_disable_txq(mvm, NULL, mvm->aux_sta.sta_id, @@ -2359,7 +2353,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int queue; int ret; unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); + iwl_mvm_get_wd_timeout(mvm, vif); struct iwl_trans_txq_scd_cfg cfg = { .fifo = IWL_MVM_TX_FIFO_VO, .sta_id = mvmvif->deflink.bcast_sta.sta_id, @@ -2389,7 +2383,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (vif->type == NL80211_IFTYPE_ADHOC) baddr = vif->bss_conf.bssid; - if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(bsta->sta_id == IWL_INVALID_STA)) return -ENOSPC; ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr, @@ -2568,7 +2562,7 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) .aggregate = false, .frame_limit = IWL_FRAME_LIMIT, }; - unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); + unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif); int ret; lockdep_assert_held(&mvm->mutex); @@ -2644,7 +2638,7 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, u32 status; /* This is a valid situation for GTK removal */ - if (sta_id == IWL_MVM_INVALID_STA) + if (sta_id == IWL_INVALID_STA) return 0; key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & @@ -2912,7 +2906,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* * The division below will be OK if either the cache line size * can be divided by the entry size (ALIGN will round up) or if - * if the entry size can be divided by the cache line size, in + * the entry size can be divided by the cache line size, in * which case the ALIGN() will do nothing. */ BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) && @@ -3207,7 +3201,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); + iwl_mvm_get_wd_timeout(mvm, vif); int queue, ret; bool alloc_queue = true; enum iwl_mvm_queue_status queue_status; @@ -3514,7 +3508,7 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, * station ID, then use AP's station ID. */ if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { + mvmvif->deflink.ap_sta_id != IWL_INVALID_STA) { u8 sta_id = mvmvif->deflink.ap_sta_id; sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], @@ -3569,7 +3563,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, int api_ver = iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA_KEY, new_api ? 2 : 1); - if (sta_id == IWL_MVM_INVALID_STA) + if (sta_id == IWL_INVALID_STA) return -EINVAL; keyidx = (key->keyidx << STA_KEY_FLG_KEYID_POS) & @@ -3728,7 +3722,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, if (remove_key) { /* This is a valid situation for IGTK */ - if (sta_id == IWL_MVM_INVALID_STA) + if (sta_id == IWL_INVALID_STA) return 0; igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); @@ -3795,7 +3789,7 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, return sta->addr; if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { + mvmvif->deflink.ap_sta_id != IWL_INVALID_STA) { u8 sta_id = mvmvif->deflink.ap_sta_id; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex)); @@ -3865,7 +3859,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); struct iwl_mvm_sta *mvm_sta; - u8 sta_id = IWL_MVM_INVALID_STA; + u8 sta_id = IWL_INVALID_STA; int ret; static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; @@ -3966,7 +3960,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); struct iwl_mvm_sta *mvm_sta; - u8 sta_id = IWL_MVM_INVALID_STA; + u8 sta_id = IWL_INVALID_STA; int ret, i; lockdep_assert_held(&mvm->mutex); @@ -4273,7 +4267,7 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, return; /* Need to block/unblock also multicast station */ - if (mvmvif->deflink.mcast_sta.sta_id != IWL_MVM_INVALID_STA) + if (mvmvif->deflink.mcast_sta.sta_id != IWL_INVALID_STA) iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, &mvmvif->deflink.mcast_sta, disable); @@ -4282,7 +4276,7 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, * Only unblock the broadcast station (FW blocks it for immediate * quiet, not the driver) */ - if (!disable && mvmvif->deflink.bcast_sta.sta_id != IWL_MVM_INVALID_STA) + if (!disable && mvmvif->deflink.bcast_sta.sta_id != IWL_INVALID_STA) iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, &mvmvif->deflink.bcast_sta, disable); @@ -4326,9 +4320,12 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u16 queue; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); + iwl_mvm_get_wd_timeout(mvm, vif); bool mld = iwl_mvm_has_mld_api(mvm->fw); - u32 type = mld ? STATION_TYPE_PEER : IWL_STA_LINK; + u32 type = IWL_STA_LINK; + + if (mld) + type = STATION_TYPE_PEER; ret = iwl_mvm_allocate_int_sta(mvm, sta, 0, NL80211_IFTYPE_UNSPECIFIED, type); @@ -4455,10 +4452,10 @@ void iwl_mvm_count_mpdu(struct iwl_mvm_sta *mvm_sta, u8 fw_sta_id, u32 count, sizeof(queue_counter->per_link)); queue_counter->window_start = jiffies; - IWL_DEBUG_STATS(mvm, "MPDU counters are cleared\n"); + IWL_DEBUG_INFO(mvm, "MPDU counters are cleared\n"); } - for (int i = 0; i < IWL_MVM_FW_MAX_LINK_ID; i++) + for (int i = 0; i < IWL_FW_MAX_LINK_ID; i++) total_mpdus += tx ? queue_counter->per_link[i].tx : queue_counter->per_link[i].rx; diff --git a/mvm/sta.h b/mvm/sta.h index 0dc83d6afb3c..6856f7440ef3 100644 --- a/mvm/sta.h +++ b/mvm/sta.h @@ -12,7 +12,7 @@ #include <linux/wait.h> #include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */ -#include "fw-api.h" /* IWL_MVM_STATION_COUNT_MAX */ +#include "fw-api.h" /* IWL_STATION_COUNT_MAX */ #include "rs.h" struct iwl_mvm; @@ -133,7 +133,7 @@ struct iwl_mvm_vif; * and no TID data as this is also not needed. * One thing to note, is that these stations have an ID in the fw, but not * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id - * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of + * we fill ERR_PTR(-EINVAL) in this mapping and all other dereferencing of * pointers from this mapping need to check that the value is not error * or NULL. * @@ -361,7 +361,7 @@ struct iwl_mvm_mpdu_counter { */ struct iwl_mvm_tpt_counter { spinlock_t lock; - struct iwl_mvm_mpdu_counter per_link[IWL_MVM_FW_MAX_LINK_ID]; + struct iwl_mvm_mpdu_counter per_link[IWL_FW_MAX_LINK_ID]; unsigned long window_start; } ____cacheline_aligned_in_smp; @@ -507,9 +507,9 @@ void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, struct ieee80211_sta *sta); int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta); -bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +void iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_link_sta *link_sta, int *ret); + struct ieee80211_link_sta *link_sta); int iwl_mvm_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta); @@ -665,8 +665,7 @@ int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta, struct iwl_mvm_link_sta *mvm_sta_link, - unsigned int link_id, - bool is_in_fw); + unsigned int link_id); int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id); int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/mvm/tdls.c b/mvm/tdls.c index 3d25ff5cd7e8..36379b738de1 100644 --- a/mvm/tdls.c +++ b/mvm/tdls.c @@ -24,7 +24,7 @@ void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) + if (IS_ERR_OR_NULL(sta) || !sta->tdls) continue; mvmsta = iwl_mvm_sta_from_mac80211(sta); @@ -47,7 +47,7 @@ int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) + if (IS_ERR_OR_NULL(sta) || !sta->tdls) continue; if (vif) { @@ -196,7 +196,7 @@ static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, mvm->tdls_cs.peer.sent_timestamp = iwl_mvm_get_systime(mvm); if (state == IWL_MVM_TDLS_SW_IDLE) - mvm->tdls_cs.cur_sta_id = IWL_MVM_INVALID_STA; + mvm->tdls_cs.cur_sta_id = IWL_INVALID_STA; } void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) @@ -250,7 +250,7 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, /* get the existing peer if it's there */ if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE && - mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) { + mvm->tdls_cs.cur_sta_id != IWL_INVALID_STA) { struct ieee80211_sta *sta = rcu_dereference_protected( mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], lockdep_is_held(&mvm->mutex)); @@ -465,14 +465,14 @@ void iwl_mvm_tdls_ch_switch_work(struct work_struct *work) iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); /* station might be gone, in that case do nothing */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA) + if (mvm->tdls_cs.peer.sta_id == IWL_INVALID_STA) return; sta = rcu_dereference_protected( mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], lockdep_is_held(&mvm->mutex)); /* the station may not be here, but if it is, it must be a TDLS peer */ - if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls)) + if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) return; mvmsta = iwl_mvm_sta_from_mac80211(sta); @@ -512,7 +512,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, sta->addr, chandef->chan->center_freq, chandef->width); /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id != IWL_MVM_INVALID_STA) { + if (mvm->tdls_cs.peer.sta_id != IWL_INVALID_STA) { IWL_DEBUG_TDLS(mvm, "Existing peer. Can't start switch with %pM\n", sta->addr); @@ -566,7 +566,7 @@ void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr); /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA) { + if (mvm->tdls_cs.peer.sta_id == IWL_INVALID_STA) { IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); goto out; } @@ -587,7 +587,7 @@ void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE) wait_for_phy = true; - mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; + mvm->tdls_cs.peer.sta_id = IWL_INVALID_STA; dev_kfree_skb(mvm->tdls_cs.peer.skb); mvm->tdls_cs.peer.skb = NULL; @@ -630,7 +630,7 @@ iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE && params->status != 0 && mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) { + mvm->tdls_cs.cur_sta_id != IWL_INVALID_STA) { struct ieee80211_sta *cur_sta; /* make sure it's the same peer */ diff --git a/mvm/time-event.c b/mvm/time-event.c index a8c42ce3b630..ebfa88b38b71 100644 --- a/mvm/time-event.c +++ b/mvm/time-event.c @@ -114,16 +114,14 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm) iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id, mvm->aux_sta.tfd_queue_msk); - if (mvm->mld_api_is_used) { - iwl_mvm_mld_rm_aux_sta(mvm); - mutex_unlock(&mvm->mutex); - return; - } - /* In newer version of this command an aux station is added only * in cases of dedicated tx queue and need to be removed in end - * of use */ - if (iwl_mvm_has_new_station_api(mvm->fw)) + * of use. For the even newer mld api, use the appropriate + * function. + */ + if (mvm->mld_api_is_used) + iwl_mvm_mld_rm_aux_sta(mvm); + else if (iwl_mvm_has_new_station_api(mvm->fw)) iwl_mvm_rm_aux_sta(mvm); } @@ -753,7 +751,7 @@ static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, u32 id, s8 link_id) { int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id); - struct iwl_mvm_session_prot_cmd cmd = { + struct iwl_session_prot_cmd cmd = { .id_and_color = cpu_to_le32(mac_link_id), .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), .conf_id = cpu_to_le32(id), @@ -957,7 +955,7 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; + struct iwl_session_prot_notif *notif = (void *)pkt->data; unsigned int ver = iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF, 2); @@ -1032,6 +1030,8 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, /* End TE, notify mac80211 */ mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; mvmvif->time_event_data.link_id = -1; + /* set the bit so the ROC cleanup will actually clean up */ + set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status); iwl_mvm_roc_finished(mvm); ieee80211_remain_on_channel_expired(mvm->hw); } else if (le32_to_cpu(notif->start)) { @@ -1150,7 +1150,7 @@ iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, enum ieee80211_roc_type type) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_session_prot_cmd cmd = { + struct iwl_session_prot_cmd cmd = { .id_and_color = cpu_to_le32(iwl_mvm_get_session_prot_id(mvm, vif, 0)), .action = cpu_to_le32(FW_CTXT_ACTION_ADD), @@ -1419,7 +1419,7 @@ static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, { struct iwl_mvm *mvm = container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_session_prot_notif *resp; + struct iwl_session_prot_notif *resp; int resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON(pkt->hdr.cmd != SESSION_PROTECTION_NOTIF || @@ -1451,7 +1451,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; struct iwl_notification_wait wait_notif; int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, (s8)link_id); - struct iwl_mvm_session_prot_cmd cmd = { + struct iwl_session_prot_cmd cmd = { .id_and_color = cpu_to_le32(mac_link_id), .action = cpu_to_le32(FW_CTXT_ACTION_ADD), .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), @@ -13,6 +13,7 @@ #include "iwl-trans.h" #include "iwl-nvm-utils.h" +#include "iwl-utils.h" #include "mvm.h" #include "sta.h" #include "time-sync.h" @@ -938,78 +939,6 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, #ifdef CONFIG_INET -static int -iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, - netdev_features_t netdev_flags, - struct sk_buff_head *mpdus_skb) -{ - struct sk_buff *tmp, *next; - struct ieee80211_hdr *hdr = (void *)skb->data; - char cb[sizeof(skb->cb)]; - u16 i = 0; - unsigned int tcp_payload_len; - unsigned int mss = skb_shinfo(skb)->gso_size; - bool ipv4 = (skb->protocol == htons(ETH_P_IP)); - bool qos = ieee80211_is_data_qos(hdr->frame_control); - u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; - - skb_shinfo(skb)->gso_size = num_subframes * mss; - memcpy(cb, skb->cb, sizeof(cb)); - - next = skb_gso_segment(skb, netdev_flags); - skb_shinfo(skb)->gso_size = mss; - skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; - - if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM) - return -ENOMEM; - - if (WARN_ONCE(IS_ERR(next), - "skb_gso_segment error: %d\n", (int)PTR_ERR(next))) - return PTR_ERR(next); - - if (next) - consume_skb(skb); - - skb_list_walk_safe(next, tmp, next) { - memcpy(tmp->cb, cb, sizeof(tmp->cb)); - /* - * Compute the length of all the data added for the A-MSDU. - * This will be used to compute the length to write in the TX - * command. We have: SNAP + IP + TCP for n -1 subframes and - * ETH header for n subframes. - */ - tcp_payload_len = skb_tail_pointer(tmp) - - skb_transport_header(tmp) - - tcp_hdrlen(tmp) + tmp->data_len; - - if (ipv4) - ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); - - if (tcp_payload_len > mss) { - skb_shinfo(tmp)->gso_size = mss; - skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 : - SKB_GSO_TCPV6; - } else { - if (qos) { - u8 *qc; - - if (ipv4) - ip_send_check(ip_hdr(tmp)); - - qc = ieee80211_get_qos_ctl((void *)tmp->data); - *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - } - skb_shinfo(tmp)->gso_size = 0; - } - - skb_mark_not_on_list(tmp); - __skb_queue_tail(mpdus_skb, tmp); - i++; - } - - return 0; -} - static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, @@ -1028,7 +957,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, if (!mvmsta->max_amsdu_len || !ieee80211_is_data_qos(hdr->frame_control) || !mvmsta->amsdu_enabled) - return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); /* * Do not build AMSDU for IPv6 with extension headers. @@ -1038,7 +967,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, ((struct ipv6hdr *)skb_network_header(skb))->nexthdr != IPPROTO_TCP) { netdev_flags &= ~NETIF_F_CSUM_MASK; - return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); } tid = ieee80211_get_tid(hdr); @@ -1052,7 +981,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, if ((info->flags & IEEE80211_TX_CTL_AMPDU && !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) || !(mvmsta->amsdu_enabled & BIT(tid))) - return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); /* * Take the min of ieee80211 station and mvm station @@ -1110,8 +1039,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * Trick the segmentation function to make it * create SKBs that can fit into one A-MSDU. */ - return iwl_mvm_tx_tso_segment(skb, num_subframes, netdev_flags, - mpdus_skb); + return iwl_tx_tso_segment(skb, num_subframes, netdev_flags, mpdus_skb); } #else /* CONFIG_INET */ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, @@ -1203,6 +1131,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, bool is_ampdu = false; int hdrlen; + if (WARN_ON_ONCE(!sta)) + return -1; + mvmsta = iwl_mvm_sta_from_mac80211(sta); fc = hdr->frame_control; hdrlen = ieee80211_hdrlen(fc); @@ -1210,10 +1141,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (IWL_MVM_NON_TRANSMITTING_AP && ieee80211_is_probe_resp(fc)) return -1; - if (WARN_ON_ONCE(!mvmsta)) - return -1; - - if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_INVALID_STA)) return -1; if (unlikely(ieee80211_is_any_nullfunc(fc)) && sta->deflink.he_cap.has_he) @@ -1343,7 +1271,7 @@ drop: int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta *mvmsta; struct ieee80211_tx_info info; struct sk_buff_head mpdus_skbs; struct ieee80211_vif *vif; @@ -1352,10 +1280,12 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct sk_buff *orig_skb = skb; const u8 *addr3; - if (WARN_ON_ONCE(!mvmsta)) + if (WARN_ON_ONCE(!sta)) return -1; - if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA)) + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_INVALID_STA)) return -1; memcpy(&info, skb->cb, sizeof(info)); @@ -1678,7 +1608,7 @@ static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, * For 22000-series and lower, this is just 12 bits. For later, 16 bits. */ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, - struct iwl_mvm_tx_resp *tx_resp) + struct iwl_tx_resp *tx_resp) { u32 val = le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + tx_resp->frame_count); @@ -1694,10 +1624,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct ieee80211_sta *sta; u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); - /* struct iwl_mvm_tx_resp_v3 is almost the same */ - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); + /* struct iwl_tx_resp_v3 is almost the same */ + struct iwl_tx_resp *tx_resp = (void *)pkt->data; + int sta_id = IWL_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_TX_RES_GET_TID(tx_resp->ra_tid); struct agg_tx_status *agg_status = iwl_mvm_get_agg_status(mvm, tx_resp); u32 status = le16_to_cpu(agg_status->status); @@ -1878,7 +1808,9 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", next_reclaimed); - iwl_mvm_count_mpdu(mvmsta, sta_id, 1, true, 0); + if (tid < IWL_MAX_TID_COUNT) + iwl_mvm_count_mpdu(mvmsta, sta_id, 1, + true, 0); } else { IWL_DEBUG_TX_REPLY(mvm, "NDP - don't update next_reclaimed\n"); @@ -1952,7 +1884,7 @@ static const char *iwl_get_agg_tx_status(u16 status) static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + struct iwl_tx_resp *tx_resp = (void *)pkt->data; struct agg_tx_status *frame_status = iwl_mvm_get_agg_status(mvm, tx_resp); int i; @@ -1986,9 +1918,9 @@ static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); + struct iwl_tx_resp *tx_resp = (void *)pkt->data; + int sta_id = IWL_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_TX_RES_GET_TID(tx_resp->ra_tid); u16 sequence = le16_to_cpu(pkt->hdr.sequence); struct iwl_mvm_sta *mvmsta; int queue = SEQ_TO_QUEUE(sequence); @@ -2027,7 +1959,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + struct iwl_tx_resp *tx_resp = (void *)pkt->data; if (tx_resp->frame_count == 1) iwl_mvm_rx_tx_cmd_single(mvm, pkt); @@ -2191,7 +2123,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ba_info.flags = IEEE80211_TX_STAT_AMPDU; if (iwl_mvm_has_new_tx_api(mvm)) { - struct iwl_mvm_compressed_ba_notif *ba_res = + struct iwl_compressed_ba_notif *ba_res = (void *)pkt->data; u8 lq_color = TX_RES_RATE_TABLE_COL_GET(ba_res->tlc_rate_info); u16 tfd_cnt; @@ -2239,8 +2171,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) /* Free per TID */ for (i = 0; i < tfd_cnt; i++) { - struct iwl_mvm_compressed_ba_tfd *ba_tfd = - &ba_res->tfd[i]; + struct iwl_compressed_ba_tfd *ba_tfd = &ba_res->tfd[i]; tid = ba_tfd->tid; if (tid == IWL_MGMT_TID) diff --git a/mvm/utils.c b/mvm/utils.c index 0e5fa8374103..dd890dcd1505 100644 --- a/mvm/utils.c +++ b/mvm/utils.c @@ -261,7 +261,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq) .data = { lq, }, }; - if (WARN_ON(lq->sta_id == IWL_MVM_INVALID_STA || + if (WARN_ON(lq->sta_id == IWL_INVALID_STA || iwl_mvm_has_tlc_offload(mvm))) return -EINVAL; @@ -297,6 +297,10 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->type != NL80211_IFTYPE_STATION) return; + /* SMPS is handled by firmware */ + if (iwl_mvm_has_rlc_offload(mvm)) + return; + mvmvif = iwl_mvm_vif_from_mac80211(vif); if (WARN_ON_ONCE(!mvmvif->link[link_id])) @@ -675,10 +679,8 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bss_iface_iterator, &bss_iter_data); - if (bss_iter_data.error) { - IWL_ERR(mvm, "More than one managed interface active!\n"); + if (bss_iter_data.error) return ERR_PTR(-EINVAL); - } return bss_iter_data.vif; } @@ -743,58 +745,20 @@ bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm) } unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q) + struct ieee80211_vif *vif) { - struct iwl_fw_dbg_trigger_tlv *trigger; - struct iwl_fw_dbg_trigger_txq_timer *txq_timer; - unsigned int default_timeout = cmd_q ? - IWL_DEF_WD_TIMEOUT : + unsigned int default_timeout = mvm->trans->trans_cfg->base_params->wd_timeout; - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS)) { - /* - * We can't know when the station is asleep or awake, so we - * must disable the queue hang detection. - */ - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_STA_PM_NOTIF) && - vif && vif->type == NL80211_IFTYPE_AP) - return IWL_WATCHDOG_DISABLED; - return default_timeout; - } - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS); - txq_timer = (void *)trigger->data; - - if (tdls) - return le32_to_cpu(txq_timer->tdls); - - if (cmd_q) - return le32_to_cpu(txq_timer->command_queue); - - if (WARN_ON(!vif)) - return default_timeout; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_ADHOC: - return le32_to_cpu(txq_timer->ibss); - case NL80211_IFTYPE_STATION: - return le32_to_cpu(txq_timer->bss); - case NL80211_IFTYPE_AP: - return le32_to_cpu(txq_timer->softap); - case NL80211_IFTYPE_P2P_CLIENT: - return le32_to_cpu(txq_timer->p2p_client); - case NL80211_IFTYPE_P2P_GO: - return le32_to_cpu(txq_timer->p2p_go); - case NL80211_IFTYPE_P2P_DEVICE: - return le32_to_cpu(txq_timer->p2p_device); - case NL80211_IFTYPE_MONITOR: - return default_timeout; - default: - WARN_ON(1); - return mvm->trans->trans_cfg->base_params->wd_timeout; - } + /* + * We can't know when the station is asleep or awake, so we + * must disable the queue hang detection. + */ + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_STA_PM_NOTIF) && + vif->type == NL80211_IFTYPE_AP) + return IWL_WATCHDOG_DISABLED; + return default_timeout; } void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/pcie/ctxt-info-gen3.c b/pcie/ctxt-info-gen3.c index ae93a72542b2..838c426db7f0 100644 --- a/pcie/ctxt-info-gen3.c +++ b/pcie/ctxt-info-gen3.c @@ -106,6 +106,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl; struct iwl_prph_info *prph_info; u32 control_flags = 0; + u32 control_flags_ext = 0; int ret; int cmdq_size = max_t(u32, IWL_CMD_QUEUE_SIZE, trans->cfg->min_txq_size); @@ -130,6 +131,12 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, break; } + if (trans->dsbr_urm_fw_dependent) + control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_FW; + + if (trans->dsbr_urm_permanent) + control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_PERM; + /* Allocate prph scratch */ prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch), &trans_pcie->prph_scratch_dma_addr, @@ -165,6 +172,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, iwl_pcie_ctxt_info_dbg_enable(trans, &prph_sc_ctrl->hwm_cfg, &control_flags); prph_sc_ctrl->control.control_flags = cpu_to_le32(control_flags); + prph_sc_ctrl->control.control_flags_ext = cpu_to_le32(control_flags_ext); /* initialize the Step equalizer data */ prph_sc_ctrl->step_cfg.mbx_addr_0 = cpu_to_le32(trans->mbx_addr_0_step); diff --git a/pcie/drv.c b/pcie/drv.c index 84fd93278450..e0b657b2f74b 100644 --- a/pcie/drv.c +++ b/pcie/drv.c @@ -500,9 +500,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)}, /* Bz devices */ - {IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0x272D, PCI_ANY_ID, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x0090, iwl_bz_trans_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x0094, iwl_bz_trans_cfg)}, @@ -542,6 +540,9 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_trans_cfg)}, {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_trans_cfg)}, {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_trans_cfg)}, + +/* Dr devices */ + {IWL_PCI_DEVICE(0x272F, PCI_ANY_ID, iwl_dr_trans_cfg)}, #endif /* CONFIG_IWLMVM */ {0} @@ -1108,19 +1109,54 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), /* Bz */ -/* FIXME: need to change the naming according to the actual CRF */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_ax201_name), + + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_ax211_name), + + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_fm_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_wh_name), + + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_ax201_name), + + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_ax211_name), + + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_bz, iwl_fm_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_bz, iwl_wh_name), + /* Ga (Gl) */ _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, @@ -1149,6 +1185,19 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, iwl_cfg_sc2f, iwl_sc2f_name), +/* Dr */ + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_dr, iwl_dr_name), + +/* Br */ + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BR, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, + iwl_cfg_br, iwl_br_name), #endif /* CONFIG_IWLMVM */ }; EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table); @@ -1253,6 +1302,9 @@ static int map_crf_id(struct iwl_trans *iwl_trans) case REG_CRF_ID_TYPE_WHP: iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_WH << 12); break; + case REG_CRF_ID_TYPE_PE: + iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_PE << 12); + break; default: ret = -EIO; IWL_ERR(iwl_trans, @@ -1358,6 +1410,47 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device, } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_pci_find_dev_info); +static void iwl_pcie_recheck_me_status(struct work_struct *wk) +{ + struct iwl_trans *trans = container_of(wk, typeof(*trans), + me_recheck_wk.work); + u32 val; + + val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG); + trans->me_present = !!(val & CSR_HW_IF_CONFIG_REG_IAMT_UP); +} + +static void iwl_pcie_check_me_status(struct iwl_trans *trans) +{ + u32 val; + + trans->me_present = -1; + + INIT_DELAYED_WORK(&trans->me_recheck_wk, + iwl_pcie_recheck_me_status); + + /* we don't have a good way of determining this until BZ */ + if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + return; + + val = iwl_read_prph(trans, CNVI_SCU_REG_FOR_ECO_1); + if (val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN) { + trans->me_present = + !!(val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT); + return; + } + + val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG); + if (val & (CSR_HW_IF_CONFIG_REG_ME_OWN | + CSR_HW_IF_CONFIG_REG_IAMT_UP)) { + trans->me_present = 1; + return; + } + + /* recheck again later, ME might still be initializing */ + schedule_delayed_work(&trans->me_recheck_wk, HZ); +} + static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct iwl_cfg_trans_params *trans; @@ -1387,6 +1480,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); + iwl_trans_pcie_check_product_reset_status(pdev); + iwl_trans_pcie_check_product_reset_mode(pdev); + /* * Let's try to grab NIC access early here. Sometimes, NICs may * fail to initialize, and if that happens it's better if we see @@ -1533,6 +1629,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, iwl_trans); + iwl_pcie_check_me_status(iwl_trans); + /* try to get ownership so that we'll know if we don't own it */ iwl_pcie_prepare_card_hw(iwl_trans); @@ -1560,6 +1658,8 @@ static void iwl_pci_remove(struct pci_dev *pdev) if (!trans) return; + cancel_delayed_work_sync(&trans->me_recheck_wk); + iwl_drv_stop(trans->drv); iwl_trans_pcie_free(trans); diff --git a/pcie/internal.h b/pcie/internal.h index 27a7e0b5b3d5..45460f93d24a 100644 --- a/pcie/internal.h +++ b/pcie/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2003-2015, 2018-2024 Intel Corporation + * Copyright (C) 2003-2015, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -563,6 +563,9 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); __cond_lock(nic_access_nobh, \ likely(__iwl_trans_pcie_grab_nic_access(trans))) +void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev); +void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev); + /***************************************************** * RX ******************************************************/ @@ -643,7 +646,8 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, unsigned int len); struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta, - u8 **hdr, unsigned int hdr_room); + u8 **hdr, unsigned int hdr_room, + unsigned int offset); void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta); @@ -1134,9 +1138,6 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans); int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans); -void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans); -void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, - bool test, bool reset); int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, diff --git a/pcie/rx.c b/pcie/rx.c index afb88eab8174..4a442d03d8d2 100644 --- a/pcie/rx.c +++ b/pcie/rx.c @@ -1702,7 +1702,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) /* The STATUS_FW_ERROR bit is set in this function. This must happen * before we wake up the command caller, to ensure a proper cleanup. */ - iwl_trans_fw_error(trans, false); + iwl_trans_fw_error(trans, IWL_ERR_TYPE_IRQ); clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); wake_up(&trans->wait_command_queue); @@ -2297,7 +2297,9 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) if (inta_hw & MSIX_HW_INT_CAUSES_REG_TOP_FATAL_ERR) { IWL_ERR(trans, "TOP Fatal error detected, inta_hw=0x%x.\n", inta_hw); - /* TODO: PLDR flow required here for >= Bz */ + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + iwl_trans_pcie_reset(trans, + IWL_RESET_MODE_PROD_RESET); } /* Error detected by uCode */ diff --git a/pcie/trans-gen2.c b/pcie/trans-gen2.c index 18dda89b7985..793514a1852a 100644 --- a/pcie/trans-gen2.c +++ b/pcie/trans-gen2.c @@ -43,7 +43,7 @@ int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) * wake device's PCI Express link L1a -> L0s */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + CSR_HW_IF_CONFIG_REG_HAP_WAKE); iwl_pcie_apm_config(trans); @@ -68,8 +68,8 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE | - CSR_HW_IF_CONFIG_REG_ENABLE_PME); + CSR_HW_IF_CONFIG_REG_WAKE_ME | + CSR_HW_IF_CONFIG_REG_WAKE_ME_PCIE_OWNER_EN); mdelay(1); iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); @@ -123,14 +123,21 @@ static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) "timeout waiting for FW reset ACK (inta_hw=0x%x)\n", inta_hw); - if (!(inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)) - iwl_trans_fw_error(trans, true); + if (!(inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)) { + struct iwl_fw_error_dump_mode mode = { + .type = IWL_ERR_TYPE_RESET_HS_TIMEOUT, + .context = IWL_ERR_CONTEXT_FROM_OPMODE, + }; + iwl_op_mode_nic_error(trans->op_mode, + IWL_ERR_TYPE_RESET_HS_TIMEOUT); + iwl_op_mode_dump_error(trans->op_mode, &mode); + } } trans_pcie->fw_reset_state = FW_RESET_IDLE; } -void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) +static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -139,9 +146,9 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) if (trans_pcie->is_down) return; - if (trans->state >= IWL_TRANS_FW_STARTED) - if (trans_pcie->fw_reset_handshake) - iwl_trans_pcie_fw_reset_handshake(trans); + if (trans->state >= IWL_TRANS_FW_STARTED && + trans_pcie->fw_reset_handshake) + iwl_trans_pcie_fw_reset_handshake(trans); trans_pcie->is_down = true; @@ -287,9 +294,6 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB): pos = scnprintf(buf, buflen, "HRCDB"); break; - case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_MS): - pos = scnprintf(buf, buflen, "MS"); - break; case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_FM): pos = scnprintf(buf, buflen, "FM"); break; @@ -526,6 +530,8 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, keep_ram_busy = !iwl_pcie_set_ltr(trans); if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + IWL_DEBUG_POWER(trans, "function scratch register value is 0x%08x\n", + iwl_read32(trans, CSR_FUNC_SCRATCH)); iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE); iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_ROM_START); diff --git a/pcie/trans.c b/pcie/trans.c index 719ddc4b72c5..c917ed4c19bc 100644 --- a/pcie/trans.c +++ b/pcie/trans.c @@ -24,6 +24,7 @@ #include "fw/error-dump.h" #include "fw/dbg.h" #include "fw/api/tx.h" +#include "fw/acpi.h" #include "mei/iwl-mei.h" #include "internal.h" #include "iwl-fh.h" @@ -311,7 +312,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) * wake device's PCI Express link L1a -> L0s */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + CSR_HW_IF_CONFIG_REG_HAP_WAKE); iwl_pcie_apm_config(trans); @@ -439,7 +440,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) * SHRD_HW_RST is applied in S3. */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + CSR_HW_IF_CONFIG_REG_PERSISTENCE); /* * Clear "initialization complete" bit to move adapter from @@ -508,8 +509,8 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE | - CSR_HW_IF_CONFIG_REG_ENABLE_PME); + CSR_HW_IF_CONFIG_REG_WAKE_ME | + CSR_HW_IF_CONFIG_REG_WAKE_ME_PCIE_OWNER_EN); mdelay(1); iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); @@ -581,12 +582,12 @@ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) int ret; iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + CSR_HW_IF_CONFIG_REG_PCI_OWN_SET); /* See if we got it */ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_PCI_OWN_SET, + CSR_HW_IF_CONFIG_REG_PCI_OWN_SET, HW_READY_TIMEOUT); if (ret >= 0) @@ -620,7 +621,7 @@ int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); + CSR_HW_IF_CONFIG_REG_WAKE_ME); do { ret = iwl_pcie_set_hw_ready(trans); @@ -1488,8 +1489,8 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) _iwl_trans_pcie_stop_device(trans, from_irq); } -void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, - bool test, bool reset) +static void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, + bool test, bool reset) { iwl_disable_interrupts(trans); @@ -1566,7 +1567,7 @@ int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset) if (!reset) /* Enable persistence mode to avoid reset */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + CSR_HW_IF_CONFIG_REG_PERSISTENCE); ret = iwl_pcie_d3_handshake(trans, true); if (ret) @@ -1643,6 +1644,8 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, out: if (*status == IWL_D3_STATUS_ALIVE) ret = iwl_pcie_d3_handshake(trans, false); + else + trans->state = IWL_TRANS_NO_FW; return ret; } @@ -1967,7 +1970,6 @@ void iwl_trans_pcie_configure(struct iwl_trans *trans, trans_pcie->txqs.cmd.q_id = trans_cfg->cmd_queue; trans_pcie->txqs.cmd.fifo = trans_cfg->cmd_fifo; - trans_pcie->txqs.cmd.wdg_timeout = trans_cfg->cmd_q_wdg_timeout; trans_pcie->txqs.page_offs = trans_cfg->cb_data_offs; trans_pcie->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); trans_pcie->txqs.queue_alloc_cmd_ver = trans_cfg->queue_alloc_cmd_ver; @@ -2104,10 +2106,157 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) iwl_trans_free(trans); } +static union acpi_object * +iwl_trans_pcie_call_prod_reset_dsm(struct pci_dev *pdev, u16 cmd, u16 value) +{ +#ifdef CONFIG_ACPI + struct iwl_dsm_internal_product_reset_cmd pldr_arg = { + .cmd = cmd, + .value = value, + }; + union acpi_object arg = { + .buffer.type = ACPI_TYPE_BUFFER, + .buffer.length = sizeof(pldr_arg), + .buffer.pointer = (void *)&pldr_arg, + }; + static const guid_t dsm_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, + 0x81, 0x4F, 0x75, 0xE4, + 0xDD, 0x26, 0xB5, 0xFD); + + if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &dsm_guid, ACPI_DSM_REV, + DSM_INTERNAL_FUNC_PRODUCT_RESET)) + return ERR_PTR(-ENODEV); + + return iwl_acpi_get_dsm_object(&pdev->dev, ACPI_DSM_REV, + DSM_INTERNAL_FUNC_PRODUCT_RESET, + &arg, &dsm_guid); +#else + return ERR_PTR(-EOPNOTSUPP); +#endif +} + +void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev) +{ + union acpi_object *res; + + res = iwl_trans_pcie_call_prod_reset_dsm(pdev, + DSM_INTERNAL_PLDR_CMD_GET_MODE, + 0); + if (IS_ERR(res)) + return; + + if (res->type != ACPI_TYPE_INTEGER) + IWL_ERR_DEV(&pdev->dev, + "unexpected return type from product reset DSM\n"); + else + IWL_DEBUG_DEV_POWER(&pdev->dev, + "product reset mode is 0x%llx\n", + res->integer.value); + + ACPI_FREE(res); +} + +static void iwl_trans_pcie_set_product_reset(struct pci_dev *pdev, bool enable, + bool integrated) +{ + union acpi_object *res; + u16 mode = enable ? DSM_INTERNAL_PLDR_MODE_EN_PROD_RESET : 0; + + if (!integrated) + mode |= DSM_INTERNAL_PLDR_MODE_EN_WIFI_FLR | + DSM_INTERNAL_PLDR_MODE_EN_BT_OFF_ON; + + res = iwl_trans_pcie_call_prod_reset_dsm(pdev, + DSM_INTERNAL_PLDR_CMD_SET_MODE, + mode); + if (IS_ERR(res)) { + if (enable) + IWL_ERR_DEV(&pdev->dev, + "ACPI _DSM not available (%d), cannot do product reset\n", + (int)PTR_ERR(res)); + return; + } + + ACPI_FREE(res); + IWL_DEBUG_DEV_POWER(&pdev->dev, "%sabled product reset via DSM\n", + enable ? "En" : "Dis"); + iwl_trans_pcie_check_product_reset_mode(pdev); +} + +void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev) +{ + union acpi_object *res; + + res = iwl_trans_pcie_call_prod_reset_dsm(pdev, + DSM_INTERNAL_PLDR_CMD_GET_STATUS, + 0); + if (IS_ERR(res)) + return; + + if (res->type != ACPI_TYPE_INTEGER) + IWL_ERR_DEV(&pdev->dev, + "unexpected return type from product reset DSM\n"); + else + IWL_DEBUG_DEV_POWER(&pdev->dev, + "product reset status is 0x%llx\n", + res->integer.value); + + ACPI_FREE(res); +} + +static void iwl_trans_pcie_call_reset(struct pci_dev *pdev) +{ +#ifdef CONFIG_ACPI + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *p, *ref; + acpi_status status; + int ret = -EINVAL; + + status = acpi_evaluate_object(ACPI_HANDLE(&pdev->dev), + "_PRR", NULL, &buffer); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_DEV_POWER(&pdev->dev, "No _PRR method found\n"); + goto out; + } + p = buffer.pointer; + + if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) { + pci_err(pdev, "Bad _PRR return type\n"); + goto out; + } + + ref = &p->package.elements[0]; + if (ref->type != ACPI_TYPE_LOCAL_REFERENCE) { + pci_err(pdev, "_PRR wasn't a reference\n"); + goto out; + } + + status = acpi_evaluate_object(ref->reference.handle, + "_RST", NULL, NULL); + if (ACPI_FAILURE(status)) { + pci_err(pdev, + "Failed to call _RST on object returned by _PRR (%d)\n", + status); + goto out; + } + ret = 0; +out: + kfree(buffer.pointer); + if (!ret) { + IWL_DEBUG_DEV_POWER(&pdev->dev, "called _RST on _PRR object\n"); + return; + } + IWL_DEBUG_DEV_POWER(&pdev->dev, + "No BIOS support, using pci_reset_function()\n"); +#endif + pci_reset_function(pdev); +} + struct iwl_trans_pcie_removal { struct pci_dev *pdev; struct work_struct work; - bool rescan; + enum iwl_reset_mode mode; + bool integrated; }; static void iwl_trans_pcie_removal_wk(struct work_struct *wk) @@ -2125,14 +2274,66 @@ static void iwl_trans_pcie_removal_wk(struct work_struct *wk) if (!bus) goto out; - dev_err(&pdev->dev, "Device gone - attempting removal\n"); - kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); + if (removal->mode == IWL_RESET_MODE_PROD_RESET) { + struct pci_dev *bt = NULL; + + if (!removal->integrated) { + /* discrete devices have WiFi/BT at function 0/1 */ + int slot = PCI_SLOT(pdev->devfn); + int func = PCI_FUNC(pdev->devfn); + + if (func == 0) + bt = pci_get_slot(bus, PCI_DEVFN(slot, 1)); + else + pci_info(pdev, "Unexpected function %d\n", + func); + } else { + /* on integrated we have to look up by ID (same bus) */ + static const struct pci_device_id bt_device_ids[] = { +#define BT_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) } + BT_DEV(0xA876), /* LNL */ + BT_DEV(0xE476), /* PTL-P */ + BT_DEV(0xE376), /* PTL-H */ + BT_DEV(0xD346), /* NVL-H */ + BT_DEV(0x6E74), /* NVL-S */ + BT_DEV(0x4D76), /* WCL */ + BT_DEV(0xD246), /* RZL-H */ + BT_DEV(0x6C46), /* RZL-M */ + {} + }; + struct pci_dev *tmp = NULL; + + for_each_pci_dev(tmp) { + if (tmp->bus != bus) + continue; + + if (pci_match_id(bt_device_ids, tmp)) { + bt = tmp; + break; + } + } + } + + if (bt) { + pci_info(bt, "Removal by WiFi due to product reset\n"); + pci_stop_and_remove_bus_device(bt); + pci_dev_put(bt); + } + } + + iwl_trans_pcie_set_product_reset(pdev, + removal->mode == + IWL_RESET_MODE_PROD_RESET, + removal->integrated); + if (removal->mode >= IWL_RESET_MODE_FUNC_RESET) + iwl_trans_pcie_call_reset(pdev); + pci_stop_and_remove_bus_device(pdev); pci_dev_put(pdev); - if (removal->rescan) { + if (removal->mode >= IWL_RESET_MODE_RESCAN) { if (bus->parent) bus = bus->parent; pci_rescan_bus(bus); @@ -2145,14 +2346,27 @@ out: module_put(THIS_MODULE); } -void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan) +void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode) { struct iwl_trans_pcie_removal *removal; + char _msg = 0, *msg = &_msg; + + if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY)) + return; if (test_bit(STATUS_TRANS_DEAD, &trans->status)) return; - IWL_ERR(trans, "Device gone - scheduling removal!\n"); + if (trans->me_present && mode == IWL_RESET_MODE_PROD_RESET) { + mode = IWL_RESET_MODE_FUNC_RESET; + if (trans->me_present < 0) + msg = " instead of product reset as ME may be present"; + else + msg = " instead of product reset as ME is present"; + } + + IWL_INFO(trans, "scheduling reset (mode=%d%s)\n", mode, msg); + iwl_pcie_dump_csr(trans); /* @@ -2179,12 +2393,13 @@ void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan) set_bit(STATUS_TRANS_DEAD, &trans->status); removal->pdev = to_pci_dev(trans->dev); - removal->rescan = rescan; + removal->mode = mode; + removal->integrated = trans->trans_cfg->integrated; INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); pci_dev_get(removal->pdev); schedule_work(&removal->work); } -EXPORT_SYMBOL(iwl_trans_pcie_remove); +EXPORT_SYMBOL(iwl_trans_pcie_reset); /* * This version doesn't disable BHs but rather assumes they're @@ -2249,7 +2464,8 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) iwl_trans_pcie_dump_regs(trans); if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) - iwl_trans_pcie_remove(trans, false); + iwl_trans_pcie_reset(trans, + IWL_RESET_MODE_REMOVE_ONLY); else iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); @@ -3036,12 +3252,47 @@ static ssize_t iwl_dbgfs_rf_read(struct file *file, strlen(trans_pcie->rf_name)); } +static ssize_t iwl_dbgfs_reset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + static const char * const modes[] = { + [IWL_RESET_MODE_SW_RESET] = "n/a", + [IWL_RESET_MODE_REPROBE] = "n/a", + [IWL_RESET_MODE_REMOVE_ONLY] = "remove", + [IWL_RESET_MODE_RESCAN] = "rescan", + [IWL_RESET_MODE_FUNC_RESET] = "function", + [IWL_RESET_MODE_PROD_RESET] = "product", + }; + char buf[10] = {}; + int mode; + + if (count > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + mode = sysfs_match_string(modes, buf); + if (mode < 0) + return mode; + + if (mode < IWL_RESET_MODE_REMOVE_ONLY) + return -EINVAL; + + iwl_trans_pcie_reset(trans, mode); + + return count; +} + DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_FILE_OPS(fh_reg); DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_WRITE_FILE_OPS(csr); DEBUGFS_READ_WRITE_FILE_OPS(rfkill); DEBUGFS_READ_FILE_OPS(rf); +DEBUGFS_WRITE_FILE_OPS(reset); static const struct file_operations iwl_dbgfs_tx_queue_ops = { .owner = THIS_MODULE, @@ -3070,6 +3321,7 @@ void iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) DEBUGFS_ADD_FILE(rfkill, dir, 0600); DEBUGFS_ADD_FILE(monitor_data, dir, 0400); DEBUGFS_ADD_FILE(rf, dir, 0400); + DEBUGFS_ADD_FILE(reset, dir, 0200); } void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans) @@ -3534,7 +3786,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, struct iwl_trans_pcie *trans_pcie, **priv; struct iwl_trans *trans; int ret, addr_size; - void __iomem * const *table; u32 bar0; /* reassign our BAR 0 if invalid due to possible runtime PM races */ @@ -3567,6 +3818,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans->max_skb_frags = IWL_TRANS_PCIE_MAX_FRAGS(trans_pcie); + /* Set a short watchdog for the command queue */ + trans_pcie->txqs.cmd.wdg_timeout = IWL_DEF_WD_TIMEOUT; + trans_pcie->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page); if (!trans_pcie->txqs.tso_hdr_page) { ret = -ENOMEM; @@ -3657,22 +3911,15 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } - ret = pcim_iomap_regions_request_all(pdev, BIT(0), DRV_NAME); + ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) { - dev_err(&pdev->dev, "pcim_iomap_regions_request_all failed\n"); - goto out_no_pci; - } - - table = pcim_iomap_table(pdev); - if (!table) { - dev_err(&pdev->dev, "pcim_iomap_table failed\n"); - ret = -ENOMEM; + dev_err(&pdev->dev, "Requesting all PCI BARs failed.\n"); goto out_no_pci; } - trans_pcie->hw_base = table[0]; + trans_pcie->hw_base = pcim_iomap(pdev, 0, 0); if (!trans_pcie->hw_base) { - dev_err(&pdev->dev, "couldn't find IO mem in first BAR\n"); + dev_err(&pdev->dev, "Could not ioremap PCI BAR 0.\n"); ret = -ENODEV; goto out_no_pci; } diff --git a/pcie/tx-gen2.c b/pcie/tx-gen2.c index b1846abb99b7..401919f9fe88 100644 --- a/pcie/tx-gen2.c +++ b/pcie/tx-gen2.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020, 2023-2024 Intel Corporation + * Copyright (C) 2018-2020, 2023-2025 Intel Corporation */ #include <net/tso.h> #include <linux/tcp.h> @@ -188,7 +188,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)); /* Our device supports 9 segments at most, it will fit in 1 page */ - sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room); + sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room, + snap_ip_tcp_hdrlen + hdr_len); if (!sgt) return -ENOMEM; @@ -347,6 +348,7 @@ iwl_tfh_tfd *iwl_txq_gen2_build_tx_amsdu(struct iwl_trans *trans, return tfd; out_err: + iwl_pcie_free_tso_pages(trans, skb, out_meta); iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd); return NULL; } @@ -1298,7 +1300,9 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, spin_unlock_irqrestore(&txq->lock, flags); IWL_ERR(trans, "No space in command queue\n"); - iwl_op_mode_cmd_queue_full(trans->op_mode); + iwl_op_mode_nic_error(trans->op_mode, + IWL_ERR_TYPE_CMD_QUEUE_FULL); + iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_CMD_QUEUE_FULL); idx = -ENOSPC; goto free_dup_buf; } diff --git a/pcie/tx.c b/pcie/tx.c index 9fe050f0ddc1..7c1dd5cc084a 100644 --- a/pcie/tx.c +++ b/pcie/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2021, 2023-2024 Intel Corporation + * Copyright (C) 2003-2014, 2018-2021, 2023-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1449,7 +1449,9 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, spin_unlock_irqrestore(&txq->lock, flags); IWL_ERR(trans, "No space in command queue\n"); - iwl_op_mode_cmd_queue_full(trans->op_mode); + iwl_op_mode_nic_error(trans->op_mode, + IWL_ERR_TYPE_CMD_QUEUE_FULL); + iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_CMD_QUEUE_FULL); idx = -ENOSPC; goto free_dup_buf; } @@ -1853,6 +1855,7 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, * @cmd_meta: command meta to store the scatter list information for unmapping * @hdr: output argument for TSO headers * @hdr_room: requested length for TSO headers + * @offset: offset into the data from which mapping should start * * Allocate space for a scatter gather list and TSO headers and map the SKB * using the scatter gather list. The SKB is unmapped again when the page is @@ -1862,9 +1865,12 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, */ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta, - u8 **hdr, unsigned int hdr_room) + u8 **hdr, unsigned int hdr_room, + unsigned int offset) { struct sg_table *sgt; + unsigned int n_segments = skb_shinfo(skb)->nr_frags + 1; + int orig_nents; if (WARN_ON_ONCE(skb_has_frag_list(skb))) return NULL; @@ -1872,8 +1878,7 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, *hdr = iwl_pcie_get_page_hdr(trans, hdr_room + __alignof__(struct sg_table) + sizeof(struct sg_table) + - (skb_shinfo(skb)->nr_frags + 1) * - sizeof(struct scatterlist), + n_segments * sizeof(struct scatterlist), skb); if (!*hdr) return NULL; @@ -1881,14 +1886,15 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, sgt = (void *)PTR_ALIGN(*hdr + hdr_room, __alignof__(struct sg_table)); sgt->sgl = (void *)(sgt + 1); - sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1); + sg_init_table(sgt->sgl, n_segments); /* Only map the data, not the header (it is copied to the TSO page) */ - sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb), - skb->data_len); - if (WARN_ON_ONCE(sgt->orig_nents <= 0)) + orig_nents = skb_to_sgvec(skb, sgt->sgl, offset, skb->len - offset); + if (WARN_ON_ONCE(orig_nents <= 0)) return NULL; + sgt->orig_nents = orig_nents; + /* And map the entire SKB */ if (dma_map_sgtable(trans->dev, sgt, DMA_TO_DEVICE, 0) < 0) return NULL; @@ -1937,7 +1943,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len; /* Our device supports 9 segments at most, it will fit in 1 page */ - sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room); + sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room, + snap_ip_tcp_hdrlen + hdr_len + iv_len); if (!sgt) return -ENOMEM; @@ -2351,6 +2358,10 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, txq_write_ptr = txq->write_ptr; spin_unlock(&txq->lock); + /* There is nothing to do if we are flushing an empty queue */ + if (is_flush && txq_write_ptr == txq_read_ptr) + goto out; + read_ptr = iwl_txq_get_cmd_index(txq, txq_read_ptr); if (!test_bit(txq_id, trans_pcie->txqs.queue_used)) { diff --git a/tests/devinfo.c b/tests/devinfo.c index 7361b6d0cdb8..d0bda23c628a 100644 --- a/tests/devinfo.c +++ b/tests/devinfo.c @@ -9,7 +9,7 @@ #include "iwl-drv.h" #include "iwl-config.h" -MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di) { |