mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
iwlwifi - next: 2026-05-26
This contains mainly: UHR support (DPS, DUO, multi-link PM), NAN enhancements (multicast, schedule config v2, multiple stations), EMLSR fixes, new Killer/LNL device IDs, firmware API cleanups, and a few bugfixes -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQM3A3Pv7vbm9vtjWbacY7uyt+OfQUCahWQRAAKCRDacY7uyt+O fSziAP9kJeU1HvEsGF4k5GHP65F/eLvq7hVrQGfx7Q/vP+ZnkAD+O3SZsyoL8mL1 LAr+ixjuMQ6yLd5xonTJvvtyPQwmVAw= =SpPv -----END PGP SIGNATURE----- Merge tag 'iwlwifi-next-2026-05-26' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next Miri Korenblit says: ==================== iwlwifi - next: 2026-05-26 This contains mainly: UHR support (DPS, DUO, multi-link PM), NAN enhancements (multicast, schedule config v2, multiple stations), EMLSR fixes, new Killer/LNL device IDs, firmware API cleanups, and a few bugfixes ==================== Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
commit
9658d9f243
|
|
@ -32,7 +32,7 @@ iwlwifi-objs += iwl-dbg-tlv.o
|
|||
iwlwifi-objs += iwl-trans.o
|
||||
|
||||
iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o
|
||||
iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o
|
||||
iwlwifi-objs += fw/dbg.o fw/dbg-old.o fw/pnvm.o fw/dump.o
|
||||
iwlwifi-objs += fw/regulatory.o
|
||||
iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o
|
||||
iwlwifi-$(CONFIG_IWLMLD) += fw/smem.o fw/init.o
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -15,26 +15,26 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_22000_UCODE_API_MIN 77
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_22000_SMEM_OFFSET 0x400000
|
||||
#define IWL_22000_SMEM_LEN 0xD0000
|
||||
|
||||
#define IWL_CC_A_FW_PRE "iwlwifi-cc-a0"
|
||||
#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0"
|
||||
#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0"
|
||||
#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0"
|
||||
|
||||
#define IWL_CC_A_MODULE_FIRMWARE(api) \
|
||||
IWL_CC_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
static const struct iwl_family_base_params iwl_22000_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 256,
|
||||
.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,
|
||||
.smem_offset = IWL_22000_SMEM_OFFSET,
|
||||
.smem_len = IWL_22000_SMEM_LEN,
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.apmg_not_supported = true,
|
||||
.mac_addr_from_csr = 0x380,
|
||||
|
|
@ -113,4 +113,7 @@ const char iwl_ax201_killer_1650s_name[] =
|
|||
const char iwl_ax201_killer_1650i_name[] =
|
||||
"Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)";
|
||||
|
||||
MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2020, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2020, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -53,10 +53,7 @@ static const struct iwl_family_base_params iwl7000_base = {
|
|||
.eeprom_size = OTP_LOW_IMAGE_SIZE_16K,
|
||||
.num_of_queues = 31,
|
||||
.max_tfd_queue_size = 256,
|
||||
.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,
|
||||
.apmg_wake_up_wa = true,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2014, 2018-2020, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2014, 2018-2020, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -39,10 +39,7 @@ static const struct iwl_family_base_params iwl8000_base = {
|
|||
.eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
|
||||
.num_of_queues = 31,
|
||||
.max_tfd_queue_size = 256,
|
||||
.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,
|
||||
.nvm_hw_section_num = 10,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2021, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2018-2021, 2023, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -30,10 +30,7 @@ static const struct iwl_family_base_params iwl9000_base = {
|
|||
.eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
|
||||
.num_of_queues = 31,
|
||||
.max_tfd_queue_size = 256,
|
||||
.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,
|
||||
.smem_offset = IWL9000_SMEM_OFFSET,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -15,21 +15,30 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_AX210_UCODE_API_MIN 89
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_AX210_SMEM_OFFSET 0x400000
|
||||
#define IWL_AX210_SMEM_LEN 0xD0000
|
||||
#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_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0"
|
||||
#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0"
|
||||
#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0"
|
||||
#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0"
|
||||
#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0"
|
||||
#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0"
|
||||
#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0"
|
||||
#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0"
|
||||
|
||||
#define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
static const struct iwl_family_base_params iwl_ax210_base = {
|
||||
.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,
|
||||
.smem_offset = IWL_AX210_SMEM_OFFSET,
|
||||
.smem_len = IWL_AX210_SMEM_LEN,
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.apmg_not_supported = true,
|
||||
.mac_addr_from_csr = 0x380,
|
||||
|
|
@ -121,3 +130,14 @@ const struct iwl_mac_cfg iwl_ma_mac_cfg = {
|
|||
.integrated = true,
|
||||
.umac_prph_offset = 0x300000
|
||||
};
|
||||
|
||||
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);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SO_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_B_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -10,26 +10,17 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_BZ_UCODE_CORE_MAX 102
|
||||
#define IWL_BZ_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_BZ_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_BZ_SMEM_OFFSET 0x400000
|
||||
#define IWL_BZ_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_BZ_UCODE_CORE_MIN 101
|
||||
|
||||
static const struct iwl_family_base_params iwl_bz_base = {
|
||||
.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,
|
||||
.smem_offset = IWL_BZ_SMEM_OFFSET,
|
||||
.smem_len = IWL_BZ_SMEM_LEN,
|
||||
.apmg_not_supported = true,
|
||||
.mac_addr_from_csr = 0x30,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -69,7 +60,7 @@ static const struct iwl_family_base_params iwl_bz_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_BZ_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_bz_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -9,28 +9,19 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_DR_UCODE_CORE_MAX 102
|
||||
#define IWL_DR_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_DR_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_DR_SMEM_OFFSET 0x400000
|
||||
#define IWL_DR_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_DR_UCODE_CORE_MIN 101
|
||||
|
||||
#define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0"
|
||||
|
||||
static const struct iwl_family_base_params iwl_dr_base = {
|
||||
.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,
|
||||
.smem_offset = IWL_DR_SMEM_OFFSET,
|
||||
.smem_len = IWL_DR_SMEM_LEN,
|
||||
.apmg_not_supported = true,
|
||||
.mac_addr_from_csr = 0x30,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -70,7 +61,7 @@ static const struct iwl_family_base_params iwl_dr_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_DR_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_dr_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
|
|
@ -11,9 +11,6 @@
|
|||
#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0"
|
||||
#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_FM_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_FM \
|
||||
.ht_params = { \
|
||||
.stbc = true, \
|
||||
|
|
@ -27,7 +24,6 @@
|
|||
.uhb_supported = true, \
|
||||
.eht_supported = true, \
|
||||
.num_rbds = IWL_NUM_RBDS_EHT, \
|
||||
.nvm_ver = IWL_FM_NVM_VERSION, \
|
||||
.nvm_type = IWL_NVM_EXT
|
||||
|
||||
const struct iwl_rf_cfg iwl_rf_fm = {
|
||||
|
|
@ -51,6 +47,8 @@ const char iwl_killer_be1790s_name[] =
|
|||
"Killer(R) Wi-Fi 7 BE1790s 320MHz Wireless Network Adapter (BE401D2W)";
|
||||
const char iwl_killer_be1790i_name[] =
|
||||
"Killer(R) Wi-Fi 7 BE1790i 320MHz Wireless Network Adapter (BE401NGW)";
|
||||
const char iwl_killer_be1730x_name[] =
|
||||
"Killer(TM) Wi-Fi 7 BE1730x 160MHz Wireless Network Adapter (BE202)";
|
||||
|
||||
const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz";
|
||||
const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz";
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_GF_UCODE_API_MIN 100
|
||||
|
||||
#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_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0"
|
||||
#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0"
|
||||
#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0"
|
||||
#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0"
|
||||
#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0"
|
||||
#define IWL_BZ_A_GF_A_FW_PRE "iwlwifi-bz-a0-gf-a0"
|
||||
#define IWL_BZ_A_GF4_A_FW_PRE "iwlwifi-bz-a0-gf4-a0"
|
||||
#define IWL_SC_A_GF_A_FW_PRE "iwlwifi-sc-a0-gf-a0"
|
||||
|
|
@ -35,9 +28,6 @@
|
|||
#define IWL_SC_A_GF4_A_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_GF_NVM_VERSION 0x0a1d
|
||||
|
||||
const struct iwl_rf_cfg iwl_rf_gf = {
|
||||
.uhb_supported = true,
|
||||
.led_mode = IWL_LED_RF_STATE,
|
||||
|
|
@ -49,7 +39,6 @@ const struct iwl_rf_cfg iwl_rf_gf = {
|
|||
.ht40_bands = BIT(NL80211_BAND_2GHZ) |
|
||||
BIT(NL80211_BAND_5GHZ),
|
||||
},
|
||||
.nvm_ver = IWL_GF_NVM_VERSION,
|
||||
.nvm_type = IWL_NVM_EXT,
|
||||
.num_rbds = IWL_NUM_RBDS_HE,
|
||||
.ucode_api_min = IWL_GF_UCODE_API_MIN,
|
||||
|
|
@ -73,12 +62,6 @@ const char iwl_ax210_name[] = "Intel(R) Wi-Fi 6E AX210 160MHz";
|
|||
const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz";
|
||||
const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz";
|
||||
|
||||
IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
|
|
@ -11,28 +11,10 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_HR_UCODE_API_MIN 100
|
||||
|
||||
#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0"
|
||||
#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0"
|
||||
#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0"
|
||||
#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0"
|
||||
#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0"
|
||||
#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0"
|
||||
#define IWL_BZ_A_HR_B_FW_PRE "iwlwifi-bz-a0-hr-b0"
|
||||
#define IWL_SC_A_HR_A_FW_PRE "iwlwifi-sc-a0-hr-b0"
|
||||
#define IWL_SC_A_HR_B_FW_PRE "iwlwifi-sc-a0-hr-b0"
|
||||
|
||||
#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_C_HR_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_MA_A_HR_B_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_A_HR_B_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_BZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(api) \
|
||||
|
|
@ -40,9 +22,6 @@
|
|||
#define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_HR_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_HR \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.non_shared_ant = ANT_B, \
|
||||
|
|
@ -54,7 +33,6 @@
|
|||
BIT(NL80211_BAND_5GHZ), \
|
||||
}, \
|
||||
.num_rbds = IWL_NUM_RBDS_HE, \
|
||||
.nvm_ver = IWL_HR_NVM_VERSION, \
|
||||
.nvm_type = IWL_NVM_EXT, \
|
||||
.ucode_api_min = IWL_HR_UCODE_API_MIN, \
|
||||
.ucode_api_max = IWL_HR_UCODE_API_MAX
|
||||
|
|
@ -78,12 +56,6 @@ const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz";
|
|||
const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz";
|
||||
const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203";
|
||||
|
||||
MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
|
||||
#define IWL_DEVICE_PE \
|
||||
.ht_params = { \
|
||||
.stbc = true, \
|
||||
.ldpc = true, \
|
||||
.ht40_bands = BIT(NL80211_BAND_2GHZ) | \
|
||||
BIT(NL80211_BAND_5GHZ), \
|
||||
}, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.non_shared_ant = ANT_B, \
|
||||
.vht_mu_mimo_supported = true, \
|
||||
.uhb_supported = true, \
|
||||
.eht_supported = true, \
|
||||
.uhr_supported = true, \
|
||||
.num_rbds = IWL_NUM_RBDS_EHT, \
|
||||
.nvm_type = IWL_NVM_EXT
|
||||
|
||||
const struct iwl_rf_cfg iwl_rf_pe = {
|
||||
IWL_DEVICE_PE,
|
||||
};
|
||||
|
||||
const char iwl_killer_bn1850w2_name[] =
|
||||
"Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_WH_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_WH \
|
||||
.ht_params = { \
|
||||
.stbc = true, \
|
||||
|
|
@ -19,7 +16,6 @@
|
|||
.vht_mu_mimo_supported = true, \
|
||||
.uhb_supported = true, \
|
||||
.num_rbds = IWL_NUM_RBDS_EHT, \
|
||||
.nvm_ver = IWL_WH_NVM_VERSION, \
|
||||
.nvm_type = IWL_NVM_EXT
|
||||
|
||||
/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
|
||||
|
|
@ -33,6 +29,8 @@ const char iwl_killer_be1775s_name[] =
|
|||
"Killer(R) Wi-Fi 7 BE1775s 320MHz Wireless Network Adapter (BE211D2W)";
|
||||
const char iwl_killer_be1775i_name[] =
|
||||
"Killer(R) Wi-Fi 7 BE1775i 320MHz Wireless Network Adapter (BE211NGW)";
|
||||
const char iwl_killer_be1735x_name[] =
|
||||
"Killer(TM) Wi-Fi 7 BE1735x 160MHz Wireless Network Adapter (BE213)";
|
||||
|
||||
const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz";
|
||||
const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -10,17 +10,10 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_SC_UCODE_CORE_MAX 102
|
||||
#define IWL_SC_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_SC_UCODE_API_MIN 100
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_SC_NVM_VERSION 0x0a1d
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_SC_SMEM_OFFSET 0x400000
|
||||
#define IWL_SC_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_SC_UCODE_CORE_MIN 101
|
||||
|
||||
#define IWL_SC_A_FM_B_FW_PRE "iwlwifi-sc-a0-fm-b0"
|
||||
#define IWL_SC_A_FM_C_FW_PRE "iwlwifi-sc-a0-fm-c0"
|
||||
|
|
@ -31,14 +24,9 @@
|
|||
static const struct iwl_family_base_params iwl_sc_base = {
|
||||
.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,
|
||||
.smem_offset = IWL_SC_SMEM_OFFSET,
|
||||
.smem_len = IWL_SC_SMEM_LEN,
|
||||
.apmg_not_supported = true,
|
||||
.mac_addr_from_csr = 0x30,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -78,7 +66,7 @@ static const struct iwl_family_base_params iwl_sc_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_SC_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_sc_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2022, 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2022, 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_fw_api_commands_h__
|
||||
#define __iwl_fw_api_commands_h__
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
* &enum iwl_data_path_subcmd_ids
|
||||
* @SCAN_GROUP: scan group, uses command IDs from
|
||||
* &enum iwl_scan_subcmd_ids
|
||||
* @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
|
||||
|
|
@ -43,7 +42,6 @@ enum iwl_mvm_command_groups {
|
|||
PHY_OPS_GROUP = 0x4,
|
||||
DATA_PATH_GROUP = 0x5,
|
||||
SCAN_GROUP = 0x6,
|
||||
NAN_GROUP = 0x7,
|
||||
LOCATION_GROUP = 0x8,
|
||||
BT_COEX_GROUP = 0x9,
|
||||
PROT_OFFLOAD_GROUP = 0xb,
|
||||
|
|
@ -59,8 +57,7 @@ enum iwl_legacy_cmds {
|
|||
/**
|
||||
* @UCODE_ALIVE_NTFY:
|
||||
* Alive data from the firmware, as described in
|
||||
* &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v4 or
|
||||
* &struct iwl_alive_ntf_v5 or &struct iwl_alive_ntf_v7.
|
||||
* &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v7.
|
||||
*/
|
||||
UCODE_ALIVE_NTFY = 0x1,
|
||||
|
||||
|
|
@ -386,7 +383,7 @@ enum iwl_legacy_cmds {
|
|||
* @STATISTICS_NOTIFICATION:
|
||||
* one of &struct iwl_notif_statistics_v10,
|
||||
* &struct iwl_notif_statistics_v11,
|
||||
* &struct iwl_notif_statistic,
|
||||
* &struct iwl_notif_statistics,
|
||||
* &struct iwl_statistics_operational_ntfy_ver_14
|
||||
* &struct iwl_statistics_operational_ntfy
|
||||
*/
|
||||
|
|
@ -560,7 +557,7 @@ enum iwl_legacy_cmds {
|
|||
WOWLAN_CONFIGURATION = 0xe1,
|
||||
|
||||
/**
|
||||
* @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_v4,
|
||||
* @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_ver_2,
|
||||
* &struct iwl_wowlan_rsc_tsc_params_cmd
|
||||
*/
|
||||
WOWLAN_TSC_RSC_PARAM = 0xe2,
|
||||
|
|
@ -653,7 +650,7 @@ enum iwl_system_subcmd_ids {
|
|||
enum iwl_statistics_subcmd_ids {
|
||||
/**
|
||||
* @STATISTICS_OPER_NOTIF: Notification about operational
|
||||
* statistics &struct iwl_system_statistics_notif_oper
|
||||
* statistics &struct iwl_system_statistics_notif_oper_v3
|
||||
*/
|
||||
STATISTICS_OPER_NOTIF = 0x0,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2022 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
|
|
@ -91,6 +91,13 @@ enum iwl_data_path_subcmd_ids {
|
|||
*/
|
||||
SEC_KEY_CMD = 0x18,
|
||||
|
||||
/**
|
||||
* @RSC_NOTIF: notification to update each Rx queue with the RSC. This
|
||||
* notification is sent after resume and uses
|
||||
* &struct iwl_wowlan_all_rsc_tsc_v5.
|
||||
*/
|
||||
RSC_NOTIF = 0xF1,
|
||||
|
||||
/**
|
||||
* @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode,
|
||||
* uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2022 Intel Corporation
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_fw_api_location_h__
|
||||
#define __iwl_fw_api_location_h__
|
||||
|
|
@ -81,12 +81,53 @@ enum iwl_location_subcmd_ids {
|
|||
* @TOF_RANGE_RESPONSE_NOTIF: ranging response, using one of
|
||||
* &struct iwl_tof_range_rsp_ntfy_v5,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v6,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v7 or
|
||||
* &struct iwl_tof_range_rsp_ntfy_v8
|
||||
* &struct iwl_tof_range_rsp_ntfy_v7,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v9 or
|
||||
* &struct iwl_tof_range_rsp_ntfy
|
||||
*/
|
||||
TOF_RANGE_RESPONSE_NOTIF = 0xFF,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_frame_format - location frame formats
|
||||
* @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HT: HT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_VHT: VHT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HE: HE
|
||||
*/
|
||||
enum iwl_location_frame_format {
|
||||
IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_bw - location bandwidth selection
|
||||
* @IWL_LOCATION_BW_20MHZ: 20 MHz
|
||||
* @IWL_LOCATION_BW_40MHZ: 40 MHz
|
||||
* @IWL_LOCATION_BW_80MHZ: 80 MHz
|
||||
* @IWL_LOCATION_BW_160MHZ: 160 MHz
|
||||
* @IWL_LOCATION_BW_320MHZ: 320 MHz
|
||||
*/
|
||||
enum iwl_location_bw {
|
||||
IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_BW_320MHZ,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_format_bw - format/BW encoding
|
||||
* @IWL_LOCATION_FMT_BW_FORMAT: &enum iwl_location_frame_format
|
||||
* @IWL_LOCATION_FMT_BW_BANDWIDTH: &enum iwl_location_bw
|
||||
*/
|
||||
enum iwl_location_format_bw {
|
||||
IWL_LOCATION_FMT_BW_FORMAT = 0x0f,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH = 0xf0,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tof_config_cmd - ToF configuration
|
||||
* @tof_disabled: indicates if ToF is disabled (or not)
|
||||
|
|
@ -263,8 +304,7 @@ struct iwl_tof_responder_config_cmd_v6 {
|
|||
* struct iwl_tof_responder_config_cmd_v7 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @rate: current AP rate
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -302,8 +342,7 @@ struct iwl_tof_responder_config_cmd_v7 {
|
|||
* struct iwl_tof_responder_config_cmd_v8 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @rate: current AP rate
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -348,8 +387,7 @@ struct iwl_tof_responder_config_cmd_v8 {
|
|||
* struct iwl_tof_responder_config_cmd_v9 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @bss_color: current AP bss_color
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -400,8 +438,7 @@ struct iwl_tof_responder_config_cmd_v9 {
|
|||
* struct iwl_tof_responder_config_cmd - ToF AP mode
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @bss_color: current AP bss_color
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -676,44 +713,13 @@ struct iwl_tof_range_req_ap_entry_v3 {
|
|||
__le32 tsf_delta;
|
||||
} __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_3 */
|
||||
|
||||
/**
|
||||
* enum iwl_location_frame_format - location frame formats
|
||||
* @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HT: HT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_VHT: VHT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HE: HE
|
||||
*/
|
||||
enum iwl_location_frame_format {
|
||||
IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_bw - location bandwidth selection
|
||||
* @IWL_LOCATION_BW_20MHZ: 20MHz
|
||||
* @IWL_LOCATION_BW_40MHZ: 40MHz
|
||||
* @IWL_LOCATION_BW_80MHZ: 80MHz
|
||||
* @IWL_LOCATION_BW_160MHZ: 160MHz
|
||||
*/
|
||||
enum iwl_location_bw {
|
||||
IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_BW_160MHZ,
|
||||
};
|
||||
|
||||
#define TK_11AZ_LEN 32
|
||||
|
||||
#define LOCATION_BW_POS 4
|
||||
|
||||
/**
|
||||
* struct iwl_tof_range_req_ap_entry_v4 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -763,8 +769,7 @@ enum iwl_location_cipher {
|
|||
* struct iwl_tof_range_req_ap_entry_v6 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -810,8 +815,7 @@ struct iwl_tof_range_req_ap_entry_v6 {
|
|||
* struct iwl_tof_range_req_ap_entry_v7 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -868,8 +872,7 @@ struct iwl_tof_range_req_ap_entry_v7 {
|
|||
* struct iwl_tof_range_req_ap_entry_v8 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -939,8 +942,7 @@ struct iwl_tof_range_req_ap_entry_v8 {
|
|||
* struct iwl_tof_range_req_ap_entry_v9 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -1024,8 +1026,7 @@ struct iwl_tof_range_req_ap_entry_v9 {
|
|||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz, 2 for 6GHz
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @bssid: AP's BSSID
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define __iwl_fw_api_mac_cfg_h__
|
||||
|
||||
#include "mac.h"
|
||||
#include "phy-ctxt.h"
|
||||
|
||||
/**
|
||||
* enum iwl_mac_conf_subcmd_ids - mac configuration command IDs
|
||||
|
|
@ -71,6 +72,23 @@ enum iwl_mac_conf_subcmd_ids {
|
|||
* @NAN_CFG_CMD: &struct iwl_nan_config_cmd
|
||||
*/
|
||||
NAN_CFG_CMD = 0x12,
|
||||
/**
|
||||
* @NAN_SCHEDULE_CMD: &struct iwl_nan_schedule_cmd
|
||||
*/
|
||||
NAN_SCHEDULE_CMD = 0x13,
|
||||
/**
|
||||
* @NAN_PEER_CMD: &struct iwl_nan_peer_cmd
|
||||
*/
|
||||
NAN_PEER_CMD = 0x14,
|
||||
/**
|
||||
* @NAN_ULW_ATTR_NOTIF: &struct iwl_nan_ulw_attr_notif
|
||||
*/
|
||||
NAN_ULW_ATTR_NOTIF = 0xf2,
|
||||
/**
|
||||
* @NAN_SCHED_UPDATE_COMPLETED_NOTIF:
|
||||
* &struct iwl_nan_sched_update_completed_notif
|
||||
*/
|
||||
NAN_SCHED_UPDATE_COMPLETED_NOTIF = 0xf3,
|
||||
/**
|
||||
* @NAN_DW_END_NOTIF: &struct iwl_nan_dw_end_notif
|
||||
*/
|
||||
|
|
@ -536,12 +554,20 @@ enum iwl_link_ctx_protection_flags {
|
|||
* radar pulses).
|
||||
* @LINK_FLG_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change
|
||||
* of threshold
|
||||
* @LINK_FLG_NPCA: NPCA enabled
|
||||
* @LINK_FLG_DPS: AP is a DPS assisting AP
|
||||
* @LINK_FLG_MLPM: AP supports UHR multi-link PM
|
||||
* @LINK_FLG_DUO: AP supports UHR DUO
|
||||
*/
|
||||
enum iwl_link_ctx_flags {
|
||||
LINK_FLG_BSS_COLOR_DIS = BIT(0),
|
||||
LINK_FLG_MU_EDCA_CW = BIT(1),
|
||||
LINK_FLG_RU_2MHZ_BLOCK = BIT(2),
|
||||
LINK_FLG_NDP_FEEDBACK_ENABLED = BIT(3),
|
||||
LINK_FLG_NPCA = BIT(4),
|
||||
LINK_FLG_DPS = BIT(6),
|
||||
LINK_FLG_MLPM = BIT(7),
|
||||
LINK_FLG_DUO = BIT(8),
|
||||
}; /* LINK_CONTEXT_FLAG_E_VER_1 */
|
||||
|
||||
/**
|
||||
|
|
@ -578,7 +604,7 @@ enum iwl_npca_flags {
|
|||
* @initial_qsrc: Indicates the value that is used to initialize the
|
||||
* EDCAF QSRC[AC] variables
|
||||
* @min_dur_threshold: minimum PPDU time to switch to the non-primary
|
||||
* NPCA channel (usec)
|
||||
* NPCA channel (spec representation)
|
||||
* @flags: NPCA flags, see &enum iwl_npca_flags
|
||||
* @reserved: reserved for alignment purposes
|
||||
*/
|
||||
|
|
@ -726,6 +752,12 @@ struct iwl_link_config_cmd {
|
|||
* @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
|
||||
* of this type can have any number of links (even none) set in the
|
||||
* link_mask. (Supported since version 3.)
|
||||
* @STATION_TYPE_NAN_BCAST: NAN station used for synchronization and
|
||||
* discovery. No queue is associated with this station.
|
||||
* @STATION_TYPE_NAN_MGMT: NAN station used for NAN management frames, e.g.,
|
||||
* SDFs and NAFs.
|
||||
* @STATION_TYPE_NAN_MCAST_DATA: NAN station used for multicast NAN data
|
||||
* frames.
|
||||
* @STATION_TYPE_MAX: maximum number of FW station types
|
||||
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
|
||||
* for the aux sta, so this type is only for driver - internal use.
|
||||
|
|
@ -736,6 +768,9 @@ enum iwl_fw_sta_type {
|
|||
STATION_TYPE_MCAST,
|
||||
STATION_TYPE_NAN_PEER_NMI,
|
||||
STATION_TYPE_NAN_PEER_NDI,
|
||||
STATION_TYPE_NAN_BCAST,
|
||||
STATION_TYPE_NAN_MGMT,
|
||||
STATION_TYPE_NAN_MCAST_DATA,
|
||||
STATION_TYPE_MAX,
|
||||
STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
|
||||
}; /* STATION_TYPE_E_VER_1, _VER_2 */
|
||||
|
|
@ -873,7 +908,9 @@ struct iwl_sta_cfg_cmd_v2 {
|
|||
* ( STA_CONFIG_CMD = 0xA )
|
||||
*
|
||||
* @sta_id: index of station in uCode's station table
|
||||
* @link_mask: bitmap of link FW IDs used with this STA
|
||||
* @link_mask: bitmap of link FW IDs used with this STA. Should be set to 0
|
||||
* for STATION_TYPE_NAN_BCAST and STATION_TYPE_NAN_MGMT as they are not
|
||||
* associated with any link added by the driver.
|
||||
* @peer_mld_address: the peers mld address
|
||||
* @reserved_for_peer_mld_address: reserved
|
||||
* @peer_link_address: the address of the link that is used to communicate
|
||||
|
|
@ -908,7 +945,8 @@ struct iwl_sta_cfg_cmd_v2 {
|
|||
* @mic_prep_pad_delay: MIC prep time padding
|
||||
* @mic_compute_pad_delay: MIC compute time padding
|
||||
* @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to
|
||||
* @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address
|
||||
* @ndi_local_addr: for an NDI peer STA or NAN multicast data station,
|
||||
* the local NDI interface MAC address
|
||||
* @reserved: Reserved for alignment
|
||||
*/
|
||||
struct iwl_sta_cfg_cmd {
|
||||
|
|
@ -1204,7 +1242,8 @@ enum iwl_nan_flags {
|
|||
* @discovery_beacon_interval: discovery beacon interval in TUs
|
||||
* @cluster_id: lower last two bytes of the cluster ID, in case the local
|
||||
* device starts a cluster
|
||||
* @sta_id: station ID of the NAN station
|
||||
* @sta_id: station ID of the NAN station. Used only in version 1, in version 2
|
||||
* it is reserved.
|
||||
* @hb_channel: channel for 5 GHz if the device supports operation on 5 GHz.
|
||||
* Valid values are 44 and 149, which correspond to the 5 GHz channel, and
|
||||
* 0 which means that NAN operation on the 5 GHz band is disabled.
|
||||
|
|
@ -1242,7 +1281,102 @@ struct iwl_nan_config_cmd {
|
|||
__le32 nan_attr_len;
|
||||
__le32 nan_vendor_elems_len;
|
||||
u8 beacon_data[];
|
||||
} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */
|
||||
} __packed; /* NAN_CONFIG_CMD_API_S_VER_1, NAN_CONFIG_CMD_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_nan_schedule_cmd_v1 - NAN schedule command
|
||||
* @channels: per channel information
|
||||
* @channels.availability_map: bitmap of slots this channel is advertising
|
||||
* availability on, will be ULW'ed out if no link/inactive link is
|
||||
* referenced by the link ID below
|
||||
* @channels.channel_entry: NAN channel entry descriptor
|
||||
* @channels.link_id: FW link ID, or %0xFF for unset
|
||||
* @channels.reserved: (reserved)
|
||||
*/
|
||||
struct iwl_nan_schedule_cmd_v1 {
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 reserved;
|
||||
} __packed channels[NUM_PHY_CTX];
|
||||
} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_1 */
|
||||
|
||||
#define IWL_MAX_AVAILABILITY_ATTR_LEN 54
|
||||
|
||||
/**
|
||||
* struct iwl_nan_schedule_cmd - NAN schedule command
|
||||
* @channels: per channel information
|
||||
* @channels.availability_map: bitmap of slots this channel is advertising
|
||||
* availability on, will be ULW'ed out if no link/inactive link is
|
||||
* referenced by the link ID below
|
||||
* @channels.channel_entry: NAN channel entry descriptor
|
||||
* @channels.link_id: FW link ID, or %0xFF for unset
|
||||
* @channels.reserved: (reserved)
|
||||
* @avail_attr: NAN availability attribute information
|
||||
* @avail_attr.attr_len: length of the availability attribute
|
||||
* @avail_attr.reserved: reserved
|
||||
* @avail_attr.attr: the availability attribute including the attribute header
|
||||
* @deferred: true if the firmware should defer applying the schedule until
|
||||
* notifying all peers. For a deferred schedule update, the firmware should
|
||||
* send a notification to the driver after the new schedule is applied.
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_nan_schedule_cmd {
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 reserved;
|
||||
} __packed channels[NUM_PHY_CTX];
|
||||
|
||||
struct {
|
||||
u8 attr_len;
|
||||
u8 reserved;
|
||||
u8 attr[IWL_MAX_AVAILABILITY_ATTR_LEN];
|
||||
} __packed avail_attr;
|
||||
|
||||
u8 deferred;
|
||||
u8 reserved[3];
|
||||
} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_nan_peer_cmd - NAN peer command
|
||||
* @nmi_sta_id: NAN management station ID
|
||||
* @sequence_id: NAN Availability attribute sequence ID
|
||||
* @committed_dw_info: committed DW info from the NAN Device
|
||||
* Capability attribute
|
||||
* @max_channel_switch_time: maximum channel switch time
|
||||
* (in microseconds); 0 means unavailable
|
||||
* @reserved: (reserved)
|
||||
* @per_phy: per-PHY information for this peer, indexed by PHY ID
|
||||
* @per_phy.availability_map: bitmap of which slots this peer
|
||||
* is available in on this PHY. 0 indicates the this per-PHY entry
|
||||
* is unused.
|
||||
* @per_phy.channel_entry: the channel description the peer is using,
|
||||
* used for comparisons in ULW management
|
||||
* @per_phy.link_id: FW link ID, should be a valid id.
|
||||
* @per_phy.map_id: map ID from peer's NAN Availability attributec
|
||||
* @initial_ulw_size: size of the initial ULW blob
|
||||
* @initial_ulw: initial ULW data from the peer
|
||||
*/
|
||||
struct iwl_nan_peer_cmd {
|
||||
u8 nmi_sta_id;
|
||||
u8 sequence_id;
|
||||
__le16 committed_dw_info;
|
||||
__le16 max_channel_switch_time;
|
||||
__le16 reserved;
|
||||
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 map_id;
|
||||
} __packed per_phy[NUM_PHY_CTX];
|
||||
|
||||
__le32 initial_ulw_size;
|
||||
u8 initial_ulw[];
|
||||
} __packed; /* NAN_PEER_SCHEDULE_CMD_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* enum iwl_nan_cluster_notif_flags - flags for the cluster notification
|
||||
|
|
@ -1280,4 +1414,44 @@ struct iwl_nan_dw_end_notif {
|
|||
u8 reserved[3];
|
||||
} __packed; /* NAN_DW_END_NTF_API_S_VER_1 */
|
||||
|
||||
#define IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN 48
|
||||
|
||||
/**
|
||||
* struct iwl_nan_ulw_attr_notif - sent to notify the host of a change in the
|
||||
* ULW attribute
|
||||
*
|
||||
* @attr_len: length of the ULW attribute in bytes
|
||||
* @reserved: reserved
|
||||
* @attr: the ULW attribute including the attribute header
|
||||
*/
|
||||
struct iwl_nan_ulw_attr_notif {
|
||||
u8 attr_len;
|
||||
u8 reserved[3];
|
||||
u8 attr[IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN];
|
||||
} __packed; /* NAN_ULW_ATTR_NOTIF_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* enum iwl_nan_sched_update_status - NAN schedule update status
|
||||
*
|
||||
* @IWL_NAN_SCHED_UPDATE_SUCCESS: schedule update completed successfully
|
||||
* @IWL_NAN_SCHED_UPDATE_FAILURE: schedule update failed. Currently not expected
|
||||
* to happen, but reserved for future use.
|
||||
*/
|
||||
enum iwl_nan_sched_update_status {
|
||||
IWL_NAN_SCHED_UPDATE_SUCCESS = 0,
|
||||
IWL_NAN_SCHED_UPDATE_FAILURE = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_nan_sched_update_completed_notif - NAN schedule update completed
|
||||
*
|
||||
* @status: status of the schedule update operation. See
|
||||
* &enum iwl_nan_sched_update_status
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_nan_sched_update_completed_notif {
|
||||
u8 status;
|
||||
u8 reserved[3];
|
||||
} __packed; /* NAN_SCHED_UPDATE_COMPLETED_NTF_API_S_VER_1 */
|
||||
|
||||
#endif /* __iwl_fw_api_mac_cfg_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -85,12 +85,13 @@ struct iwl_ltr_config_cmd {
|
|||
* '1' PM could sleep over DTIM till listen Interval.
|
||||
* @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
|
||||
* access categories are both delivery and trigger enabled.
|
||||
* (Not supported since version 3)
|
||||
* @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
|
||||
* PBW Snoozing enabled
|
||||
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
|
||||
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
|
||||
* @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
|
||||
* detection enablement
|
||||
* detection enablement (Not supported since version 3)
|
||||
* @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif
|
||||
*/
|
||||
enum iwl_power_flags {
|
||||
|
|
@ -175,9 +176,9 @@ struct iwl_device_power_cmd {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_mac_power_cmd - New power command containing uAPSD support
|
||||
* struct iwl_mac_power_cmd_v2 - power command V2 containing uAPSD support
|
||||
* MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
|
||||
* @id_and_color: MAC contex identifier, &enum iwl_ctxt_id_and_color
|
||||
* @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
|
||||
* @flags: Power table command flags from POWER_FLAGS_*
|
||||
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
|
||||
* Minimum allowed:- 3 * DTIM. Keep alive period must be
|
||||
|
|
@ -216,7 +217,7 @@ struct iwl_device_power_cmd {
|
|||
* @limited_ps_threshold: (unused)
|
||||
* @reserved: reserved (padding)
|
||||
*/
|
||||
struct iwl_mac_power_cmd {
|
||||
struct iwl_mac_power_cmd_v2 {
|
||||
/* CONTEXT_DESC_API_T_VER_1 */
|
||||
__le32 id_and_color;
|
||||
|
||||
|
|
@ -242,6 +243,43 @@ struct iwl_mac_power_cmd {
|
|||
u8 reserved;
|
||||
} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_mac_power_cmd - power command
|
||||
* MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
|
||||
* @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
|
||||
* @flags: Power table command flags from POWER_FLAGS_*
|
||||
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
|
||||
* Minimum allowed:- 3 * DTIM. Keep alive period must be
|
||||
* set regardless of power scheme or current power state.
|
||||
* FW use this value also when PM is disabled.
|
||||
* @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
|
||||
* PSM transition - legacy PM
|
||||
* @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
|
||||
* PSM transition - legacy PM
|
||||
* @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
|
||||
* Default: 80dbm
|
||||
* @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
|
||||
* is set. For example, if it is required to skip over
|
||||
* one DTIM, this value need to be set to 2 (DTIM periods).
|
||||
* @qndp_tid: TID client shall use for uAPSD QNDP triggers
|
||||
* @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
|
||||
* each corresponding AC.
|
||||
* Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
|
||||
*/
|
||||
struct iwl_mac_power_cmd {
|
||||
/* CONTEXT_DESC_API_T_VER_1 */
|
||||
__le32 id_and_color;
|
||||
|
||||
__le16 flags;
|
||||
__le16 keep_alive_seconds;
|
||||
__le32 rx_data_timeout;
|
||||
__le32 tx_data_timeout;
|
||||
u8 lprx_rssi_threshold;
|
||||
u8 skip_dtim_periods;
|
||||
u8 qndp_tid;
|
||||
u8 uapsd_ac_flags;
|
||||
} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_3 */
|
||||
|
||||
/*
|
||||
* struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
|
||||
* associated AP is identified as improperly implementing uAPSD protocol.
|
||||
|
|
|
|||
|
|
@ -263,6 +263,9 @@ enum iwl_rx_mpdu_reorder_data {
|
|||
|
||||
enum iwl_rx_mpdu_phy_info {
|
||||
IWL_RX_MPDU_PHY_EOF_INDICATION = BIT(0),
|
||||
IWL_RX_MPDU_PHY_UEQM = BIT(1) | BIT(2),
|
||||
IWL_RX_MPDU_PHY_2X_LDPC = BIT(3),
|
||||
IWL_RX_MPDU_PHY_NPCA = BIT(4),
|
||||
IWL_RX_MPDU_PHY_AMPDU = BIT(5),
|
||||
IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6),
|
||||
IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7),
|
||||
|
|
@ -732,6 +735,8 @@ struct iwl_rx_mpdu_desc {
|
|||
* RX_MPDU_RES_START_API_S_VER_4,
|
||||
* RX_MPDU_RES_START_API_S_VER_5,
|
||||
* RX_MPDU_RES_START_API_S_VER_6
|
||||
* RX_MPDU_RES_START_API_S_VER_7
|
||||
* RX_MPDU_RES_START_API_S_VER_8
|
||||
*/
|
||||
|
||||
#define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1)
|
||||
|
|
@ -1169,8 +1174,14 @@ struct iwl_he_tb_sigs {
|
|||
#define OFDM_UCODE_TRIG_BASE_RX_BANDWIDTH 0x00007000
|
||||
#define OFDM_UCODE_TRIG_BASE_PS160 0x00008000
|
||||
#define OFDM_UCODE_EHT_TRIG_CONTROL_CHANNEL 0x000f0000
|
||||
/* for UHR, since PE RF: */
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS_UHR_AND_LATER 0x01f00000
|
||||
#define OFDM_UCODE_RX_IS_DRU 0x02000000
|
||||
#define OFDM_UCODE_RX_IS_2XLDPC_ENABLED 0x04000000
|
||||
#define OFDM_UCODE_RX_DRU_DBW 0x60000000
|
||||
__le32 tb_rx0;
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS 0x0000000f
|
||||
/* prior to PE RF, cannot have 5 bits needed for UHR: */
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS_PRE_UHR 0x0000000f
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_DCM 0x00000010
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_GI_LTF_TYPE 0x00000060
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_NSTS 0x00000380
|
||||
|
|
@ -1275,15 +1286,16 @@ struct iwl_uhr_sigs {
|
|||
__le32 usig_a1;
|
||||
#define OFDM_RX_FRAME_UHR_BSS_COLOR2 0x0000003f
|
||||
__le32 usig_a1_uhr;
|
||||
#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004
|
||||
#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8
|
||||
#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100
|
||||
#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600
|
||||
#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000
|
||||
#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004
|
||||
#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8
|
||||
#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100
|
||||
#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600
|
||||
#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000
|
||||
#define OFDM_RX_FRAME_UHR_USIG_CRC_OK 0x40000000
|
||||
__le32 usig_a2_uhr;
|
||||
#define OFDM_RX_FRAME_UHR_SPATIAL_REUSE 0x0000000f
|
||||
#define OFDM_RX_FRAME_UHR_GI_LTF_TYPE 0x00000030
|
||||
|
|
@ -1305,6 +1317,7 @@ struct iwl_uhr_sigs {
|
|||
#define OFDM_RX_FRAME_UHR_CODING 0x00000200
|
||||
#define OFDM_RX_FRAME_UHR_SPATIAL_CONFIG 0x00003c00
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU 0x003fc000
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU_P80 0x00004000
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU_PS160 0x00400000
|
||||
#define OFDM_RX_FRAME_UHR_UEQM 0x00800000
|
||||
#define OFDM_RX_FRAME_UHR_2XLDPC 0x01000000
|
||||
|
|
@ -1330,7 +1343,12 @@ struct iwl_uhr_tb_sigs {
|
|||
|
||||
struct iwl_uhr_elr_sigs {
|
||||
/* same as UHR above */
|
||||
__le32 usig_a1, usig_a2_uhr;
|
||||
__le32 usig_a1;
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_USIG_STA_ID 0x00001ffc
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_VALIDATE 0x0000e000
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_CRC_OK 0x00010000
|
||||
__le32 usig_a2_uhr_elr;
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_VER_ID 0x00000007
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_UPLINK_FLAG 0x00000008
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_MCS 0x00000010
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2021, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2021, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -191,7 +191,6 @@ enum iwl_sta_sleep_flag {
|
|||
#define STA_KEY_IDX_INVALID (0xff)
|
||||
#define STA_KEY_MAX_DATA_KEY_NUM (4)
|
||||
#define IWL_MAX_GLOBAL_KEYS (4)
|
||||
#define IWL_MAX_NUM_IGTKS 2
|
||||
#define STA_KEY_LEN_WEP40 (5)
|
||||
#define STA_KEY_LEN_WEP104 (13)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -557,7 +557,7 @@ struct iwl_stats_ntfy_per_mac {
|
|||
|
||||
#define IWL_STATS_MAX_BW_INDEX 5
|
||||
/**
|
||||
* struct iwl_stats_ntfy_per_phy - per PHY statistics
|
||||
* struct iwl_stats_ntfy_per_phy_v1 - per PHY statistics
|
||||
* @channel_load: channel load
|
||||
* @channel_load_by_us: device contribution to MCLM
|
||||
* @channel_load_not_by_us: other devices' contribution to MCLM
|
||||
|
|
@ -572,7 +572,7 @@ struct iwl_stats_ntfy_per_mac {
|
|||
* per channel BW. note BACK counted as 1
|
||||
* @last_tx_ch_width_indx: last txed frame channel width index
|
||||
*/
|
||||
struct iwl_stats_ntfy_per_phy {
|
||||
struct iwl_stats_ntfy_per_phy_v1 {
|
||||
__le32 channel_load;
|
||||
__le32 channel_load_by_us;
|
||||
__le32 channel_load_not_by_us;
|
||||
|
|
@ -585,6 +585,67 @@ struct iwl_stats_ntfy_per_phy {
|
|||
__le32 last_tx_ch_width_indx;
|
||||
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* struct iwl_stats_ntfy_coex - coex statistics
|
||||
*
|
||||
* @wifi_kill_cnt: count of wifi frames killed by BT
|
||||
* @wifi_tx_cts_kill_cnt: count of wifi Tx CTS frames killed by BT
|
||||
* @ttc2_ppdu_error_count: Count PPDU errors on TTC2 - BT Tx indication rises
|
||||
* within wifi Tx packet on non-shared antenna and wifi is NOT killed by
|
||||
* PTA/TCL.
|
||||
* @trc2_ppdu_error_count: count PPDU errors on TRC2 - BT Rx indication rises
|
||||
* within wifi Tx packet on non-shared antenna and wifi is NOT killed by
|
||||
* PTA/TCL
|
||||
* @rrc1_collision_count: count RRC1 - BT Rx indication rises within wifi Rx
|
||||
* packet on shared antenna
|
||||
* @rrc2_collision_count: count RRC2 - BT Rx indication rises within wifi Rx
|
||||
* packet on non-shared antenna
|
||||
* @rtc2_collision_count: count RTC2 - BT Tx indication rises within wifi Rx
|
||||
* packet on non-shared antenna
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_stats_ntfy_coex {
|
||||
__le16 wifi_kill_cnt;
|
||||
__le16 wifi_tx_cts_kill_cnt;
|
||||
__le16 ttc2_ppdu_error_count;
|
||||
__le16 trc2_ppdu_error_count;
|
||||
__le16 rrc1_collision_count;
|
||||
__le16 rrc2_collision_count;
|
||||
__le16 rtc2_collision_count;
|
||||
__le16 reserved;
|
||||
} __packed; /* STATISTICS_FW_NTFY_COEX_TELEMETRY_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* struct iwl_stats_ntfy_per_phy - per PHY statistics
|
||||
* @channel_load: channel load
|
||||
* @channel_load_by_us: device contribution to MCLM
|
||||
* @channel_load_not_by_us: other devices' contribution to MCLM
|
||||
* @clt: CLT HW timer (TIM_CH_LOAD2)
|
||||
* @act: active accumulator SW
|
||||
* @elp: elapsed time accumulator SW
|
||||
* @rx_detected_per_ch_width: number of deferred TX per channel width,
|
||||
* 0 - 20, 1/2/3 - 40/80/160
|
||||
* @success_per_ch_width: number of frames that got ACK/BACK/CTS
|
||||
* per channel BW. note, BACK counted as 1
|
||||
* @fail_per_ch_width: number of frames that didn't get ACK/BACK/CTS
|
||||
* per channel BW. note BACK counted as 1
|
||||
* @last_tx_ch_width_indx: last txed frame channel width index
|
||||
* @coex: coex related data
|
||||
*/
|
||||
struct iwl_stats_ntfy_per_phy {
|
||||
__le32 channel_load;
|
||||
__le32 channel_load_by_us;
|
||||
__le32 channel_load_not_by_us;
|
||||
__le32 clt;
|
||||
__le32 act;
|
||||
__le32 elp;
|
||||
__le32 rx_detected_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 success_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 fail_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 last_tx_ch_width_indx;
|
||||
struct iwl_stats_ntfy_coex coex;
|
||||
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_2 */
|
||||
|
||||
/* unknown channel load (due to not being active on channel) */
|
||||
#define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff
|
||||
|
||||
|
|
@ -599,12 +660,27 @@ struct iwl_stats_ntfy_per_sta {
|
|||
|
||||
#define IWL_STATS_MAX_PHY_OPERATIONAL 3
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_notif_oper_v3 - statistics notification
|
||||
*
|
||||
* @time_stamp: time when the notification is sent from firmware
|
||||
* @per_link: per link statistics, &struct iwl_stats_ntfy_per_link
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
*/
|
||||
struct iwl_system_statistics_notif_oper_v3 {
|
||||
__le32 time_stamp;
|
||||
struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
|
||||
struct iwl_stats_ntfy_per_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_notif_oper - statistics notification
|
||||
*
|
||||
* @time_stamp: time when the notification is sent from firmware
|
||||
* @per_link: per link statistics, &struct iwl_stats_ntfy_per_link
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
*/
|
||||
struct iwl_system_statistics_notif_oper {
|
||||
|
|
@ -612,7 +688,7 @@ struct iwl_system_statistics_notif_oper {
|
|||
struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
|
||||
struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_4 */
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_part1_notif_oper - part1 stats notification
|
||||
|
|
@ -642,7 +718,7 @@ struct iwl_system_statistics_end_notif {
|
|||
* @hdr: general statistics header
|
||||
* @flags: bitmap of possible notification structures
|
||||
* @per_mac: per mac statistics, &struct iwl_stats_ntfy_per_mac
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
* @rx_time: rx time
|
||||
* @tx_time: usec the radio is transmitting.
|
||||
|
|
@ -653,7 +729,7 @@ struct iwl_statistics_operational_ntfy {
|
|||
struct iwl_statistics_ntfy_hdr hdr;
|
||||
__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_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
__le64 rx_time;
|
||||
__le64 tx_time;
|
||||
|
|
|
|||
1022
drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c
Normal file
1022
drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2019, 2021-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2019, 2021-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -38,6 +38,11 @@ struct iwl_fw_dbg_params {
|
|||
u32 out_ctrl;
|
||||
};
|
||||
|
||||
/* old-style dump entry point */
|
||||
void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
|
||||
struct iwl_fwrt_dump_data *dump_data);
|
||||
struct scatterlist *iwl_fw_dbg_alloc_sgtable(ssize_t size);
|
||||
|
||||
extern const struct iwl_fw_dump_desc iwl_dump_desc_assert;
|
||||
|
||||
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
||||
|
|
|
|||
|
|
@ -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-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -275,16 +275,19 @@ static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* ignore this flag, we cannot use the response */
|
||||
hcmd.flags &= ~CMD_WANT_SKB;
|
||||
/* reject flags other than async, they cannot be used this way */
|
||||
if (hcmd.flags & ~CMD_ASYNC) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fwrt->ops && fwrt->ops->send_hcmd)
|
||||
ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
|
||||
else
|
||||
ret = -EPERM;
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (hcmd.flags & CMD_WANT_SKB)
|
||||
iwl_free_resp(&hcmd);
|
||||
out:
|
||||
kfree(data);
|
||||
return ret ?: count;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -265,18 +265,6 @@ struct iwl_fw_ini_error_dump_data {
|
|||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_ini_dump_entry - dump entry descriptor
|
||||
* @list: list of dump entries
|
||||
* @size: size of the data
|
||||
* @data: entry data
|
||||
*/
|
||||
struct iwl_fw_ini_dump_entry {
|
||||
struct list_head list;
|
||||
u32 size;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_ini_dump_file_hdr - header of dump file
|
||||
* @barker: must be %IWL_FW_INI_ERROR_DUMP_BARKER
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2008-2014, 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2008-2014, 2018-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -111,6 +111,7 @@ enum iwl_ucode_tlv_type {
|
|||
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_FW_NUM_MCAST_KEY_ENTRIES = IWL_UCODE_TLV_CONST_BASE + 3,
|
||||
|
||||
IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0,
|
||||
IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_TLV_DEBUG_BASE + 1,
|
||||
|
|
@ -1063,10 +1064,15 @@ struct iwl_fw_dump_exclude {
|
|||
__le32 addr, size;
|
||||
};
|
||||
|
||||
struct iwl_fw_fseq_bin_version {
|
||||
struct iwl_fw_fseq_bin_version_v1 {
|
||||
__le32 major, minor;
|
||||
}; /* FW_TLV_FSEQ_BIN_VERSION_S */
|
||||
|
||||
struct iwl_fw_fseq_bin_version {
|
||||
/* rf_id is currently unused and always zero */
|
||||
__le32 mac_id, rf_id, major, minor;
|
||||
}; /* FW_TLV_FSEQ_BIN_VERSION_S */
|
||||
|
||||
static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv,
|
||||
size_t fixed_size, size_t var_size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -53,6 +53,7 @@ struct iwl_ucode_capabilities {
|
|||
u32 num_stations;
|
||||
u32 num_links;
|
||||
u32 num_beacons;
|
||||
u32 num_mcast_key_entries;
|
||||
DECLARE_BITMAP(_api, NUM_IWL_UCODE_TLV_API);
|
||||
DECLARE_BITMAP(_capa, NUM_IWL_UCODE_TLV_CAPA);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2023, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/dmi.h>
|
||||
#include "iwl-drv.h"
|
||||
|
|
@ -112,6 +112,11 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "WIKO"),
|
||||
},
|
||||
},
|
||||
{ .ident = "XIAOMI",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2021-2022, 2025 Intel Corporation
|
||||
* Copyright (C) 2021-2022, 2025-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -124,6 +124,9 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
|
|||
case RATE_MCS_MOD_TYPE_EHT:
|
||||
type = "EHT";
|
||||
break;
|
||||
case RATE_MCS_MOD_TYPE_UHR:
|
||||
type = "UHR";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown"; /* shouldn't happen */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ struct iwl_mac_cfg {
|
|||
* @nvm_type: see &enum iwl_nvm_type
|
||||
* @uhb_supported: ultra high band channels supported
|
||||
* @eht_supported: EHT supported
|
||||
* @uhr_supported: UHR supported
|
||||
* @num_rbds: number of receive buffer descriptors to use
|
||||
* (only used for multi-queue capable devices)
|
||||
*
|
||||
|
|
@ -449,7 +450,8 @@ struct iwl_rf_cfg {
|
|||
lp_xtal_workaround:1,
|
||||
vht_mu_mimo_supported:1,
|
||||
uhb_supported:1,
|
||||
eht_supported:1;
|
||||
eht_supported:1,
|
||||
uhr_supported:1;
|
||||
u8 valid_tx_ant;
|
||||
u8 valid_rx_ant;
|
||||
u8 non_shared_ant;
|
||||
|
|
@ -674,6 +676,7 @@ extern const char iwl_killer_be1750w_name[];
|
|||
extern const char iwl_killer_be1750x_name[];
|
||||
extern const char iwl_killer_be1790s_name[];
|
||||
extern const char iwl_killer_be1790i_name[];
|
||||
extern const char iwl_killer_be1730x_name[];
|
||||
extern const char iwl_be201_name[];
|
||||
extern const char iwl_be200_name[];
|
||||
extern const char iwl_be202_name[];
|
||||
|
|
@ -681,6 +684,7 @@ extern const char iwl_be401_name[];
|
|||
extern const char iwl_be213_name[];
|
||||
extern const char iwl_killer_be1775s_name[];
|
||||
extern const char iwl_killer_be1775i_name[];
|
||||
extern const char iwl_killer_be1735x_name[];
|
||||
extern const char iwl_be211_name[];
|
||||
extern const char iwl_killer_bn1850w2_name[];
|
||||
extern const char iwl_killer_bn1850i_name[];
|
||||
|
|
@ -744,7 +748,8 @@ extern const struct iwl_rf_cfg iwl_rf_fm_160mhz;
|
|||
#define iwl_rf_wh iwl_rf_fm
|
||||
#define iwl_rf_wh_160mhz iwl_rf_fm_160mhz
|
||||
extern const struct iwl_rf_cfg iwl_rf_wh_non_eht;
|
||||
#define iwl_rf_pe iwl_rf_fm
|
||||
extern const struct iwl_rf_cfg iwl_rf_pe;
|
||||
#define iwl_rf_pe_no_uhr iwl_rf_fm
|
||||
#endif /* CONFIG_IWLMLD */
|
||||
|
||||
#endif /* __IWL_CONFIG_H__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -366,6 +366,7 @@ enum {
|
|||
#define CSR_HW_RF_ID_TYPE_GF4 (0x0010E000)
|
||||
#define CSR_HW_RF_ID_TYPE_FM (0x00112000)
|
||||
#define CSR_HW_RF_ID_TYPE_WP (0x00113000)
|
||||
#define CSR_HW_RF_ID_TYPE_PE (0x00114000)
|
||||
|
||||
/* HW_RF CHIP STEP */
|
||||
#define CSR_HW_RF_STEP(_val) (((_val) >> 8) & 0xF)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -1294,8 +1294,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|||
|
||||
if (tlv_len != sizeof(*fseq_ver))
|
||||
goto invalid_tlv_len;
|
||||
IWL_DEBUG_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
|
||||
fseq_ver->version);
|
||||
IWL_DEBUG_INFO(drv,
|
||||
"TLV_FW_FSEQ_VERSION: %.32s (sha1: %.20s)\n",
|
||||
fseq_ver->version, fseq_ver->sha1);
|
||||
}
|
||||
break;
|
||||
case IWL_UCODE_TLV_FW_NUM_STATIONS:
|
||||
|
|
@ -1330,6 +1331,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|||
capa->num_beacons =
|
||||
le32_to_cpup((const __le32 *)tlv_data);
|
||||
break;
|
||||
case IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES:
|
||||
if (tlv_len != sizeof(u32))
|
||||
goto invalid_tlv_len;
|
||||
capa->num_mcast_key_entries =
|
||||
le32_to_cpup((const __le32 *)tlv_data);
|
||||
break;
|
||||
case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: {
|
||||
const struct iwl_umac_debug_addrs *dbg_ptrs =
|
||||
(const void *)tlv_data;
|
||||
|
|
@ -1640,6 +1647,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|||
fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
|
||||
fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
|
||||
fw->ucode_capa.num_beacons = 1;
|
||||
fw->ucode_capa.num_mcast_key_entries = 2;
|
||||
/* dump all fw memory areas by default */
|
||||
fw->dbg.dump_mask = 0xffffffff;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2003-2014, 2018-2022, 2024-2026 Intel Corporation
|
||||
* Copyright (C) 2015-2016 Intel Deutschland GmbH
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
|
|
@ -12,7 +12,6 @@
|
|||
#include "iwl-debug.h"
|
||||
#include "iwl-prph.h"
|
||||
#include "iwl-fh.h"
|
||||
#include "pcie/gen1_2/internal.h"
|
||||
|
||||
void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
|
||||
{
|
||||
|
|
@ -168,6 +167,22 @@ int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
do {
|
||||
if ((iwl_read_umac_prph_no_grab(trans, addr) & mask) ==
|
||||
(bits & mask))
|
||||
return 0;
|
||||
udelay(IWL_POLL_INTERVAL);
|
||||
t += IWL_POLL_INTERVAL;
|
||||
} while (t < timeout);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
|
||||
{
|
||||
if (iwl_trans_grab_nic_access(trans)) {
|
||||
|
|
@ -396,12 +411,6 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_activate_nic(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
|
||||
|
||||
void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
|
||||
u32 sw_err_bit)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2018-2021, 2025 Intel Corporation
|
||||
* Copyright (C) 2018-2021, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_io_h__
|
||||
#define __iwl_io_h__
|
||||
|
|
@ -51,14 +51,14 @@ static inline void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
|
|||
|
||||
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout);
|
||||
int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout);
|
||||
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
|
||||
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
|
||||
u32 bits, u32 mask);
|
||||
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
|
||||
void iwl_force_nmi(struct iwl_trans *trans);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
/* Error handling */
|
||||
int iwl_dump_fh(struct iwl_trans *trans, char **buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
#include "iwl-prph.h"
|
||||
#include "iwl-io.h"
|
||||
#include "iwl-csr.h"
|
||||
#include "fw/acpi.h"
|
||||
#include "fw/api/nvm-reg.h"
|
||||
#include "fw/api/commands.h"
|
||||
#include "fw/api/cmdhdr.h"
|
||||
|
|
@ -206,28 +205,30 @@ enum iwl_reg_capa_flags_v2 {
|
|||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory
|
||||
* enum iwl_reg_capa_flags_v5 - global flags applied for the whole regulatory
|
||||
* domain.
|
||||
* @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
|
||||
* @REG_CAPA_V5_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5Ghz).
|
||||
* @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
|
||||
* @REG_CAPA_V5_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5Ghz).
|
||||
* @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
|
||||
* @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
|
||||
* @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
|
||||
* @REG_CAPA_V5_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
|
||||
* @REG_CAPA_V5_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
|
||||
* @REG_CAPA_V5_11BE_DISABLED: 11be is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V5_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V5_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5GHz).
|
||||
* @REG_CAPA_V5_11BN_DISABLED: UHR is not allowed for this regulatory domain
|
||||
*/
|
||||
enum iwl_reg_capa_flags_v4 {
|
||||
REG_CAPA_V4_160MHZ_ALLOWED = BIT(3),
|
||||
REG_CAPA_V4_80MHZ_ALLOWED = BIT(4),
|
||||
REG_CAPA_V4_MCS_12_ALLOWED = BIT(5),
|
||||
REG_CAPA_V4_MCS_13_ALLOWED = BIT(6),
|
||||
REG_CAPA_V4_11BE_DISABLED = BIT(8),
|
||||
REG_CAPA_V4_11AX_DISABLED = BIT(13),
|
||||
REG_CAPA_V4_320MHZ_ALLOWED = BIT(16),
|
||||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */
|
||||
enum iwl_reg_capa_flags_v5 {
|
||||
REG_CAPA_V5_160MHZ_ALLOWED = BIT(3),
|
||||
REG_CAPA_V5_80MHZ_ALLOWED = BIT(4),
|
||||
REG_CAPA_V5_MCS_12_ALLOWED = BIT(5),
|
||||
REG_CAPA_V5_MCS_13_ALLOWED = BIT(6),
|
||||
REG_CAPA_V5_11BE_DISABLED = BIT(8),
|
||||
REG_CAPA_V5_11AX_DISABLED = BIT(13),
|
||||
REG_CAPA_V5_320MHZ_ALLOWED = BIT(16),
|
||||
REG_CAPA_V5_11BN_DISABLED = BIT(17),
|
||||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4, 5 */
|
||||
|
||||
/*
|
||||
* API v2 for reg_capa_flags is relevant from version 6 and onwards of the
|
||||
|
|
@ -546,7 +547,7 @@ static const u8 iwl_vendor_caps[] = {
|
|||
0x00
|
||||
};
|
||||
|
||||
static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
||||
static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = {
|
||||
{
|
||||
.types_mask = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT),
|
||||
|
|
@ -690,6 +691,16 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
|||
*/
|
||||
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
|
||||
},
|
||||
.uhr_cap = {
|
||||
.has_uhr = true,
|
||||
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
|
||||
IEEE80211_UHR_PHY_CAP_ELR_TX,
|
||||
.mac.mac_cap = {
|
||||
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP |
|
||||
IEEE80211_UHR_MAC_CAP0_DPS_SUPP,
|
||||
[1] = IEEE80211_UHR_MAC_CAP1_DUO_SUPP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.types_mask = BIT(NL80211_IFTYPE_AP) |
|
||||
|
|
@ -788,6 +799,11 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
|||
*/
|
||||
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
|
||||
},
|
||||
.uhr_cap = {
|
||||
.has_uhr = true,
|
||||
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
|
||||
IEEE80211_UHR_PHY_CAP_ELR_TX,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -855,6 +871,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
|
|||
fips_enabled)
|
||||
iftype_data->eht_cap.has_eht = false;
|
||||
|
||||
if (!data->sku_cap_11bn_enable || !iftype_data->eht_cap.has_eht)
|
||||
iftype_data->uhr_cap.has_uhr = false;
|
||||
|
||||
/* Advertise an A-MPDU exponent extension based on
|
||||
* operating band
|
||||
*/
|
||||
|
|
@ -1023,9 +1042,9 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
struct ieee80211_sband_iftype_data *iftype_data;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_iftype_cap));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_iftype_cap));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_iftype_cap));
|
||||
|
||||
switch (sband->band) {
|
||||
case NL80211_BAND_2GHZ:
|
||||
|
|
@ -1042,10 +1061,10 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
return;
|
||||
}
|
||||
|
||||
memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
|
||||
memcpy(iftype_data, iwl_iftype_cap, sizeof(iwl_iftype_cap));
|
||||
|
||||
_ieee80211_set_sband_iftype_data(sband, iftype_data,
|
||||
ARRAY_SIZE(iwl_he_eht_capa));
|
||||
ARRAY_SIZE(iwl_iftype_cap));
|
||||
|
||||
for (i = 0; i < sband->n_iftype_data; i++)
|
||||
iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
|
||||
|
|
@ -1054,6 +1073,46 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_init_nan_phy_capa(const struct iwl_fw *fw, struct iwl_nvm_data *data)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
|
||||
if (!fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT))
|
||||
return;
|
||||
|
||||
data->nan_phy_capa.ht = data->bands[NL80211_BAND_2GHZ].ht_cap;
|
||||
data->nan_phy_capa.vht = data->bands[NL80211_BAND_5GHZ].vht_cap;
|
||||
|
||||
he_cap = ieee80211_get_he_iftype_cap(&data->bands[NL80211_BAND_2GHZ],
|
||||
NL80211_IFTYPE_STATION);
|
||||
if (he_cap) {
|
||||
data->nan_phy_capa.he = *he_cap;
|
||||
data->nan_phy_capa.he.he_cap_elem.phy_cap_info[0] |=
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: we copied HE capabilities from the 2.4 GHz band,
|
||||
* but there are bits that are band-dependent:
|
||||
*
|
||||
* IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 - 2.4 GHz - set
|
||||
* IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 - 5 GHz - not set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G - set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G - set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G - set
|
||||
*
|
||||
* We copied from STA iftype - so we have the following bits set:
|
||||
* IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS
|
||||
* IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS
|
||||
* IEEE80211_HE_PHY_CAP7_MAX_NC_1
|
||||
* IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO
|
||||
*
|
||||
* Need to check which one should actually be set for NAN.
|
||||
*/
|
||||
}
|
||||
|
||||
void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
|
||||
u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw)
|
||||
{
|
||||
|
|
@ -1082,6 +1141,8 @@ void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
|
|||
if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
|
||||
iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
|
||||
fw);
|
||||
|
||||
iwl_init_nan_phy_capa(fw, data);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_reinit_cab);
|
||||
|
||||
|
|
@ -1611,6 +1672,9 @@ u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
|||
if (reg_capa.disable_11be)
|
||||
flags |= NL80211_RRF_NO_EHT;
|
||||
|
||||
if (reg_capa.disable_11bn)
|
||||
flags |= NL80211_RRF_NO_UHR;
|
||||
|
||||
return flags;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_nvm_get_regdom_bw_flags);
|
||||
|
|
@ -1621,11 +1685,13 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
|
|||
|
||||
if (resp_ver >= REG_CAPA_V4_RESP_VER) {
|
||||
reg_capa.allow_40mhz = true;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED;
|
||||
reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED;
|
||||
reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED;
|
||||
reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED;
|
||||
reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V5_80MHZ_ALLOWED;
|
||||
reg_capa.allow_160mhz = flags & REG_CAPA_V5_160MHZ_ALLOWED;
|
||||
reg_capa.allow_320mhz = flags & REG_CAPA_V5_320MHZ_ALLOWED;
|
||||
reg_capa.disable_11ax = flags & REG_CAPA_V5_11AX_DISABLED;
|
||||
reg_capa.disable_11be = flags & REG_CAPA_V5_11BE_DISABLED;
|
||||
/* can check: was reserved and irrelevant for pre-UHR devices */
|
||||
reg_capa.disable_11bn = flags & REG_CAPA_V5_11BN_DISABLED;
|
||||
} else if (resp_ver >= REG_CAPA_V2_RESP_VER) {
|
||||
reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED;
|
||||
|
|
@ -1683,8 +1749,16 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
|
|||
IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
|
||||
num_of_ch);
|
||||
|
||||
/* build a regdomain rule for every valid channel */
|
||||
regd = kzalloc_flex(*regd, reg_rules, num_of_ch);
|
||||
/* build a regdomain rule for every valid channel.
|
||||
* Certain firmware versions might report no valid channels
|
||||
* if booted in RF-kill, i.e. not all calibrations etc. are
|
||||
* running. We'll get out of this situation later when the
|
||||
* rfkill is removed and we update the regdomain again, but
|
||||
* since cfg80211 doesn't accept an empty regdomain, we need
|
||||
* to allocate space for at least one rule to add a dummy
|
||||
* (unusable) rule in this case so we can init.
|
||||
*/
|
||||
regd = kzalloc_flex(*regd, reg_rules, num_of_ch ?: 1);
|
||||
if (!regd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
@ -1758,14 +1832,7 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
|
|||
reg_query_regdb_wmm(regd->alpha2, center_freq, rule);
|
||||
}
|
||||
|
||||
/*
|
||||
* Certain firmware versions might report no valid channels
|
||||
* if booted in RF-kill, i.e. not all calibrations etc. are
|
||||
* running. We'll get out of this situation later when the
|
||||
* rfkill is removed and we update the regdomain again, but
|
||||
* since cfg80211 doesn't accept an empty regdomain, add a
|
||||
* dummy (unusable) rule here in this case so we can init.
|
||||
*/
|
||||
/* If no valid rules were found, add a dummy rule */
|
||||
if (!valid_rules) {
|
||||
valid_rules = 1;
|
||||
rule = ®d->reg_rules[valid_rules - 1];
|
||||
|
|
@ -2079,6 +2146,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
|
|||
!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
|
||||
if (trans->cfg->eht_supported)
|
||||
nvm->sku_cap_11be_enable = true;
|
||||
nvm->sku_cap_11bn_enable = trans->cfg->uhr_supported;
|
||||
|
||||
/* Initialize PHY sku data */
|
||||
nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
|
||||
|
|
@ -2106,6 +2174,8 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
|
|||
iwl_init_sbands(trans, nvm, channel_profile, tx_ant, rx_ant,
|
||||
sbands_flags, v4, fw);
|
||||
|
||||
iwl_init_nan_phy_capa(fw, nvm);
|
||||
|
||||
iwl_free_resp(&hcmd);
|
||||
return nvm;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ enum iwl_nvm_sbands_flags {
|
|||
* for this regulatory domain (valid only in 6 Ghz).
|
||||
* @disable_11ax: 11ax is forbidden for this regulatory domain.
|
||||
* @disable_11be: 11be is forbidden for this regulatory domain.
|
||||
* @disable_11bn: UHR/11bn is not allowed for this regulatory domain
|
||||
*/
|
||||
struct iwl_reg_capa {
|
||||
bool allow_40mhz;
|
||||
|
|
@ -43,6 +44,7 @@ struct iwl_reg_capa {
|
|||
bool allow_320mhz;
|
||||
bool disable_11ax;
|
||||
bool disable_11be;
|
||||
bool disable_11bn;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018, 2020-2023 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018, 2020-2023, 2026 Intel Corporation
|
||||
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
||||
*/
|
||||
#ifndef __iwl_eeprom_parse_h__
|
||||
|
|
@ -32,6 +32,7 @@ struct iwl_nvm_data {
|
|||
bool sku_cap_ipan_enable;
|
||||
bool sku_cap_mimo_disabled;
|
||||
bool sku_cap_11be_enable;
|
||||
bool sku_cap_11bn_enable;
|
||||
|
||||
u16 radio_cfg_type;
|
||||
u8 radio_cfg_step;
|
||||
|
|
@ -55,6 +56,12 @@ struct iwl_nvm_data {
|
|||
struct ieee80211_sband_iftype_data uhb[2];
|
||||
} iftd;
|
||||
|
||||
struct {
|
||||
struct ieee80211_sta_ht_cap ht;
|
||||
struct ieee80211_sta_vht_cap vht;
|
||||
struct ieee80211_sta_he_cap he;
|
||||
} nan_phy_capa;
|
||||
|
||||
struct ieee80211_channel channels[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -411,6 +411,11 @@ enum {
|
|||
#define HPM_SECONDARY_DEVICE_STATE 0xa03404
|
||||
#define WFPM_MAC_OTP_CFG7_ADDR 0xa03338
|
||||
#define WFPM_MAC_OTP_CFG7_DATA 0xa0333c
|
||||
#define WFPM_RSRCS_4PHS_REQ_STTS 0xa033f8
|
||||
#define WFPM_RSRCS_4PHS_ACK_STTS 0xa033fc
|
||||
|
||||
#define RSRC_REQ_CNVR_TOP BIT(6)
|
||||
#define RSRC_ACK_CNVR_TOP BIT(6)
|
||||
|
||||
|
||||
/* For UMAG_GEN_HW_STATUS reg check */
|
||||
|
|
|
|||
|
|
@ -2,16 +2,14 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2019-2021, 2023-2025 Intel Corporation
|
||||
* Copyright (C) 2019-2021, 2023-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "fw/api/tx.h"
|
||||
#include "iwl-trans.h"
|
||||
#include "iwl-drv.h"
|
||||
#include "iwl-fh.h"
|
||||
#include <linux/dmapool.h>
|
||||
#include "fw/api/commands.h"
|
||||
#include "pcie/gen1_2/internal.h"
|
||||
|
|
@ -461,6 +459,12 @@ int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
|
|||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_read_mem);
|
||||
|
||||
int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords)
|
||||
{
|
||||
return iwl_trans_pcie_read_mem_no_grab(trans, addr, buf, dwords);
|
||||
}
|
||||
|
||||
int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
|
||||
const void *buf, int dwords)
|
||||
{
|
||||
|
|
@ -822,3 +826,10 @@ bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans)
|
|||
return iwl_pcie_gen1_2_is_ltr_enabled(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_is_ltr_enabled);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_activate_nic(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -700,106 +700,6 @@ struct iwl_cmd_meta {
|
|||
u32 tbs;
|
||||
};
|
||||
|
||||
/*
|
||||
* The FH will write back to the first TB only, so we need to copy some data
|
||||
* into the buffer regardless of whether it should be mapped or not.
|
||||
* This indicates how big the first TB must be to include the scratch buffer
|
||||
* and the assigned PN.
|
||||
* Since PN location is 8 bytes at offset 12, it's 20 now.
|
||||
* If we make it bigger then allocations will be bigger and copy slower, so
|
||||
* that's probably not useful.
|
||||
*/
|
||||
#define IWL_FIRST_TB_SIZE 20
|
||||
#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64)
|
||||
|
||||
struct iwl_pcie_txq_entry {
|
||||
void *cmd;
|
||||
struct sk_buff *skb;
|
||||
/* buffer to free after command completes */
|
||||
const void *free_buf;
|
||||
struct iwl_cmd_meta meta;
|
||||
};
|
||||
|
||||
struct iwl_pcie_first_tb_buf {
|
||||
u8 buf[IWL_FIRST_TB_SIZE_ALIGN];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_txq - Tx Queue for DMA
|
||||
* @tfds: transmit frame descriptors (DMA memory)
|
||||
* @first_tb_bufs: start of command headers, including scratch buffers, for
|
||||
* the writeback -- this is DMA memory and an array holding one buffer
|
||||
* for each command on the queue
|
||||
* @first_tb_dma: DMA address for the first_tb_bufs start
|
||||
* @entries: transmit entries (driver state)
|
||||
* @lock: queue lock
|
||||
* @reclaim_lock: reclaim lock
|
||||
* @stuck_timer: timer that fires if queue gets stuck
|
||||
* @trans: pointer back to transport (for timer)
|
||||
* @need_update: indicates need to update read/write index
|
||||
* @ampdu: true if this queue is an ampdu queue for an specific RA/TID
|
||||
* @wd_timeout: queue watchdog timeout (jiffies) - per queue
|
||||
* @frozen: tx stuck queue timer is frozen
|
||||
* @frozen_expiry_remainder: remember how long until the timer fires
|
||||
* @block: queue is blocked
|
||||
* @bc_tbl: byte count table of the queue (relevant only for gen2 transport)
|
||||
* @write_ptr: 1-st empty entry (index) host_w
|
||||
* @read_ptr: last used entry (index) host_r
|
||||
* @dma_addr: physical addr for BD's
|
||||
* @n_window: safe queue window
|
||||
* @id: queue id
|
||||
* @low_mark: low watermark, resume queue if free space more than this
|
||||
* @high_mark: high watermark, stop queue if free space less than this
|
||||
* @overflow_q: overflow queue for handling frames that didn't fit on HW queue
|
||||
* @overflow_tx: need to transmit from overflow
|
||||
*
|
||||
* A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
|
||||
* descriptors) and required locking structures.
|
||||
*
|
||||
* Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
|
||||
* always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
|
||||
* there might be HW changes in the future). For the normal TX
|
||||
* queues, n_window, which is the size of the software queue data
|
||||
* is also 256; however, for the command queue, n_window is only
|
||||
* 32 since we don't need so many commands pending. Since the HW
|
||||
* still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256.
|
||||
* This means that we end up with the following:
|
||||
* HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
|
||||
* SW entries: | 0 | ... | 31 |
|
||||
* where N is a number between 0 and 7. This means that the SW
|
||||
* data is a window overlayed over the HW queue.
|
||||
*/
|
||||
struct iwl_txq {
|
||||
void *tfds;
|
||||
struct iwl_pcie_first_tb_buf *first_tb_bufs;
|
||||
dma_addr_t first_tb_dma;
|
||||
struct iwl_pcie_txq_entry *entries;
|
||||
/* lock for syncing changes on the queue */
|
||||
spinlock_t lock;
|
||||
/* lock to prevent concurrent reclaim */
|
||||
spinlock_t reclaim_lock;
|
||||
unsigned long frozen_expiry_remainder;
|
||||
struct timer_list stuck_timer;
|
||||
struct iwl_trans *trans;
|
||||
bool need_update;
|
||||
bool frozen;
|
||||
bool ampdu;
|
||||
int block;
|
||||
unsigned long wd_timeout;
|
||||
struct sk_buff_head overflow_q;
|
||||
struct iwl_dma_ptr bc_tbl;
|
||||
|
||||
int write_ptr;
|
||||
int read_ptr;
|
||||
dma_addr_t dma_addr;
|
||||
int n_window;
|
||||
u32 id;
|
||||
int low_mark;
|
||||
int high_mark;
|
||||
|
||||
bool overflow_tx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_trans_info - transport info for outside use
|
||||
* @name: the device name
|
||||
|
|
@ -1019,6 +919,14 @@ void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
|
|||
int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, int dwords);
|
||||
|
||||
/*
|
||||
* Note the special calling convention - it's allowed to drop the
|
||||
* internal transport lock and re-enable BHs temporarily, but will
|
||||
* not release NIC access.
|
||||
*/
|
||||
int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords);
|
||||
|
||||
int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs,
|
||||
u32 *val);
|
||||
|
||||
|
|
@ -1034,6 +942,14 @@ void iwl_trans_debugfs_cleanup(struct iwl_trans *trans);
|
|||
(bufsize) / sizeof(u32)); \
|
||||
})
|
||||
|
||||
static inline int
|
||||
iwl_trans_read_mem_bytes_no_grab(struct iwl_trans *trans,
|
||||
u32 addr, void *buf, u32 bufsize)
|
||||
{
|
||||
return iwl_trans_read_mem_no_grab(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);
|
||||
|
||||
|
|
@ -1153,6 +1069,8 @@ static inline bool iwl_trans_dbg_ini_valid(struct iwl_trans *trans)
|
|||
|
||||
void iwl_trans_interrupts(struct iwl_trans *trans, bool enable);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans)
|
||||
{
|
||||
clear_bit(STATUS_IN_SW_RESET, &trans->status);
|
||||
|
|
@ -1236,9 +1154,6 @@ enum iwl_reset_mode {
|
|||
void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);
|
||||
void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans);
|
||||
|
||||
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
|
||||
struct iwl_host_cmd *cmd);
|
||||
|
||||
/* Internal helper */
|
||||
static inline void iwl_trans_set_info(struct iwl_trans *trans,
|
||||
struct iwl_trans_info *info)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto out_unlock;
|
||||
|
||||
sta_id = ffs(ba_data->sta_mask) - 1;
|
||||
link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
|
||||
if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
|
||||
|
|
@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
|
|||
goto out_unlock;
|
||||
|
||||
/* pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto out_unlock;
|
||||
|
||||
sta_id = ffs(ba_data->sta_mask) - 1;
|
||||
link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);
|
||||
if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))
|
||||
|
|
@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expired(struct timer_list *t)
|
|||
}
|
||||
|
||||
/* timer expired, pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto unlock;
|
||||
|
||||
sta_id = ffs(ba_data->sta_mask) - 1;
|
||||
link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/crc32.h>
|
||||
|
||||
|
|
@ -239,6 +239,58 @@ int iwl_mld_store_ap_early_key(struct iwl_mld *mld,
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void iwl_mld_stop_beacon(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link)
|
||||
{
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
|
||||
struct iwl_mac_beacon_cmd cmd = {};
|
||||
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, BEACON_TEMPLATE_CMD, 14);
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return;
|
||||
|
||||
if (cmd_ver < 15)
|
||||
return;
|
||||
|
||||
/* leave byte_cnt 0 */
|
||||
cmd.link_id = cpu_to_le32(mld_link->fw_id);
|
||||
|
||||
iwl_mld_send_cmd_pdu(mld, BEACON_TEMPLATE_CMD, &cmd);
|
||||
}
|
||||
|
||||
void
|
||||
iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link,
|
||||
u64 changes)
|
||||
{
|
||||
u32 link_changes = 0;
|
||||
|
||||
if (changes & BSS_CHANGED_ERP_SLOT)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;
|
||||
|
||||
if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))
|
||||
link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS;
|
||||
|
||||
if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH))
|
||||
link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS;
|
||||
|
||||
if (changes & BSS_CHANGED_HE_BSS_COLOR)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
|
||||
|
||||
if (link_changes)
|
||||
iwl_mld_change_link_in_fw(mld, link, link_changes);
|
||||
|
||||
if (changes & BSS_CHANGED_BEACON) {
|
||||
WARN_ON(!link->enable_beacon);
|
||||
iwl_mld_update_beacon_template(mld, vif, link);
|
||||
}
|
||||
|
||||
/* Enabling beacons was already covered above */
|
||||
if ((changes & BSS_CHANGED_BEACON_ENABLED) && !link->enable_beacon)
|
||||
iwl_mld_stop_beacon(mld, vif, link);
|
||||
}
|
||||
|
||||
static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link)
|
||||
|
|
@ -276,10 +328,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
|
|||
if (vif->type == NL80211_IFTYPE_AP)
|
||||
iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link);
|
||||
|
||||
ret = iwl_mld_update_beacon_template(mld, vif, link);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* the link should be already activated when assigning chan context,
|
||||
* and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_ap_h__
|
||||
#define __iwl_ap_h__
|
||||
|
|
@ -14,6 +14,12 @@ int iwl_mld_update_beacon_template(struct iwl_mld *mld,
|
|||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
void
|
||||
iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link,
|
||||
u64 changes);
|
||||
|
||||
int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include "mld.h"
|
||||
|
||||
|
|
@ -43,6 +43,12 @@ struct iwl_mld_resume_key_iter_data {
|
|||
struct iwl_mld_wowlan_status *wowlan_status;
|
||||
};
|
||||
|
||||
struct iwl_mld_rsc_resume_iter_data {
|
||||
struct iwl_mld *mld;
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif;
|
||||
int queue;
|
||||
};
|
||||
|
||||
struct iwl_mld_suspend_key_iter_data {
|
||||
struct iwl_wowlan_rsc_tsc_params_cmd *rsc;
|
||||
bool have_rsc;
|
||||
|
|
@ -282,7 +288,8 @@ static void
|
|||
iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
||||
struct iwl_mld_wowlan_status *wowlan_status,
|
||||
const struct iwl_wowlan_gtk_status *gtk_data,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *sc,
|
||||
int rsc_notif_ver)
|
||||
{
|
||||
int status_idx = 0;
|
||||
|
||||
|
|
@ -305,14 +312,18 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
|||
wowlan_status->gtk[status_idx].id =
|
||||
wowlan_status->gtk[status_idx].flags &
|
||||
IWL_WOWLAN_GTK_IDX_MASK;
|
||||
/* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc
|
||||
* The gtk ids can be any two numbers between 0 and 3,
|
||||
* the id_map maps between the key id and the index in sc->mcast
|
||||
*/
|
||||
rsc_idx =
|
||||
sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
|
||||
iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
|
||||
sc, rsc_idx);
|
||||
/* If RSC_NOTIF is not supported */
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN) {
|
||||
/* The rsc for both gtk keys are stored in
|
||||
* gtk[0]->sc->mcast_rsc. The gtk ids can be any two
|
||||
* numbers between 0 and 3, the id_map maps between the
|
||||
* key id and the index in sc->mcast
|
||||
*/
|
||||
rsc_idx =
|
||||
sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
|
||||
iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
|
||||
sc, rsc_idx);
|
||||
}
|
||||
|
||||
if (key_status == IWL_WOWLAN_STATUS_NEW_KEY) {
|
||||
memcpy(wowlan_status->gtk[status_idx].key,
|
||||
|
|
@ -598,6 +609,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
|||
PROT_OFFLOAD_GROUP,
|
||||
WOWLAN_INFO_NOTIFICATION,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
int rsc_notif_ver = iwl_fw_lookup_notif_ver(mld->fw,
|
||||
DATA_PATH_GROUP,
|
||||
RSC_NOTIF,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
if (wowlan_info_ver == 5) {
|
||||
/* v5 format - validate before conversion */
|
||||
|
|
@ -642,8 +657,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
|||
return true;
|
||||
|
||||
iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk,
|
||||
¬if->gtk[0].sc);
|
||||
iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc);
|
||||
¬if->gtk[0].sc, rsc_notif_ver);
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN)
|
||||
iwl_mld_convert_ptk_resume_seq(mld, wowlan_status,
|
||||
¬if->gtk[0].sc);
|
||||
/* only one igtk is passed by FW */
|
||||
iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]);
|
||||
iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk);
|
||||
|
|
@ -902,8 +919,14 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
|
|||
struct iwl_mld_resume_key_iter_data *data = _data;
|
||||
struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status;
|
||||
u8 status_idx;
|
||||
int rsc_notif_ver = iwl_fw_lookup_notif_ver(data->mld->fw,
|
||||
DATA_PATH_GROUP,
|
||||
RSC_NOTIF,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
if (key->keyidx >= 0 && key->keyidx <= 3) {
|
||||
/* If RSC_NOTIF is not supported */
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN &&
|
||||
key->keyidx >= 0 && key->keyidx <= 3) {
|
||||
/* PTK */
|
||||
if (sta) {
|
||||
iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
|
||||
|
|
@ -932,6 +955,105 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_rsc_update_key_iter(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key,
|
||||
void *_data)
|
||||
{
|
||||
struct iwl_mld_rsc_resume_iter_data *data = _data;
|
||||
struct ieee80211_key_seq seq;
|
||||
|
||||
if (key->keyidx > 3)
|
||||
return;
|
||||
|
||||
if (sta) {
|
||||
/* PTK */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(data->notif->ucast_rsc) !=
|
||||
IWL_MAX_TID_COUNT);
|
||||
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
/* TKIP: just update key sequences */
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
iwl_mld_le64_to_tkip_seq(data->notif->ucast_rsc[tid],
|
||||
&seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
}
|
||||
} else {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct iwl_mld_ptk_pn *mld_ptk_pn =
|
||||
rcu_dereference_wiphy(data->mld->wiphy,
|
||||
mld_sta->ptk_pn[key->keyidx]);
|
||||
|
||||
if (WARN_ON(!mld_ptk_pn))
|
||||
return;
|
||||
|
||||
if (WARN_ON(data->queue >=
|
||||
data->mld->trans->info.num_rxqs))
|
||||
return;
|
||||
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
iwl_mld_le64_to_aes_seq(data->notif->ucast_rsc[tid],
|
||||
&seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
memcpy(mld_ptk_pn->q[data->queue].pn[tid],
|
||||
seq.ccmp.pn,
|
||||
IEEE80211_CCMP_PN_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
IWL_DEBUG_WOWLAN(data->mld,
|
||||
"Updated PTK RSC for key %d on queue %d\n",
|
||||
key->keyidx, data->queue);
|
||||
} else {
|
||||
/* GTK */
|
||||
int rsc_idx = data->notif->mcast_key_id_map[key->keyidx];
|
||||
|
||||
if (rsc_idx == IWL_MCAST_KEY_MAP_INVALID)
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(data->mld,
|
||||
rsc_idx >= ARRAY_SIZE(data->notif->mcast_rsc),
|
||||
"Invalid mcast key mapping: %d for key %d\n",
|
||||
rsc_idx, key->keyidx))
|
||||
return;
|
||||
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
__le64 rsc =
|
||||
data->notif->mcast_rsc[rsc_idx][tid];
|
||||
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
iwl_mld_le64_to_tkip_seq(rsc, &seq);
|
||||
else
|
||||
iwl_mld_le64_to_aes_seq(rsc, &seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
}
|
||||
|
||||
IWL_DEBUG_WOWLAN(data->mld,
|
||||
"Updated GTK %d RSC (rsc_idx %d) on queue %d\n",
|
||||
key->keyidx, rsc_idx, data->queue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iwl_mld_process_rsc_notification(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
|
||||
int queue)
|
||||
{
|
||||
struct iwl_mld_rsc_resume_iter_data iter_data = {
|
||||
.mld = mld,
|
||||
.notif = notif,
|
||||
.queue = queue,
|
||||
};
|
||||
|
||||
/* Iterate through all active keys and update RSC */
|
||||
ieee80211_iter_keys_rcu(mld->hw, vif,
|
||||
iwl_mld_rsc_update_key_iter,
|
||||
&iter_data);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
||||
struct iwl_mld *mld,
|
||||
|
|
@ -951,19 +1073,25 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
|||
|
||||
iwl_mld_update_mcast_rx_seq(key_config, key_data);
|
||||
|
||||
/* The FW holds only one igtk so we keep track of the valid one */
|
||||
/* The FW holds only one IGTK so we keep track of the valid one */
|
||||
if (key_config->keyidx == 4 || key_config->keyidx == 5) {
|
||||
struct iwl_mld_link *mld_link =
|
||||
iwl_mld_link_from_mac80211(link_conf);
|
||||
struct iwl_mld_link_sta *mld_ap_link_sta;
|
||||
|
||||
mld_ap_link_sta = iwl_mld_get_ap_link_sta(vif,
|
||||
link_conf->link_id);
|
||||
if (WARN_ON(!mld_ap_link_sta))
|
||||
return;
|
||||
|
||||
/* If we had more than one rekey, mac80211 will tell us to
|
||||
* remove the old and add the new so we will update the IGTK in
|
||||
* drv_set_key
|
||||
*/
|
||||
if (mld_link->igtk && mld_link->igtk != key_config) {
|
||||
if (mld_ap_link_sta->rx_igtk &&
|
||||
mld_ap_link_sta->rx_igtk != key_config) {
|
||||
/* mark the old IGTK as not in FW */
|
||||
mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;
|
||||
mld_link->igtk = key_config;
|
||||
mld_ap_link_sta->rx_igtk->hw_key_idx =
|
||||
STA_KEY_IDX_INVALID;
|
||||
mld_ap_link_sta->rx_igtk = key_config;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_d3_h__
|
||||
#define __iwl_mld_d3_h__
|
||||
|
|
@ -42,6 +42,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld);
|
|||
void iwl_mld_set_rekey_data(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_gtk_rekey_data *data);
|
||||
void iwl_mld_process_rsc_notification(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
|
||||
int queue);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -979,15 +979,39 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
|
|||
VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200);
|
||||
VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200);
|
||||
}
|
||||
#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
|
||||
|
||||
#define LINK_DEBUGFS_WIPHY_WRITE_FILE_OPS(name, bufsz) \
|
||||
WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf)
|
||||
|
||||
/*
|
||||
* Note: no locking is provided, so the function must have its own,
|
||||
* but it cannot acquire the wiphy mutex.
|
||||
*/
|
||||
#define LINK_DEBUGFS_READ_FILE_OPS(name, bufsz) \
|
||||
_MLD_DEBUGFS_READ_FILE_OPS(link_##name, bufsz, struct ieee80211_bss_conf)
|
||||
|
||||
#define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
|
||||
debugfs_create_file(alias, mode, parent, link_conf, \
|
||||
&iwl_dbgfs_link_##name##_ops)
|
||||
#define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \
|
||||
LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
|
||||
|
||||
static ssize_t iwl_dbgfs_link_fw_id_read(struct ieee80211_bss_conf *link_conf,
|
||||
size_t buflen, void *buf)
|
||||
{
|
||||
struct iwl_mld_link *mld_link;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
if (!mld_link)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, buflen, "%d\n", mld_link->fw_id);
|
||||
}
|
||||
|
||||
LINK_DEBUGFS_READ_FILE_OPS(fw_id, 64);
|
||||
|
||||
void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
|
|
@ -1008,6 +1032,8 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
|
|||
/* Release the reference from debugfs_lookup */
|
||||
dput(mld_link_dir);
|
||||
}
|
||||
|
||||
LINK_DEBUGFS_ADD_FILE(fw_id, mld_link_dir, 0400);
|
||||
}
|
||||
|
||||
static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
|
|
@ -1035,7 +1061,6 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
|||
|
||||
/* input is in FW format (v2 or v3) so convert to v3 */
|
||||
rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3);
|
||||
rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3));
|
||||
|
||||
ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id,
|
||||
partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE :
|
||||
|
|
@ -1050,20 +1075,22 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
|||
return ret ? : count;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
{
|
||||
return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false);
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_fixed_rate_v3_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
{
|
||||
return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true);
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
static ssize_t iwl_dbgfs_link_sta_tlc_dhc_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
{
|
||||
struct ieee80211_link_sta *link_sta = data;
|
||||
struct iwl_mld_link_sta *mld_link_sta;
|
||||
|
|
@ -1090,18 +1117,42 @@ static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
|
|||
return ret ? : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_fw_id_read(struct ieee80211_link_sta *link_sta,
|
||||
size_t buflen, void *buf)
|
||||
{
|
||||
struct iwl_mld_link_sta *mld_link_sta;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
|
||||
if (!mld_link_sta)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, buflen, "%u\n", mld_link_sta->fw_id);
|
||||
}
|
||||
|
||||
#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
|
||||
debugfs_create_file(alias, mode, parent, link_sta, \
|
||||
&iwl_dbgfs_##name##_ops)
|
||||
&iwl_dbgfs_link_sta_##name##_ops)
|
||||
#define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \
|
||||
LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
|
||||
|
||||
#define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \
|
||||
WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta)
|
||||
WIPHY_DEBUGFS_WRITE_FILE_OPS(link_sta_##name, bufsz, link_sta)
|
||||
|
||||
/*
|
||||
* Note: no locking is provided, so the function must have its own,
|
||||
* but it cannot acquire the wiphy mutex.
|
||||
*/
|
||||
#define LINK_STA_DEBUGFS_READ_OPS(name, bufsz) \
|
||||
_MLD_DEBUGFS_READ_FILE_OPS(link_sta_##name, bufsz, \
|
||||
struct ieee80211_link_sta)
|
||||
|
||||
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64);
|
||||
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64);
|
||||
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64);
|
||||
LINK_STA_DEBUGFS_READ_OPS(fw_id, 64);
|
||||
|
||||
void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -1111,4 +1162,5 @@ void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
|
|||
LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200);
|
||||
LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200);
|
||||
LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200);
|
||||
LINK_STA_DEBUGFS_ADD_FILE(fw_id, dir, 0400);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,24 +71,34 @@ iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld,
|
|||
|
||||
switch (peer->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
|
||||
target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
|
||||
target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
|
||||
target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "iface.h"
|
||||
#include "nan.h"
|
||||
#include "hcmd.h"
|
||||
#include "key.h"
|
||||
#include "mlo.h"
|
||||
|
|
@ -55,6 +56,24 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|||
|
||||
ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
mld_vif->nan.mac_added = false;
|
||||
/* Clean up NAN links */
|
||||
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++)
|
||||
iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]);
|
||||
|
||||
if (mld_vif->nan.bcast_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
if (mld_vif->nan.mgmt_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
|
||||
mld_vif->nan.tx_igtk = NULL;
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA &&
|
||||
mld_vif->nan.mcast_data_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.mcast_data_sta);
|
||||
|
||||
CLEANUP_STRUCT(mld_vif);
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +113,8 @@ static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
|
|||
return FW_MAC_TYPE_P2P_DEVICE;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
return FW_MAC_TYPE_IBSS;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
return FW_MAC_TYPE_NAN;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
|
@ -362,6 +383,42 @@ static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld,
|
|||
MAC_CFG_FILTER_ACCEPT_GRP);
|
||||
}
|
||||
|
||||
static int iwl_mld_fill_mac_cmd_nan(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_vif *ndi_being_added,
|
||||
struct iwl_mac_config_cmd *cmd)
|
||||
{
|
||||
struct ieee80211_vif *iter;
|
||||
u32 idx = 0;
|
||||
|
||||
cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);
|
||||
|
||||
/*
|
||||
* A NAN_DATA vif might be in the process of being added - it won't
|
||||
* be found by the iteration below since it's not yet active/in-driver.
|
||||
* In hw restart, the iteration below will find the ndi_being_added.
|
||||
*/
|
||||
if (ndi_being_added && !mld->fw_status.in_hw_restart) {
|
||||
memcpy(cmd->nan.ndi_addrs[idx].addr, ndi_being_added->addr, ETH_ALEN);
|
||||
idx++;
|
||||
}
|
||||
|
||||
for_each_active_interface(iter, mld->hw) {
|
||||
if (iter->type != NL80211_IFTYPE_NAN_DATA)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(idx >= ARRAY_SIZE(cmd->nan.ndi_addrs)))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cmd->nan.ndi_addrs[idx].addr, iter->addr, ETH_ALEN);
|
||||
idx++;
|
||||
}
|
||||
|
||||
cmd->nan.ndi_addrs_count = cpu_to_le32(idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
|
|
@ -374,16 +431,23 @@ iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
return iwl_mld_send_mac_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action)
|
||||
static int
|
||||
__iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action, struct ieee80211_vif *ndi_being_added)
|
||||
{
|
||||
struct iwl_mac_config_cmd cmd = {};
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
return 0;
|
||||
/* NAN_DATA interface type is not known to FW */
|
||||
if (WARN_ON(vif->type == NL80211_IFTYPE_NAN_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
/* ndi_being_added is only relevant for NAN and when adding a NAN_DATA interface */
|
||||
if (WARN_ON(ndi_being_added &&
|
||||
(vif->type != NL80211_IFTYPE_NAN || action != FW_CTXT_ACTION_MODIFY)))
|
||||
return -EINVAL;
|
||||
|
||||
if (action == FW_CTXT_ACTION_REMOVE)
|
||||
return iwl_mld_rm_mac_from_fw(mld, vif);
|
||||
|
|
@ -411,6 +475,11 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
case NL80211_IFTYPE_ADHOC:
|
||||
iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd);
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
ret = iwl_mld_fill_mac_cmd_nan(mld, vif, ndi_being_added, &cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "not supported yet\n");
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -419,6 +488,12 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
return iwl_mld_send_mac_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action)
|
||||
{
|
||||
return __iwl_mld_mac_fw_action(mld, vif, action, NULL);
|
||||
}
|
||||
|
||||
static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
|
||||
struct wiphy_work *wk)
|
||||
{
|
||||
|
|
@ -430,7 +505,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
|
|||
iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(vif, vif)
|
||||
IWL_MLD_ALLOC_FN_STATIC(vif, vif)
|
||||
|
||||
/* Constructor function for struct iwl_mld_vif */
|
||||
static void
|
||||
|
|
@ -456,9 +531,40 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk,
|
||||
iwl_mld_mlo_scan_start_wk);
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++) {
|
||||
memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i]));
|
||||
mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta);
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta);
|
||||
} else if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.mcast_data_sta);
|
||||
}
|
||||
|
||||
iwl_mld_init_internal_sta(&mld_vif->aux_sta);
|
||||
}
|
||||
|
||||
static int iwl_mld_update_nan_mac(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *ndi_being_added)
|
||||
{
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
|
||||
if (WARN_ON_ONCE(!vif))
|
||||
return -ENODEV;
|
||||
|
||||
mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
if (!iwl_mld_vif_fw_id_valid(mld_vif))
|
||||
return 0;
|
||||
|
||||
return __iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY,
|
||||
ndi_being_added);
|
||||
}
|
||||
|
||||
int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
|
@ -468,10 +574,14 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
|
||||
iwl_mld_init_vif(mld, vif);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
/* NAN MACs are added to FW only when a schedule is set */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
return 0;
|
||||
|
||||
/* NAN_DATA interface type is not known to FW, but we need to update NAN MAC */
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return iwl_mld_update_nan_mac(mld, vif);
|
||||
|
||||
ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -483,23 +593,52 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
|
||||
return -EINVAL;
|
||||
|
||||
ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD);
|
||||
if (ret) {
|
||||
RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mld_vif->nan.mac_added = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_update_nan_mac(mld, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iwl_mld_vif_fw_id_valid(mld_vif))
|
||||
return;
|
||||
|
||||
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
|
||||
|
||||
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
|
||||
return;
|
||||
|
||||
RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
mld_vif->nan.mac_added = false;
|
||||
|
||||
iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,
|
||||
mld_vif->fw_id);
|
||||
}
|
||||
|
|
@ -616,24 +755,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
|
|||
kfree_rcu(old_data, rcu_head);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
|
||||
struct ieee80211_vif *vif;
|
||||
|
||||
if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif),
|
||||
"mac id is invalid: %d\n", notif->mac_id))
|
||||
return;
|
||||
|
||||
vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]);
|
||||
|
||||
if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
|
||||
return;
|
||||
|
||||
IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
|
|
@ -748,6 +869,6 @@ struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)
|
|||
|
||||
fw_id = __ffs(fw_id_bitmap);
|
||||
|
||||
return wiphy_dereference(mld->wiphy,
|
||||
mld->fw_id_to_vif[fw_id]);
|
||||
return rcu_dereference_wiphy(mld->wiphy,
|
||||
mld->fw_id_to_vif[fw_id]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <net/mac80211.h>
|
||||
|
||||
#include "link.h"
|
||||
#include "nan.h"
|
||||
#include "session-protect.h"
|
||||
#include "d3.h"
|
||||
#include "fw/api/time-event.h"
|
||||
|
|
@ -137,7 +138,6 @@ struct iwl_mld_emlsr {
|
|||
* @beacon_inject_active: indicates an active debugfs beacon ie injection
|
||||
* @low_latency_causes: bit flags, indicating the causes for low-latency,
|
||||
* see @iwl_mld_low_latency_cause.
|
||||
* @ps_disabled: indicates that PS is disabled for this interface
|
||||
* @last_link_activation_time: last time a link was activated, for
|
||||
* deferring MLO scans (to make them more reliable)
|
||||
* @mld: pointer to the mld structure.
|
||||
|
|
@ -152,6 +152,16 @@ struct iwl_mld_emlsr {
|
|||
* p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use.
|
||||
* @aux_sta: station used for remain on channel. Used in P2P device.
|
||||
* @mlo_scan_start_wk: worker to start a deferred MLO scan
|
||||
* @nan: NAN parameters
|
||||
* @nan.links: NAN links for FW (indexed by FW link ID)
|
||||
* @nan.mac_added: track whether or not the MAC was added to FW
|
||||
* @nan.bcast_sta: internal station used for NAN synchronization and discovery
|
||||
* activities. No queue is associated with it.
|
||||
* @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs
|
||||
* and NAFs.
|
||||
* @nan.mcast_data_sta: internal station used for multicast NAN Data frames.
|
||||
* @nan.tx_igtk: TX IGTK key for NAN, tracked separately since NAN does not
|
||||
* use the vif links.
|
||||
*/
|
||||
struct iwl_mld_vif {
|
||||
/* Add here fields that need clean up on restart */
|
||||
|
|
@ -167,7 +177,6 @@ struct iwl_mld_vif {
|
|||
bool beacon_inject_active;
|
||||
#endif
|
||||
u8 low_latency_causes;
|
||||
bool ps_disabled;
|
||||
time64_t last_link_activation_time;
|
||||
);
|
||||
/* And here fields that survive a fw restart */
|
||||
|
|
@ -175,6 +184,16 @@ struct iwl_mld_vif {
|
|||
struct iwl_mld_link deflink;
|
||||
struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
|
||||
struct {
|
||||
/* use only with wiphy protection */
|
||||
struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS];
|
||||
bool mac_added;
|
||||
struct iwl_mld_int_sta bcast_sta;
|
||||
struct iwl_mld_int_sta mgmt_sta;
|
||||
struct iwl_mld_int_sta mcast_data_sta;
|
||||
struct ieee80211_key_conf *tx_igtk;
|
||||
} nan;
|
||||
|
||||
struct iwl_mld_emlsr emlsr;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
@ -206,6 +225,20 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
|
|||
/* Call only for interfaces that were added to the driver! */
|
||||
static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
|
||||
{
|
||||
struct ieee80211_vif *vif = iwl_mld_vif_to_mac80211(mld_vif);
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
return false;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
if (!mld_vif->nan.mac_added)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Should be added to FW */
|
||||
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif)))
|
||||
return false;
|
||||
|
||||
|
|
@ -221,6 +254,12 @@ static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
|
|||
link_id++) \
|
||||
if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id)))
|
||||
|
||||
#define for_each_mld_nan_valid_link(mld_vif, nan_link) \
|
||||
for (nan_link = &(mld_vif)->nan.links[0]; \
|
||||
nan_link < &(mld_vif)->nan.links[ARRAY_SIZE((mld_vif)->nan.links)]; \
|
||||
nan_link++) \
|
||||
if (nan_link->fw_id != FW_CTXT_ID_INVALID)
|
||||
|
||||
/* Retrieve pointer to mld link from mac80211 structures */
|
||||
static inline struct iwl_mld_link *
|
||||
iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
|
||||
|
|
@ -235,6 +274,7 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
|
|||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action);
|
||||
int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
int iwl_mld_add_nan_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
void iwl_mld_set_vif_associated(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif);
|
||||
|
|
@ -245,9 +285,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
|
|||
void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif);
|
||||
|
||||
|
|
@ -258,4 +295,19 @@ static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif)
|
|||
|
||||
struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld);
|
||||
|
||||
static inline struct iwl_mld_link_sta *
|
||||
iwl_mld_get_ap_link_sta(struct ieee80211_vif *vif, int link_id)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct ieee80211_sta *ap_sta = mld_vif->ap_sta;
|
||||
struct iwl_mld_sta *mld_ap_sta;
|
||||
|
||||
if (!ap_sta)
|
||||
return NULL;
|
||||
|
||||
mld_ap_sta = iwl_mld_sta_from_mac80211(ap_sta);
|
||||
|
||||
return iwl_mld_link_sta_dereference_check(mld_ap_sta, link_id);
|
||||
}
|
||||
|
||||
#endif /* __iwl_mld_iface_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#include "key.h"
|
||||
#include "iface.h"
|
||||
|
|
@ -12,7 +12,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
bool igtk = key->keyidx == 4 || key->keyidx == 5;
|
||||
u32 flags = 0;
|
||||
|
|
@ -38,9 +37,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!sta && vif->type == NL80211_IFTYPE_STATION)
|
||||
sta = mld_vif->ap_sta;
|
||||
|
||||
/* If we are installing an iGTK (in AP or STA mode), we need to tell
|
||||
* the firmware this key will en/decrypt MGMT frames.
|
||||
* Same goes if we are installing a pairwise key for an MFP station.
|
||||
|
|
@ -53,6 +49,12 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
|
||||
flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
|
||||
|
||||
/* When a GTK is configured for a station, it can only be
|
||||
* used for Rx and never for Tx. Thus, set the NO TX flag.
|
||||
*/
|
||||
if (!pairwise && sta)
|
||||
flags |= IWL_SEC_KEY_FLAG_NO_TX;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +69,40 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta) {
|
||||
/* Older firmware versions do not support transmission of
|
||||
* multicast data frames.
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(mld_vif->nan.mcast_data_sta.sta_id ==
|
||||
IWL_INVALID_STA))
|
||||
return 0;
|
||||
|
||||
return BIT(mld_vif->nan.mcast_data_sta.sta_id);
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN && !sta) {
|
||||
/* Older firmware versions do not support installation of
|
||||
* IGTK/BIGTK keys.
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(mld_vif->nan.bcast_sta.sta_id == IWL_INVALID_STA ||
|
||||
mld_vif->nan.mgmt_sta.sta_id == IWL_INVALID_STA))
|
||||
return 0;
|
||||
|
||||
if (key->keyidx >= 4 && key->keyidx <= 5)
|
||||
return BIT(mld_vif->nan.mgmt_sta.sta_id);
|
||||
|
||||
if (key->keyidx >= 6 && key->keyidx <= 7)
|
||||
return BIT(mld_vif->nan.bcast_sta.sta_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* AP group keys are per link and should be on the mcast/bcast STA */
|
||||
if (vif->type == NL80211_IFTYPE_AP &&
|
||||
!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
|
||||
|
|
@ -92,11 +128,7 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
|
|||
return BIT(link->mcast_sta.sta_id);
|
||||
}
|
||||
|
||||
/* for client mode use the AP STA also for group keys */
|
||||
if (!sta && vif->type == NL80211_IFTYPE_STATION)
|
||||
sta = mld_vif->ap_sta;
|
||||
|
||||
/* STA should be non-NULL now */
|
||||
/* STA should be non-NULL */
|
||||
if (WARN_ON(!sta))
|
||||
return 0;
|
||||
|
||||
|
|
@ -178,34 +210,69 @@ static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
|
|||
iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
|
||||
}
|
||||
|
||||
static struct ieee80211_key_conf **
|
||||
iwl_mld_get_igtk_ptr(struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
/* key's link ID is set to -1 for non-MLO */
|
||||
int link_id = key->link_id < 0 ? 0 : key->link_id;
|
||||
struct iwl_mld_link_sta *mld_ap_link_sta;
|
||||
struct iwl_mld_link *mld_link;
|
||||
struct iwl_mld_sta *mld_sta;
|
||||
|
||||
if (key->keyidx != 4 && key->keyidx != 5)
|
||||
return NULL;
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
if (WARN_ON(!sta))
|
||||
return NULL;
|
||||
|
||||
mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
mld_ap_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
|
||||
link_id);
|
||||
if (WARN_ON(!mld_ap_link_sta))
|
||||
return NULL;
|
||||
|
||||
return &mld_ap_link_sta->rx_igtk;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
if (sta) {
|
||||
mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
return &mld_sta->deflink.rx_igtk;
|
||||
}
|
||||
|
||||
return &mld_vif->nan.tx_igtk;
|
||||
case NL80211_IFTYPE_AP:
|
||||
mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
if (WARN_ON(!mld_link))
|
||||
return NULL;
|
||||
|
||||
return &mld_link->tx_igtk;
|
||||
default:
|
||||
WARN_ONCE(1, "invalid iftype %d for IGTK\n", vif->type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
|
||||
u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct ieee80211_key_conf **igtk_ptr;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (!sta_mask)
|
||||
return;
|
||||
|
||||
if (key->keyidx == 4 || key->keyidx == 5) {
|
||||
struct iwl_mld_link *mld_link;
|
||||
unsigned int link_id = 0;
|
||||
|
||||
/* set to -1 for non-MLO right now */
|
||||
if (key->link_id >= 0)
|
||||
link_id = key->link_id;
|
||||
|
||||
mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
if (WARN_ON(!mld_link))
|
||||
return;
|
||||
|
||||
if (mld_link->igtk == key)
|
||||
mld_link->igtk = NULL;
|
||||
|
||||
igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key);
|
||||
if (igtk_ptr && *igtk_ptr == key) {
|
||||
*igtk_ptr = NULL;
|
||||
mld->num_igtks--;
|
||||
}
|
||||
|
||||
|
|
@ -222,46 +289,43 @@ int iwl_mld_add_key(struct iwl_mld *mld,
|
|||
{
|
||||
u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
|
||||
u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_link *mld_link = NULL;
|
||||
bool igtk = key->keyidx == 4 || key->keyidx == 5;
|
||||
struct ieee80211_key_conf **igtk_ptr;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (!sta_mask)
|
||||
return -EINVAL;
|
||||
if (!sta_mask) {
|
||||
/* for NAN (GTK) indicate SW-only, it's not used at all */
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta &&
|
||||
!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 1;
|
||||
|
||||
if (igtk) {
|
||||
if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
|
||||
/* otherwise that's not valid */
|
||||
IWL_WARN(mld, "empty STA mask for key %d\n", key->keyidx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key);
|
||||
if (igtk_ptr) {
|
||||
if (mld->num_igtks == mld->fw->ucode_capa.num_mcast_key_entries)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
u8 link_id = 0;
|
||||
|
||||
/* set to -1 for non-MLO right now */
|
||||
if (key->link_id >= 0)
|
||||
link_id = key->link_id;
|
||||
|
||||
mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return -EINVAL;
|
||||
|
||||
if (mld_link->igtk) {
|
||||
if (*igtk_ptr) {
|
||||
IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
|
||||
mld_link->igtk->keyidx);
|
||||
iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
|
||||
(*igtk_ptr)->keyidx);
|
||||
iwl_mld_remove_key(mld, vif, sta, *igtk_ptr);
|
||||
}
|
||||
|
||||
WARN_ON(mld_link->igtk);
|
||||
}
|
||||
|
||||
ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
IWL_WARN(mld, "failed to add key to FW (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mld_link) {
|
||||
mld_link->igtk = key;
|
||||
if (igtk_ptr) {
|
||||
WARN_ON(*igtk_ptr);
|
||||
*igtk_ptr = key;
|
||||
mld->num_igtks++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,34 @@
|
|||
#include "fw/api/context.h"
|
||||
#include "fw/dbg.h"
|
||||
|
||||
static int iwl_mld_send_link_cmd(struct iwl_mld *mld,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
enum iwl_ctxt_action action)
|
||||
/**
|
||||
* struct iwl_mld_link_chan_load_threshold - channel load thresholds
|
||||
* @high_lim: level up transition thresholds, in percentage
|
||||
* @low_lim: level down transition thresholds, in percentage
|
||||
*/
|
||||
struct iwl_mld_link_chan_load_threshold {
|
||||
u8 high_lim;
|
||||
u8 low_lim;
|
||||
};
|
||||
|
||||
static const struct iwl_mld_link_chan_load_threshold
|
||||
link_chan_load_thresh_tbl[] = {
|
||||
[LINK_CHAN_LOAD_LVL1] = { .high_lim = 45, .low_lim = 40 },
|
||||
[LINK_CHAN_LOAD_LVL2] = { .high_lim = 70, .low_lim = 65 },
|
||||
[LINK_CHAN_LOAD_LVL3] = { .high_lim = 85, .low_lim = 80 },
|
||||
};
|
||||
|
||||
int iwl_mld_send_link_cmd(struct iwl_mld *mld,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
enum iwl_ctxt_action action)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (WARN_ON_ONCE(cmd->link_id == cpu_to_le32(FW_CTXT_ID_INVALID)))
|
||||
return -EINVAL;
|
||||
|
||||
cmd->action = cpu_to_le32(action);
|
||||
ret = iwl_mld_send_cmd_pdu(mld,
|
||||
WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
|
||||
|
|
@ -315,10 +335,28 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
|
|||
link_sta_dereference_check(mld_vif->ap_sta,
|
||||
link->link_id);
|
||||
|
||||
if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he &&
|
||||
if (WARN_ON(!link_sta))
|
||||
return -EINVAL;
|
||||
|
||||
if (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;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
link_sta->uhr_cap.mac.mac_cap[0] &
|
||||
IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP)
|
||||
flags |= LINK_FLG_DPS;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
link_sta->uhr_cap.mac.mac_cap[1] &
|
||||
IEEE80211_UHR_MAC_CAP1_DUO_SUPP)
|
||||
flags |= LINK_FLG_DUO;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
mld_vif->ap_sta->ext_mld_capa_ops &
|
||||
IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM)
|
||||
flags |= LINK_FLG_MLPM;
|
||||
}
|
||||
|
||||
cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext;
|
||||
|
|
@ -354,6 +392,21 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
|
|||
if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS))
|
||||
changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
|
||||
|
||||
if (link->uhr_support && link->npca.enabled) {
|
||||
flags |= LINK_FLG_NPCA;
|
||||
if (link->npca.moplen)
|
||||
cmd.npca_params.flags |= IWL_NPCA_FLAG_MAC_HDR_BASED;
|
||||
cmd.npca_params.dis_subch_bmap =
|
||||
cpu_to_le16(link->chanreq.oper.npca_punctured);
|
||||
cmd.npca_params.initial_qsrc = link->npca.init_qsrc;
|
||||
cmd.npca_params.min_dur_threshold = link->npca.min_dur_thresh;
|
||||
/* spec/mac80211 have these in units of 4 usec */
|
||||
cmd.npca_params.switch_delay =
|
||||
4 * link->npca.switch_delay;
|
||||
cmd.npca_params.switch_back_delay =
|
||||
4 * link->npca.switch_back_delay;
|
||||
}
|
||||
|
||||
send_cmd:
|
||||
cmd.modify_mask = cpu_to_le32(changes);
|
||||
cmd.flags = cpu_to_le32(flags);
|
||||
|
|
@ -437,7 +490,8 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
|
|||
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link, bss_conf)
|
||||
IWL_MLD_ALLOC_FN(link, bss_conf)
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_link_fw_id);
|
||||
|
||||
/* Constructor function for struct iwl_mld_link */
|
||||
static int
|
||||
|
|
@ -608,15 +662,20 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
|||
* OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
|
||||
* on current link and the link's bss_param_ch_count has changed on
|
||||
* the other link's beacon.
|
||||
*
|
||||
* When both links lose beacons, keep the primary (symmetric failure).
|
||||
* When only the current link is sick, keep the other link.
|
||||
*/
|
||||
if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
|
||||
scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
|
||||
missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
|
||||
(bss_param_ch_cnt_link_id != link_id &&
|
||||
missed_bcon >=
|
||||
IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
|
||||
if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
|
||||
scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) {
|
||||
iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
|
||||
iwl_mld_get_primary_link(vif));
|
||||
} else if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
|
||||
(bss_param_ch_cnt_link_id != link_id &&
|
||||
missed_bcon >=
|
||||
IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
|
||||
iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
|
||||
iwl_mld_get_other_link(vif, link_id));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
|
||||
|
|
@ -651,35 +710,131 @@ struct iwl_mld_rssi_to_grade {
|
|||
u16 grade;
|
||||
};
|
||||
|
||||
#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
|
||||
#define RSSI_TO_GRADE_LINE_WITH_LB(_lb, _hb_uhb, _grade) \
|
||||
{ \
|
||||
.rssi = {_lb, _hb_uhb}, \
|
||||
.grade = _grade \
|
||||
}
|
||||
|
||||
/*
|
||||
* This array must be sorted by increasing RSSI for proper functionality.
|
||||
* The grades are actually estimated throughput, represented as fixed-point
|
||||
* with a scale factor of 1/10.
|
||||
*/
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
|
||||
RSSI_TO_GRADE_LINE(-85, -89, 172),
|
||||
RSSI_TO_GRADE_LINE(-83, -86, 344),
|
||||
RSSI_TO_GRADE_LINE(-82, -85, 516),
|
||||
RSSI_TO_GRADE_LINE(-80, -83, 688),
|
||||
RSSI_TO_GRADE_LINE(-77, -79, 1032),
|
||||
RSSI_TO_GRADE_LINE(-73, -76, 1376),
|
||||
RSSI_TO_GRADE_LINE(-70, -74, 1548),
|
||||
RSSI_TO_GRADE_LINE(-69, -72, 1720),
|
||||
RSSI_TO_GRADE_LINE(-65, -68, 2064),
|
||||
RSSI_TO_GRADE_LINE(-61, -66, 2294),
|
||||
RSSI_TO_GRADE_LINE(-58, -61, 2580),
|
||||
RSSI_TO_GRADE_LINE(-55, -58, 2868),
|
||||
RSSI_TO_GRADE_LINE(-46, -55, 3098),
|
||||
RSSI_TO_GRADE_LINE(-43, -54, 3442)
|
||||
#define RSSI_TO_GRADE_LINE(_rssi, _grade) \
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(_rssi, _rssi, _grade)
|
||||
|
||||
/* Tables must be sorted by increasing RSSI */
|
||||
|
||||
/* 20 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_20mhz[] = {
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-94, -95, 9),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-92, -93, 17),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-90, -90, 34),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 52),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-83, -84, 69),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-79, -80, 103),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-75, -75, 137),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-72, -73, 155),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-70, -71, 172),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 206),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 230),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-59, -60, 258),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-57, -58, 287),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-52, -53, 310),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-50, -50, 345),
|
||||
};
|
||||
|
||||
#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
|
||||
/* 40 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_40mhz[] = {
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-95, -95, 9),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-93, -93, 17),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-91, -92, 18),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-89, -90, 34),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 68),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-84, -84, 104),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-80, -81, 138),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-77, -77, 206),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-72, -72, 274),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-69, -70, 310),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 344),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 412),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-61, -62, 460),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-56, -57, 516),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-54, -55, 574),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-49, -50, 620),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-46, -47, 690),
|
||||
};
|
||||
|
||||
/* 80 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_80mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-83, 136),
|
||||
RSSI_TO_GRADE_LINE(-80, 208),
|
||||
RSSI_TO_GRADE_LINE(-77, 276),
|
||||
RSSI_TO_GRADE_LINE(-74, 412),
|
||||
RSSI_TO_GRADE_LINE(-69, 548),
|
||||
RSSI_TO_GRADE_LINE(-67, 620),
|
||||
RSSI_TO_GRADE_LINE(-66, 688),
|
||||
RSSI_TO_GRADE_LINE(-61, 824),
|
||||
RSSI_TO_GRADE_LINE(-59, 920),
|
||||
RSSI_TO_GRADE_LINE(-54, 1032),
|
||||
RSSI_TO_GRADE_LINE(-52, 1148),
|
||||
RSSI_TO_GRADE_LINE(-47, 1240),
|
||||
RSSI_TO_GRADE_LINE(-44, 1380),
|
||||
};
|
||||
|
||||
/* 160 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_160mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-86, 72),
|
||||
RSSI_TO_GRADE_LINE(-84, 136),
|
||||
RSSI_TO_GRADE_LINE(-81, 272),
|
||||
RSSI_TO_GRADE_LINE(-78, 416),
|
||||
RSSI_TO_GRADE_LINE(-75, 552),
|
||||
RSSI_TO_GRADE_LINE(-71, 824),
|
||||
RSSI_TO_GRADE_LINE(-67, 1096),
|
||||
RSSI_TO_GRADE_LINE(-65, 1240),
|
||||
RSSI_TO_GRADE_LINE(-63, 1376),
|
||||
RSSI_TO_GRADE_LINE(-59, 1648),
|
||||
RSSI_TO_GRADE_LINE(-57, 1840),
|
||||
RSSI_TO_GRADE_LINE(-52, 2064),
|
||||
RSSI_TO_GRADE_LINE(-50, 2296),
|
||||
RSSI_TO_GRADE_LINE(-46, 2480),
|
||||
RSSI_TO_GRADE_LINE(-42, 2760),
|
||||
};
|
||||
|
||||
/* 320 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_320mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-86, 72),
|
||||
RSSI_TO_GRADE_LINE(-84, 136),
|
||||
RSSI_TO_GRADE_LINE(-83, 144),
|
||||
RSSI_TO_GRADE_LINE(-81, 272),
|
||||
RSSI_TO_GRADE_LINE(-78, 544),
|
||||
RSSI_TO_GRADE_LINE(-75, 832),
|
||||
RSSI_TO_GRADE_LINE(-72, 1104),
|
||||
RSSI_TO_GRADE_LINE(-69, 1648),
|
||||
RSSI_TO_GRADE_LINE(-64, 2192),
|
||||
RSSI_TO_GRADE_LINE(-62, 2480),
|
||||
RSSI_TO_GRADE_LINE(-61, 2752),
|
||||
RSSI_TO_GRADE_LINE(-57, 3296),
|
||||
RSSI_TO_GRADE_LINE(-55, 3680),
|
||||
RSSI_TO_GRADE_LINE(-50, 4128),
|
||||
RSSI_TO_GRADE_LINE(-47, 4592),
|
||||
RSSI_TO_GRADE_LINE(-43, 4960),
|
||||
RSSI_TO_GRADE_LINE(-40, 5520),
|
||||
};
|
||||
|
||||
#define DEFAULT_CHAN_LOAD_2GHZ 30
|
||||
#define DEFAULT_CHAN_LOAD_5GHZ 15
|
||||
|
|
@ -689,31 +844,27 @@ static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
|
|||
#define SCALE_FACTOR 256
|
||||
#define MAX_CHAN_LOAD 256
|
||||
|
||||
static unsigned int
|
||||
iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf)
|
||||
static void
|
||||
iwl_mld_apply_puncturing_penalty(const struct ieee80211_bss_conf *link_conf,
|
||||
unsigned int *grade, int bw_mhz)
|
||||
{
|
||||
enum nl80211_chan_width chan_width =
|
||||
link_conf->chanreq.oper.width;
|
||||
int mhz = nl80211_chan_width_to_mhz(chan_width);
|
||||
unsigned int n_subchannels;
|
||||
unsigned int n_punctured, n_subchannels;
|
||||
|
||||
if (WARN_ONCE(mhz < 20 || mhz > 320,
|
||||
"Invalid channel width : (%d)\n", mhz))
|
||||
return 1;
|
||||
/* Puncturing only applicable for BW >= 80 MHz */
|
||||
if (bw_mhz < 80)
|
||||
return;
|
||||
|
||||
/* total number of subchannels */
|
||||
n_subchannels = mhz / 20;
|
||||
n_punctured = hweight16(link_conf->chanreq.oper.punctured);
|
||||
if (n_punctured == 0)
|
||||
return;
|
||||
|
||||
/* No puncturing if less than 80 MHz */
|
||||
if (mhz >= 80)
|
||||
n_subchannels -= hweight16(link_conf->chanreq.oper.punctured);
|
||||
n_subchannels = bw_mhz / 20;
|
||||
|
||||
return n_subchannels;
|
||||
*grade = *grade * (n_subchannels - n_punctured) / n_subchannels;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const struct element *bss_load_elem = NULL;
|
||||
|
|
@ -787,6 +938,51 @@ int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
|
|||
return chan_load;
|
||||
}
|
||||
|
||||
/* Returns whether internal MLO Scan needs to be triggered */
|
||||
bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 new_chan_load)
|
||||
{
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
enum iwl_mld_link_chan_load_level new_lvl;
|
||||
bool scan_trig = false;
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return false;
|
||||
|
||||
/* For each Level,
|
||||
* First check if high limit threshold crosses
|
||||
* If not then, check if low limit threshold crosses
|
||||
* Set new level based on low limit thresh only if old level
|
||||
* is not lower than level threshold
|
||||
*/
|
||||
for (new_lvl = LINK_CHAN_LOAD_LVL_MAX;
|
||||
new_lvl > LINK_CHAN_LOAD_LVL_NONE; new_lvl--) {
|
||||
if (new_chan_load >=
|
||||
link_chan_load_thresh_tbl[new_lvl].high_lim)
|
||||
break;
|
||||
if (new_chan_load >=
|
||||
link_chan_load_thresh_tbl[new_lvl].low_lim &&
|
||||
mld_link->chan_load_lvl >= new_lvl)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Trigger scan only for Level Up Transition */
|
||||
if (new_lvl > mld_link->chan_load_lvl)
|
||||
scan_trig = true;
|
||||
|
||||
IWL_DEBUG_EHT(mld,
|
||||
"Link %d: chan_load=%d%%, old_lvl=%d, new_lvl=%d, scan_trig=%d\n",
|
||||
link_conf->link_id, new_chan_load,
|
||||
mld_link->chan_load_lvl, new_lvl, scan_trig);
|
||||
|
||||
/* Update computed new level */
|
||||
mld_link->chan_load_lvl = new_lvl;
|
||||
|
||||
return scan_trig;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_chan_load_requires_scan);
|
||||
|
||||
static unsigned int
|
||||
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
|
|
@ -828,16 +1024,214 @@ iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
|
|||
return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
|
||||
}
|
||||
|
||||
static s8
|
||||
iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
|
||||
const struct cfg80211_bss_ies *beacon_ies;
|
||||
const struct element *elem;
|
||||
|
||||
/* Duplicated beacon feature is only specific to 6 GHz */
|
||||
if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ,
|
||||
"Unexpected band %d\n",
|
||||
link_conf->chanreq.oper.chan->band))
|
||||
return 0;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
beacon_ies = wiphy_dereference(mld->wiphy, link_conf->bss->beacon_ies);
|
||||
if (!beacon_ies)
|
||||
return 0;
|
||||
|
||||
elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION,
|
||||
beacon_ies->data, beacon_ies->len);
|
||||
if (!elem ||
|
||||
elem->datalen < sizeof(struct ieee80211_he_operation) + 1 ||
|
||||
elem->datalen < ieee80211_he_oper_size(&elem->data[1]))
|
||||
return 0;
|
||||
|
||||
he_6ghz_oper = ieee80211_he_6ghz_oper((const void *)&elem->data[1]);
|
||||
if (!he_6ghz_oper)
|
||||
return 0;
|
||||
|
||||
if (!(he_6ghz_oper->control & IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON))
|
||||
return 0;
|
||||
|
||||
/* Apply adjustment based on operational bandwidth */
|
||||
switch (link_conf->chanreq.oper.width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
return 0;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return 3;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return 6;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
return 9;
|
||||
case NL80211_CHAN_WIDTH_320:
|
||||
return 12;
|
||||
default:
|
||||
WARN_ONCE(1, "Unexpected channel width: %d\n",
|
||||
link_conf->chanreq.oper.width);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
int bw_mhz)
|
||||
{
|
||||
int start_freq, primary_idx;
|
||||
|
||||
if (!psd->valid)
|
||||
return S8_MAX;
|
||||
|
||||
start_freq = chandef->center_freq1 - (bw_mhz / 2);
|
||||
primary_idx = (chandef->chan->center_freq - start_freq - 10) / 20;
|
||||
|
||||
if (primary_idx < 0 || primary_idx >= psd->count)
|
||||
return S8_MAX;
|
||||
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
return psd->power[primary_idx] / 2;
|
||||
}
|
||||
|
||||
static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct ieee80211_parsed_tpe *tpe = &link_conf->tpe;
|
||||
s8 psd_20mhz, psd_oper, psd_local, psd_reg, psd_boost;
|
||||
s8 min_20mhz, min_oper, adjustment, ap_power_limit;
|
||||
s8 psd_avg_local = S8_MAX, psd_avg_reg = S8_MAX;
|
||||
s8 eirp_20mhz, eirp_oper, eirp_local, eirp_reg;
|
||||
int bw_mhz, num_subchans;
|
||||
u8 bw_index;
|
||||
|
||||
/* PSD/EIRP adjustment is only specific to 6 GHz */
|
||||
if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ,
|
||||
"PSD/EIRP adjustment called for non-6 GHz band %d\n",
|
||||
link_conf->chanreq.oper.chan->band))
|
||||
return 0;
|
||||
|
||||
bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width);
|
||||
|
||||
switch (bw_mhz) {
|
||||
case 20:
|
||||
bw_index = 0;
|
||||
break;
|
||||
case 40:
|
||||
bw_index = 1;
|
||||
break;
|
||||
case 80:
|
||||
bw_index = 2;
|
||||
break;
|
||||
case 160:
|
||||
bw_index = 3;
|
||||
break;
|
||||
case 320:
|
||||
bw_index = 4;
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Unexpected bandwidth: %d MHz\n", bw_mhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (link_conf->power_type == IEEE80211_REG_VLP_AP)
|
||||
ap_power_limit = 14;
|
||||
else
|
||||
ap_power_limit = 23;
|
||||
|
||||
/* Primary 20 MHz PSD */
|
||||
psd_local = iwl_mld_get_primary_psd(&tpe->psd_local[0],
|
||||
&link_conf->chanreq.oper,
|
||||
bw_mhz);
|
||||
psd_reg = iwl_mld_get_primary_psd(&tpe->psd_reg_client[0],
|
||||
&link_conf->chanreq.oper,
|
||||
bw_mhz);
|
||||
psd_20mhz = min(psd_local, psd_reg);
|
||||
|
||||
/* TPE element stores EIRP limit as value * 2 */
|
||||
eirp_local = (tpe->max_local[0].valid && tpe->max_local[0].count > 0) ?
|
||||
tpe->max_local[0].power[0] / 2 : S8_MAX;
|
||||
eirp_reg = (tpe->max_reg_client[0].valid &&
|
||||
tpe->max_reg_client[0].count > 0) ?
|
||||
tpe->max_reg_client[0].power[0] / 2 : S8_MAX;
|
||||
eirp_20mhz = min(eirp_local, eirp_reg);
|
||||
|
||||
num_subchans = bw_mhz / 20;
|
||||
|
||||
if (tpe->psd_local[0].valid) {
|
||||
int sum_local = 0, valid_local = 0;
|
||||
int count_local = min(num_subchans, tpe->psd_local[0].count);
|
||||
|
||||
for (int i = 0; i < count_local; i++) {
|
||||
if (tpe->psd_local[0].power[i] != S8_MIN) {
|
||||
sum_local += tpe->psd_local[0].power[i];
|
||||
valid_local++;
|
||||
}
|
||||
}
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
if (valid_local > 0)
|
||||
psd_avg_local = sum_local / valid_local / 2;
|
||||
}
|
||||
|
||||
if (tpe->psd_reg_client[0].valid) {
|
||||
int sum_reg = 0, valid_reg = 0;
|
||||
int count_reg = min(num_subchans, tpe->psd_reg_client[0].count);
|
||||
|
||||
for (int i = 0; i < count_reg; i++) {
|
||||
if (tpe->psd_reg_client[0].power[i] != S8_MIN) {
|
||||
sum_reg +=
|
||||
tpe->psd_reg_client[0].power[i];
|
||||
valid_reg++;
|
||||
}
|
||||
}
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
if (valid_reg > 0)
|
||||
psd_avg_reg = sum_reg / valid_reg / 2;
|
||||
}
|
||||
|
||||
psd_oper = min(psd_avg_local, psd_avg_reg);
|
||||
|
||||
/* TPE element stores EIRP limit as value * 2 */
|
||||
eirp_local = (tpe->max_local[0].valid &&
|
||||
tpe->max_local[0].count > bw_index) ?
|
||||
tpe->max_local[0].power[bw_index] / 2 : S8_MAX;
|
||||
eirp_reg = (tpe->max_reg_client[0].valid &&
|
||||
tpe->max_reg_client[0].count > bw_index) ?
|
||||
tpe->max_reg_client[0].power[bw_index] / 2 : S8_MAX;
|
||||
eirp_oper = min(eirp_local, eirp_reg);
|
||||
|
||||
min_20mhz = min(ap_power_limit, min(eirp_20mhz, psd_20mhz));
|
||||
|
||||
/* PSD boost: 10*log10(BW/20) approximated as 3*ilog2(BW/20) */
|
||||
psd_boost = 3 * ilog2(bw_mhz / 20);
|
||||
|
||||
/* Use int for psd_oper + psd_boost to prevent s8 overflow */
|
||||
min_oper = min(ap_power_limit,
|
||||
min(eirp_oper,
|
||||
(s8)min_t(int, psd_oper + psd_boost, S8_MAX)));
|
||||
|
||||
adjustment = max(min_oper - min_20mhz, 0);
|
||||
|
||||
return adjustment;
|
||||
}
|
||||
|
||||
/* This function calculates the grade of a link. Returns 0 in error case */
|
||||
unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct iwl_mld_rssi_to_grade *grade_table;
|
||||
enum nl80211_band band;
|
||||
int rssi_idx;
|
||||
int rssi_idx, table_size, bw_mhz;
|
||||
s32 link_rssi;
|
||||
unsigned int grade = MAX_GRADE;
|
||||
unsigned int grade;
|
||||
|
||||
if (WARN_ON_ONCE(!link_conf))
|
||||
if (WARN_ON_ONCE(!link_conf || !link_conf->bss))
|
||||
return 0;
|
||||
|
||||
bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width);
|
||||
if (bw_mhz < 0)
|
||||
return 0;
|
||||
|
||||
band = link_conf->chanreq.oper.chan->band;
|
||||
|
|
@ -852,25 +1246,64 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
* For 6 GHz the RSSI of the beacons is lower than
|
||||
* the RSSI of the data.
|
||||
*/
|
||||
if (band == NL80211_BAND_6GHZ && link_rssi)
|
||||
link_rssi += 4;
|
||||
if (band == NL80211_BAND_6GHZ && link_rssi) {
|
||||
s8 rssi_adj_6g =
|
||||
iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf);
|
||||
|
||||
if (!rssi_adj_6g)
|
||||
rssi_adj_6g =
|
||||
iwl_mld_get_psd_eirp_rssi_adjust(link_conf);
|
||||
|
||||
if (!rssi_adj_6g)
|
||||
rssi_adj_6g = 4;
|
||||
|
||||
link_rssi += rssi_adj_6g;
|
||||
}
|
||||
|
||||
/* Select grading table based on operational bandwidth */
|
||||
switch (bw_mhz) {
|
||||
case 20:
|
||||
grade_table = rssi_to_grade_20mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_20mhz);
|
||||
break;
|
||||
case 40:
|
||||
grade_table = rssi_to_grade_40mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_40mhz);
|
||||
break;
|
||||
case 80:
|
||||
grade_table = rssi_to_grade_80mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_80mhz);
|
||||
break;
|
||||
case 160:
|
||||
grade_table = rssi_to_grade_160mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_160mhz);
|
||||
break;
|
||||
case 320:
|
||||
grade_table = rssi_to_grade_320mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_320mhz);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Invalid bandwidth: %d MHz\n", bw_mhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize grade to maximum value from selected table */
|
||||
grade = grade_table[table_size - 1].grade;
|
||||
|
||||
rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
|
||||
|
||||
/* No valid RSSI - take the lowest grade */
|
||||
/* No valid RSSI - take the lowest grade from selected table */
|
||||
if (!link_rssi)
|
||||
link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
|
||||
link_rssi = grade_table[0].rssi[rssi_idx];
|
||||
|
||||
IWL_DEBUG_EHT(mld,
|
||||
"Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n",
|
||||
link_conf->link_id, band,
|
||||
link_conf->chanreq.oper.width,
|
||||
"Calculating grade of link %d: band = %d, BW = %d, punct subchannels = 0x%x RSSI = %d\n",
|
||||
link_conf->link_id, band, bw_mhz,
|
||||
link_conf->chanreq.oper.punctured, link_rssi);
|
||||
|
||||
/* Get grade based on RSSI */
|
||||
for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
|
||||
const struct iwl_mld_rssi_to_grade *line =
|
||||
&rssi_to_grade_map[i];
|
||||
/* Get grade based on RSSI from the bandwidth-specific table */
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
const struct iwl_mld_rssi_to_grade *line = &grade_table[i];
|
||||
|
||||
if (link_rssi > line->rssi[rssi_idx])
|
||||
continue;
|
||||
|
|
@ -879,8 +1312,10 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* Apply the channel load and puncturing factors */
|
||||
grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR;
|
||||
grade = grade * iwl_mld_get_n_subchannels(link_conf);
|
||||
grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) /
|
||||
SCALE_FACTOR;
|
||||
|
||||
iwl_mld_apply_puncturing_penalty(link_conf, &grade, bw_mhz);
|
||||
|
||||
IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_link_h__
|
||||
#define __iwl_mld_link_h__
|
||||
|
|
@ -10,6 +10,14 @@
|
|||
#include "mld.h"
|
||||
#include "sta.h"
|
||||
|
||||
enum iwl_mld_link_chan_load_level {
|
||||
LINK_CHAN_LOAD_LVL_NONE,
|
||||
LINK_CHAN_LOAD_LVL1,
|
||||
LINK_CHAN_LOAD_LVL2,
|
||||
LINK_CHAN_LOAD_LVL3,
|
||||
LINK_CHAN_LOAD_LVL_MAX = LINK_CHAN_LOAD_LVL3
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_probe_resp_data - data for NoA/CSA updates
|
||||
* @rcu_head: used for freeing the data on update
|
||||
|
|
@ -28,14 +36,17 @@ struct iwl_probe_resp_data {
|
|||
* @rcu_head: RCU head for freeing this data.
|
||||
* @fw_id: the fw id of the link.
|
||||
* @active: if the link is active or not.
|
||||
* @avg_signal: The current average signal of beacons [dBm] retrieved from
|
||||
* firmware per-link periodic stats (STATISTICS_OPER_NOTIF).
|
||||
* @queue_params: QoS data from mac80211. This is updated with a call to
|
||||
* drv_conf_tx per each AC, and then notified once with BSS_CHANGED_QOS.
|
||||
* So we store it here and then send one link cmd for all the ACs.
|
||||
* @chan_ctx: pointer to the channel context assigned to the link. If a link
|
||||
* has an assigned channel context it means that it is active.
|
||||
* @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked.
|
||||
* @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two.
|
||||
* This tracks the one IGTK that currently exists in FW.
|
||||
* @tx_igtk: FW can only have one IGTK per MAC at a time, whereas mac80211 can
|
||||
* have two. This tracks the one IGTK that currently exists in FW, for TX
|
||||
* purposes. The RX IGTKs are tracked per station.
|
||||
* @bigtks: BIGTKs of the AP. Only valid for STA mode.
|
||||
* @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
|
||||
* @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
|
||||
|
|
@ -49,6 +60,8 @@ struct iwl_probe_resp_data {
|
|||
* @silent_deactivation: next deactivation needs to be silent.
|
||||
* @probe_resp_data: data from FW notification to store NOA related data to be
|
||||
* inserted into probe response.
|
||||
* @chan_load_lvl: current channel load level for a link, computed based on
|
||||
* channel load by others on a link.
|
||||
*/
|
||||
struct iwl_mld_link {
|
||||
struct rcu_head rcu_head;
|
||||
|
|
@ -57,11 +70,13 @@ struct iwl_mld_link {
|
|||
struct_group(zeroed_on_hw_restart,
|
||||
u8 fw_id;
|
||||
bool active;
|
||||
s8 avg_signal;
|
||||
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
|
||||
struct ieee80211_chanctx_conf __rcu *chan_ctx;
|
||||
bool he_ru_2mhz_block;
|
||||
struct ieee80211_key_conf *igtk;
|
||||
struct ieee80211_key_conf *tx_igtk;
|
||||
struct ieee80211_key_conf __rcu *bigtks[2];
|
||||
enum iwl_mld_link_chan_load_level chan_load_lvl;
|
||||
);
|
||||
/* And here fields that survive a fw restart */
|
||||
struct iwl_mld_int_sta bcast_sta;
|
||||
|
|
@ -99,6 +114,13 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link)
|
|||
/* Convert a percentage from [0,100] to [0,255] */
|
||||
#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100)
|
||||
|
||||
int iwl_mld_allocate_link_fw_id(struct iwl_mld *mld, u8 *fw_id,
|
||||
struct ieee80211_bss_conf *mac80211_ptr);
|
||||
|
||||
int iwl_mld_send_link_cmd(struct iwl_mld *mld,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
enum iwl_ctxt_action action);
|
||||
|
||||
int iwl_mld_add_link(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *bss_conf);
|
||||
void iwl_mld_remove_link(struct iwl_mld *mld,
|
||||
|
|
@ -123,10 +145,17 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
bool expect_active_link);
|
||||
|
||||
bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 new_chan_load);
|
||||
|
||||
void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -62,17 +62,16 @@ static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = {
|
|||
|
||||
static const struct ieee80211_iface_limit iwl_mld_limits_nan[] = {
|
||||
{
|
||||
.max = 2,
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_STATION),
|
||||
},
|
||||
{
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_NAN),
|
||||
},
|
||||
/* Removed when two channels are permitted */
|
||||
{
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_AP),
|
||||
.max = 2,
|
||||
.types = BIT(NL80211_IFTYPE_NAN_DATA),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -90,19 +89,13 @@ iwl_mld_iface_combinations[] = {
|
|||
.limits = iwl_mld_limits_ap,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_ap),
|
||||
},
|
||||
/* NAN combinations follow, these exclude P2P */
|
||||
/* NAN combination follow, this excludes P2P and AP */
|
||||
{
|
||||
.num_different_channels = 2,
|
||||
.max_interfaces = 3,
|
||||
.limits = iwl_mld_limits_nan,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_nan) - 1,
|
||||
},
|
||||
{
|
||||
.num_different_channels = 1,
|
||||
.num_different_channels = 3,
|
||||
.max_interfaces = 4,
|
||||
.limits = iwl_mld_limits_nan,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_nan),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = {
|
||||
|
|
@ -272,6 +265,40 @@ static void iwl_mac_hw_set_flags(struct iwl_mld *mld)
|
|||
ieee80211_hw_set(hw, TDLS_WIDER_BW);
|
||||
}
|
||||
|
||||
static void iwl_mld_hw_set_nan(struct iwl_mld *mld)
|
||||
{
|
||||
struct ieee80211_hw *hw = mld->hw;
|
||||
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN);
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN_DATA);
|
||||
|
||||
hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ);
|
||||
if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels)
|
||||
hw->wiphy->nan_supported_bands |=
|
||||
BIT(NL80211_BAND_5GHZ);
|
||||
|
||||
hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
|
||||
WIPHY_NAN_FLAGS_USERSPACE_DE;
|
||||
|
||||
hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_VHT |
|
||||
NAN_OP_MODE_PHY_MODE_HE |
|
||||
NAN_OP_MODE_160MHZ;
|
||||
|
||||
hw->wiphy->nan_capa.n_antennas =
|
||||
(hweight32(hw->wiphy->available_antennas_tx) &
|
||||
NAN_DEV_CAPA_NUM_TX_ANT_MASK) |
|
||||
((hweight32(hw->wiphy->available_antennas_rx) <<
|
||||
NAN_DEV_CAPA_NUM_RX_ANT_POS) &
|
||||
NAN_DEV_CAPA_NUM_RX_ANT_MASK);
|
||||
|
||||
/* Maximal channel switch time is 4 msec */
|
||||
hw->wiphy->nan_capa.max_channel_switch_time = 4 * USEC_PER_MSEC;
|
||||
|
||||
hw->wiphy->nan_capa.phy.ht = mld->nvm_data->nan_phy_capa.ht;
|
||||
hw->wiphy->nan_capa.phy.vht = mld->nvm_data->nan_phy_capa.vht;
|
||||
hw->wiphy->nan_capa.phy.he = mld->nvm_data->nan_phy_capa.he;
|
||||
}
|
||||
|
||||
static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
||||
{
|
||||
struct ieee80211_hw *hw = mld->hw;
|
||||
|
|
@ -334,37 +361,16 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
|||
|
||||
wiphy->hw_timestamp_max_peers = 1;
|
||||
|
||||
wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
|
||||
if (iwl_mld_nan_supported(mld)) {
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN);
|
||||
hw->wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
hw->wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations);
|
||||
|
||||
hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ);
|
||||
if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels)
|
||||
hw->wiphy->nan_supported_bands |=
|
||||
BIT(NL80211_BAND_5GHZ);
|
||||
|
||||
hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
|
||||
WIPHY_NAN_FLAGS_USERSPACE_DE;
|
||||
|
||||
hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK |
|
||||
NAN_OP_MODE_80P80MHZ |
|
||||
NAN_OP_MODE_160MHZ;
|
||||
|
||||
/* Support 2 antenna's for Tx and Rx */
|
||||
hw->wiphy->nan_capa.n_antennas = 0x22;
|
||||
|
||||
/* Maximal channel switch time is 4 msec */
|
||||
hw->wiphy->nan_capa.max_channel_switch_time = 4;
|
||||
hw->wiphy->nan_capa.dev_capabilities =
|
||||
NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED |
|
||||
NAN_DEV_CAPA_NDPE_SUPPORTED;
|
||||
} else {
|
||||
wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
/* Do not include NAN combinations */
|
||||
wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations) - 2;
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations);
|
||||
iwl_mld_hw_set_nan(mld);
|
||||
} else {
|
||||
/* Do not include NAN combination */
|
||||
wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations) - 1;
|
||||
}
|
||||
|
||||
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
|
||||
|
|
@ -414,6 +420,8 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
|||
|
||||
mld->ext_capab[0].eml_capabilities = IWL_MLD_EMLSR_CAPA;
|
||||
mld->ext_capab[0].mld_capa_and_ops = IWL_MLD_CAPA_OPS;
|
||||
mld->ext_capab[0].ext_mld_capa_and_ops =
|
||||
IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -449,22 +457,6 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld)
|
|||
|
||||
static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld)
|
||||
{
|
||||
int ratecheck;
|
||||
|
||||
/* check for rates version 3 */
|
||||
ratecheck =
|
||||
(iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) +
|
||||
(iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP,
|
||||
TLC_MNG_UPDATE_NOTIF, 0) >= 4) +
|
||||
(iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
|
||||
REPLY_RX_MPDU_CMD, 0) >= 6) +
|
||||
(iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9);
|
||||
|
||||
if (ratecheck != 0 && ratecheck != 4) {
|
||||
IWL_ERR(mld, "Firmware has inconsistent rates\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 11ax is expected to be enabled for all supported devices */
|
||||
if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable))
|
||||
return -EINVAL;
|
||||
|
|
@ -673,6 +665,28 @@ int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
if (WARN_ON(!mld->nan_device_vif)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (iwl_mld_nan_use_nan_stations(mld)) {
|
||||
struct iwl_mld_vif *mld_vif =
|
||||
iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_int_sta *sta =
|
||||
&mld_vif->nan.mcast_data_sta;
|
||||
|
||||
ret = iwl_mld_add_nan_mcast_data_sta(mld,
|
||||
vif->addr,
|
||||
sta);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the default link, but not if this is an MLD vif as that implies
|
||||
* the HW is restarting and it will be configured by change_vif_links.
|
||||
|
|
@ -739,10 +753,17 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
|
|||
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
|
||||
mld->p2p_device_vif = NULL;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
mld->nan_device_vif = NULL;
|
||||
else
|
||||
} else if (vif->type != NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_remove_link(mld, &vif->bss_conf);
|
||||
} else if (iwl_mld_nan_use_nan_stations(mld)) {
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_int_sta *sta = &mld_vif->nan.mcast_data_sta;
|
||||
|
||||
if (sta->sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_remove_nan_mcast_data_sta(mld, sta);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink);
|
||||
|
|
@ -988,6 +1009,30 @@ void iwl_mld_remove_chanctx(struct ieee80211_hw *hw,
|
|||
mld->used_phy_ids &= ~BIT(phy->fw_id);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_update_link_npca_puncturing(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct ieee80211_vif *vif;
|
||||
|
||||
for_each_active_interface(vif, hw) {
|
||||
struct ieee80211_bss_conf *link;
|
||||
int link_id;
|
||||
|
||||
for_each_vif_active_link(vif, link, link_id) {
|
||||
if (rcu_access_pointer(link->chanctx_conf) != ctx)
|
||||
continue;
|
||||
|
||||
if (!link->npca.enabled)
|
||||
continue;
|
||||
|
||||
iwl_mld_change_link_in_fw(mld, link,
|
||||
LINK_CONTEXT_MODIFY_UHR_PARAMS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void iwl_mld_change_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx, u32 changed)
|
||||
|
|
@ -1003,9 +1048,19 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw,
|
|||
IEEE80211_CHANCTX_CHANGE_CHANNEL)))
|
||||
return;
|
||||
|
||||
/* NPCA puncturing is in link API for FW */
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT) {
|
||||
iwl_mld_update_link_npca_puncturing(hw, ctx);
|
||||
changed &= ~IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT;
|
||||
}
|
||||
|
||||
/* Check if a FW update is required */
|
||||
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_AP)
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_AP ||
|
||||
changed & IEEE80211_CHANCTX_CHANGE_NPCA)
|
||||
goto update;
|
||||
|
||||
if (chandef->chan == phy->chandef.chan &&
|
||||
|
|
@ -1231,33 +1286,6 @@ int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link,
|
||||
u64 changes)
|
||||
{
|
||||
u32 link_changes = 0;
|
||||
|
||||
if (changes & BSS_CHANGED_ERP_SLOT)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO;
|
||||
|
||||
if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))
|
||||
link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS;
|
||||
|
||||
if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH))
|
||||
link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS;
|
||||
|
||||
if (changes & BSS_CHANGED_HE_BSS_COLOR)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
|
||||
|
||||
if (link_changes)
|
||||
iwl_mld_change_link_in_fw(mld, link, link_changes);
|
||||
|
||||
if (changes & BSS_CHANGED_BEACON)
|
||||
iwl_mld_update_beacon_template(mld, vif, link);
|
||||
}
|
||||
|
||||
static
|
||||
u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -1289,6 +1317,9 @@ u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld,
|
|||
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_NPCA)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_UHR_PARAMS;
|
||||
|
||||
return link_changes;
|
||||
}
|
||||
|
||||
|
|
@ -1367,6 +1398,10 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw,
|
|||
if (changes & BSS_CHANGED_MU_GROUPS)
|
||||
iwl_mld_update_mu_groups(mld, link_conf);
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
/* NAN has no links */
|
||||
break;
|
||||
default:
|
||||
/* shouldn't happen */
|
||||
WARN_ON_ONCE(1);
|
||||
|
|
@ -1382,28 +1417,6 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw,
|
|||
iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
bool workaround_required =
|
||||
iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2;
|
||||
|
||||
if (!workaround_required)
|
||||
return;
|
||||
|
||||
/* Send the device-level power commands since the
|
||||
* firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to
|
||||
* determine SMPS mode.
|
||||
*/
|
||||
if (mld_vif->ps_disabled == !enable)
|
||||
return;
|
||||
|
||||
mld_vif->ps_disabled = !enable;
|
||||
|
||||
iwl_mld_update_device_power(mld, false);
|
||||
}
|
||||
|
||||
static
|
||||
void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -1414,6 +1427,11 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
iwl_mld_nan_vif_cfg_changed(mld, vif, changes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
|
|
@ -1436,10 +1454,8 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_PS) {
|
||||
iwl_mld_smps_workaround(mld, vif, vif->cfg.ps);
|
||||
if (changes & BSS_CHANGED_PS)
|
||||
iwl_mld_update_mac_power(mld, vif, false);
|
||||
}
|
||||
|
||||
/* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */
|
||||
}
|
||||
|
|
@ -1609,7 +1625,7 @@ iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN || vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return 0;
|
||||
|
||||
link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
|
|
@ -1825,7 +1841,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
|
|||
new_state == IEEE80211_STA_AUTHORIZED) {
|
||||
ret = 0;
|
||||
|
||||
if (!sta->tdls) {
|
||||
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {
|
||||
mld_vif->authorized = true;
|
||||
|
||||
/* Ensure any block due to a non-BSS link is synced */
|
||||
|
|
@ -1846,7 +1862,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
|
|||
FW_CTXT_ACTION_MODIFY);
|
||||
if (ret)
|
||||
return ret;
|
||||
iwl_mld_smps_workaround(mld, vif, vif->cfg.ps);
|
||||
}
|
||||
|
||||
/* MFP is set by default before the station is authorized.
|
||||
|
|
@ -1891,7 +1906,6 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
|
|||
&mld_vif->mlo_scan_start_wk);
|
||||
|
||||
iwl_mld_reset_cca_40mhz_workaround(mld, vif);
|
||||
iwl_mld_smps_workaround(mld, vif, true);
|
||||
}
|
||||
|
||||
/* once we move into assoc state, need to update the FW to
|
||||
|
|
@ -2236,7 +2250,6 @@ static int iwl_mld_set_key_add(struct iwl_mld *mld,
|
|||
|
||||
ret = iwl_mld_add_key(mld, vif, sta, key);
|
||||
if (ret) {
|
||||
IWL_WARN(mld, "set key failed (%d)\n", ret);
|
||||
if (ptk_pn) {
|
||||
RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL);
|
||||
kfree(ptk_pn);
|
||||
|
|
@ -2278,7 +2291,9 @@ static void iwl_mld_set_key_remove(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* if this key was stored to be added later to the FW - free it here */
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
|
||||
(vif->type == NL80211_IFTYPE_AP ||
|
||||
vif->type == NL80211_IFTYPE_ADHOC))
|
||||
iwl_mld_free_ap_early_key(mld, key, mld_vif);
|
||||
|
||||
/* We already removed it */
|
||||
|
|
@ -2296,9 +2311,20 @@ static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw,
|
|||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* FW always needs the AP STA for client mode.
|
||||
* Note that during removal this could already
|
||||
* be NULL (mac80211 removes keys after STAs)
|
||||
* but then we'll already have removed the key
|
||||
* and set hw_key_idx = STA_KEY_IDX_INVALID.
|
||||
*/
|
||||
if (!sta && vif->type == NL80211_IFTYPE_STATION)
|
||||
sta = mld_vif->ap_sta;
|
||||
|
||||
switch (cmd) {
|
||||
case SET_KEY:
|
||||
ret = iwl_mld_set_key_add(mld, vif, sta, key);
|
||||
|
|
@ -2559,15 +2585,108 @@ iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
|
|||
ret);
|
||||
}
|
||||
|
||||
static int iwl_mld_count_free_link_ids(struct iwl_mld *mld)
|
||||
{
|
||||
int free_count = 0;
|
||||
|
||||
for (int i = 0; i < mld->fw->ucode_capa.num_links; i++) {
|
||||
if (!rcu_access_pointer(mld->fw_id_to_bss_conf[i]))
|
||||
free_count++;
|
||||
}
|
||||
|
||||
return free_count;
|
||||
}
|
||||
|
||||
static bool
|
||||
iwl_mld_chanctx_used_by_other_vif(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *chanctx_conf)
|
||||
{
|
||||
struct ieee80211_bss_conf *iter_link;
|
||||
struct ieee80211_vif *iter_vif;
|
||||
int link_id;
|
||||
|
||||
for_each_active_interface(iter_vif, hw) {
|
||||
if (vif == iter_vif)
|
||||
continue;
|
||||
|
||||
/* NAN doesn't have active links, so we don't count NAN users */
|
||||
for_each_vif_active_link(iter_vif, iter_link, link_id) {
|
||||
if (rcu_access_pointer(iter_link->chanctx_conf) ==
|
||||
chanctx_conf)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
u16 desired_links)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
int n_links = hweight16(desired_links);
|
||||
int n_add = hweight16(desired_links & ~vif->active_links);
|
||||
unsigned long to_deactivate = vif->active_links & ~desired_links;
|
||||
int free_link_ids;
|
||||
int i;
|
||||
|
||||
/* Check if HW supports the wanted number of links */
|
||||
return n_links <= iwl_mld_max_active_links(mld, vif);
|
||||
if (n_links > iwl_mld_max_active_links(mld, vif))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* During link switch, mac80211 first adds the new links, then removes
|
||||
* the old ones. This means we temporarily need extra link objects
|
||||
* during the transition. Check if we have enough free link IDs.
|
||||
*/
|
||||
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
|
||||
if (free_link_ids >= n_add)
|
||||
return true;
|
||||
|
||||
if (!mld->nan_device_vif)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Not enough free link IDs. First try to evacuate NAN from the
|
||||
* channel context of a link that is going to be deactivated.
|
||||
*/
|
||||
for_each_set_bit(i, &to_deactivate, IEEE80211_MLD_MAX_NUM_LINKS) {
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
|
||||
link_conf = link_conf_dereference_protected(vif, i);
|
||||
if (!link_conf)
|
||||
continue;
|
||||
|
||||
chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
continue;
|
||||
|
||||
if (iwl_mld_chanctx_used_by_other_vif(hw, vif, chanctx_conf))
|
||||
continue;
|
||||
|
||||
if (ieee80211_nan_try_evacuate(hw, chanctx_conf)) {
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
/*
|
||||
* Evacuation of one channel should do the job. If not,
|
||||
* something bad is happening. Don't try to evacuate more
|
||||
*/
|
||||
return free_link_ids >= n_add;
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find/evacuate any channel going to go unused, try any */
|
||||
if (ieee80211_nan_try_evacuate(hw, NULL)) {
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
if (free_link_ids >= n_add)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -2847,4 +2966,5 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
|
|||
.start_nan = iwl_mld_start_nan,
|
||||
.stop_nan = iwl_mld_stop_nan,
|
||||
.nan_change_conf = iwl_mld_nan_change_config,
|
||||
.nan_peer_sched_changed = iwl_mld_mac802111_nan_peer_sched_changed,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/cfg80211.h>
|
||||
|
|
@ -129,7 +129,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld,
|
|||
|
||||
mld->mcc_src = resp->source_id;
|
||||
|
||||
/* FM is the earliest supported and later always do puncturing */
|
||||
/* FM follows BIOS/MCC policy, WH disallows puncturing only in US/CA. */
|
||||
if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) {
|
||||
if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing,
|
||||
le16_to_cpu(resp->mcc)))
|
||||
|
|
@ -137,6 +137,15 @@ iwl_mld_get_regdomain(struct iwl_mld *mld,
|
|||
else
|
||||
__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,
|
||||
mld->hw->flags);
|
||||
} else if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) ==
|
||||
IWL_CFG_RF_TYPE_WH) {
|
||||
u16 mcc = le16_to_cpu(resp->mcc);
|
||||
|
||||
if (mcc == IWL_MCC_US || mcc == IWL_MCC_CANADA)
|
||||
ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING);
|
||||
else
|
||||
__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,
|
||||
mld->hw->flags);
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -162,7 +162,6 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
|
|||
HCMD_NAME(PHY_CONFIGURATION_CMD),
|
||||
HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
|
||||
HCMD_NAME(POWER_TABLE_CMD),
|
||||
HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
|
||||
HCMD_NAME(BEACON_NOTIFICATION),
|
||||
HCMD_NAME(BEACON_TEMPLATE_CMD),
|
||||
HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
|
||||
|
|
@ -236,6 +235,10 @@ static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = {
|
|||
HCMD_NAME(STA_REMOVE_CMD),
|
||||
HCMD_NAME(ROC_CMD),
|
||||
HCMD_NAME(NAN_CFG_CMD),
|
||||
HCMD_NAME(NAN_SCHEDULE_CMD),
|
||||
HCMD_NAME(NAN_PEER_CMD),
|
||||
HCMD_NAME(NAN_ULW_ATTR_NOTIF),
|
||||
HCMD_NAME(NAN_SCHED_UPDATE_COMPLETED_NOTIF),
|
||||
HCMD_NAME(NAN_DW_END_NOTIF),
|
||||
HCMD_NAME(NAN_JOINED_CLUSTER_NOTIF),
|
||||
HCMD_NAME(MISSED_BEACONS_NOTIF),
|
||||
|
|
@ -259,6 +262,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
|
|||
HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),
|
||||
HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
|
||||
HCMD_NAME(SEC_KEY_CMD),
|
||||
HCMD_NAME(RSC_NOTIF),
|
||||
HCMD_NAME(ESR_MODE_NOTIF),
|
||||
HCMD_NAME(MONITOR_NOTIF),
|
||||
HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
|
||||
|
|
@ -411,9 +415,6 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
|
|||
|
||||
iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir);
|
||||
|
||||
/* we'll verify later it matches between commands */
|
||||
mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11;
|
||||
|
||||
iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir);
|
||||
|
||||
iwl_mld_get_bios_tables(mld);
|
||||
|
|
@ -675,6 +676,15 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode,
|
|||
if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT &&
|
||||
mld->fw_status.running)
|
||||
mld->fw_status.in_hw_restart = true;
|
||||
|
||||
/* FW is dead. We don't want to process its notifications.
|
||||
* Right, we cancel them also in iwl_mld_stop_fw, but
|
||||
* iwl_mld_async_handlers_wk might be executed before
|
||||
* ieee80211_restart_work.
|
||||
* In addition, in case of an error during recovery,
|
||||
* iwl_mld_stop_fw might be too late.
|
||||
*/
|
||||
iwl_mld_cancel_async_notifications(mld);
|
||||
}
|
||||
|
||||
static void iwl_mld_dump_error(struct iwl_op_mode *op_mode,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_h__
|
||||
#define __iwl_mld_h__
|
||||
|
|
@ -189,7 +189,6 @@
|
|||
* TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).
|
||||
* @set_tx_ant: stores the last TX antenna bitmask set by user space (if any)
|
||||
* @set_rx_ant: stores the last RX antenna bitmask set by user space (if any)
|
||||
* @fw_rates_ver_3: FW rates are in version 3
|
||||
* @low_latency: low-latency manager.
|
||||
* @tzone: thermal zone device's data
|
||||
* @cooling_dev: cooling device's related data
|
||||
|
|
@ -299,8 +298,6 @@ struct iwl_mld {
|
|||
u8 set_tx_ant;
|
||||
u8 set_rx_ant;
|
||||
|
||||
bool fw_rates_ver_3;
|
||||
|
||||
struct iwl_mld_low_latency low_latency;
|
||||
|
||||
bool ibss_manager;
|
||||
|
|
@ -555,15 +552,24 @@ iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
|
|||
return -ENOSPC; \
|
||||
}
|
||||
|
||||
#define IWL_MLD_ALLOC_FN_STATIC(_type, _mac80211_type) \
|
||||
static IWL_MLD_ALLOC_FN(_type, _mac80211_type)
|
||||
|
||||
static inline struct ieee80211_bss_conf *
|
||||
iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
|
||||
{
|
||||
struct ieee80211_bss_conf *link;
|
||||
|
||||
if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,
|
||||
"Invalid fw_link_id: %d\n", fw_link_id))
|
||||
return NULL;
|
||||
|
||||
return wiphy_dereference(mld->wiphy,
|
||||
link = wiphy_dereference(mld->wiphy,
|
||||
mld->fw_id_to_bss_conf[fw_link_id]);
|
||||
if (IS_ERR(link))
|
||||
return NULL;
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include "mlo.h"
|
||||
#include "phy.h"
|
||||
|
|
@ -1081,8 +1081,13 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
container_of((const void *)phy, struct ieee80211_chanctx_conf,
|
||||
drv_priv);
|
||||
struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
|
||||
struct ieee80211_bss_conf *prim_link;
|
||||
u32 new_chan_load = phy->avg_channel_load_not_by_us;
|
||||
struct ieee80211_bss_conf *prim_link, *link_conf;
|
||||
unsigned int prim_link_id;
|
||||
int link_id;
|
||||
|
||||
if (!ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) <= 1)
|
||||
return;
|
||||
|
||||
prim_link_id = iwl_mld_get_primary_link(vif);
|
||||
prim_link = link_conf_dereference_protected(vif, prim_link_id);
|
||||
|
|
@ -1090,6 +1095,32 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
if (WARN_ON(!prim_link))
|
||||
return;
|
||||
|
||||
/* Evaluate MLO Internal Scan for high chan load beyond thresholds */
|
||||
for_each_vif_active_link(vif, link_conf, link_id) {
|
||||
if (rcu_access_pointer(link_conf->chanctx_conf) != chanctx)
|
||||
continue;
|
||||
|
||||
/* No QBSS IE - links will be selected based on default channel
|
||||
* load values, so the same link will be selected again.
|
||||
* No point in scan.
|
||||
*/
|
||||
if (iwl_mld_get_chan_load_from_element(mld, link_conf) < 0)
|
||||
continue;
|
||||
|
||||
if (iwl_mld_chan_load_requires_scan(mld,
|
||||
link_conf,
|
||||
new_chan_load)) {
|
||||
/* When EMLSR is active, only trigger scan based on
|
||||
* primary link
|
||||
*/
|
||||
if (iwl_mld_emlsr_active(vif) && link_conf != prim_link)
|
||||
continue;
|
||||
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
|
||||
return;
|
||||
|
||||
|
|
@ -1107,7 +1138,6 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
prim_link_id);
|
||||
} else {
|
||||
u32 old_chan_load = data->prev_chan_load_not_by_us;
|
||||
u32 new_chan_load = phy->avg_channel_load_not_by_us;
|
||||
u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx);
|
||||
|
||||
#define THRESHOLD_CROSSED(threshold) \
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@
|
|||
|
||||
#include "mld.h"
|
||||
#include "iface.h"
|
||||
#include "link.h"
|
||||
#include "mlo.h"
|
||||
#include "tlc.h"
|
||||
#include "fw/api/mac-cfg.h"
|
||||
#include "fw/api/mac.h"
|
||||
#include "fw/api/rs.h"
|
||||
|
||||
#define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
|
||||
#define IWL_NAN_RSSI_CLOSE 55
|
||||
|
|
@ -14,8 +18,16 @@
|
|||
|
||||
bool iwl_mld_nan_supported(struct iwl_mld *mld)
|
||||
{
|
||||
return fw_has_capa(&mld->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT);
|
||||
const struct iwl_fw *fw = mld->fw;
|
||||
|
||||
if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT) &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD), 0) >= 1 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD), 0) >= 1 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), 0) >= 3 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 0) >= 4 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD), 0) >= 6)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
|
||||
|
|
@ -38,6 +50,98 @@ static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
|
|||
return iwl_mld_send_cmd(mld, &hcmd);
|
||||
}
|
||||
|
||||
bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld)
|
||||
{
|
||||
/*
|
||||
* If the FW supports version 1 of the NAN config command, it means that
|
||||
* it needs to receive the station ID of the auxiliary station in the
|
||||
* NAN configuration command. Otherwise, use the NAN dedicated station
|
||||
* types.
|
||||
*/
|
||||
return iwl_fw_lookup_cmd_ver(mld->fw,
|
||||
WIDE_ID(MAC_CONF_GROUP,
|
||||
NAN_CFG_CMD), 1) != 1;
|
||||
}
|
||||
|
||||
static const struct iwl_mld_int_sta *
|
||||
iwl_mld_nan_get_mgmt_sta(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
const struct iwl_mld_int_sta *sta;
|
||||
|
||||
if (iwl_mld_nan_use_nan_stations(mld))
|
||||
sta = &mld_vif->nan.mgmt_sta;
|
||||
else
|
||||
sta = &mld_vif->aux_sta;
|
||||
|
||||
if (WARN_ON(sta->sta_id == IWL_INVALID_STA))
|
||||
return NULL;
|
||||
|
||||
return sta;
|
||||
}
|
||||
|
||||
int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
|
||||
|
||||
if (!sta)
|
||||
return IWL_MLD_INVALID_QUEUE;
|
||||
|
||||
return sta->queue_id;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_flush(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
|
||||
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
if (WARN_ON(sta->queue_id == IWL_MLD_INVALID_QUEUE))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: flush queues for sta=%u\n",
|
||||
sta->sta_id);
|
||||
|
||||
iwl_mld_flush_link_sta_txqs(mld, sta->sta_id);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_remove_stations(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
iwl_mld_nan_flush(mld, vif);
|
||||
|
||||
if (!iwl_mld_nan_use_nan_stations(mld)) {
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
return;
|
||||
}
|
||||
|
||||
iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
iwl_mld_remove_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_add_stations(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return iwl_mld_add_aux_sta(mld, &mld_vif->aux_sta);
|
||||
|
||||
ret = iwl_mld_add_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iwl_mld_add_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
if (ret)
|
||||
iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_config(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_nan_conf *conf,
|
||||
|
|
@ -114,7 +218,12 @@ static int iwl_mld_nan_config(struct iwl_mld *mld,
|
|||
conf->vendor_elems_len);
|
||||
}
|
||||
|
||||
cmd.sta_id = mld_vif->aux_sta.sta_id;
|
||||
/* FW needs to know about the station ID only with version 1 of the
|
||||
* NAN configuration command
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
cmd.sta_id = mld_vif->aux_sta.sta_id;
|
||||
|
||||
return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
|
||||
conf->extra_nan_attrs_len +
|
||||
conf->vendor_elems_len);
|
||||
|
|
@ -124,8 +233,6 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|||
struct cfg80211_nan_conf *conf)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
|
||||
int ret;
|
||||
|
||||
IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
|
||||
|
|
@ -134,19 +241,20 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iwl_mld_add_aux_sta(mld, aux_sta);
|
||||
ret = iwl_mld_nan_add_stations(mld, vif);
|
||||
if (ret)
|
||||
goto unblock_emlsr;
|
||||
|
||||
ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
|
||||
goto remove_aux;
|
||||
goto remove_stas;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_aux:
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
remove_stas:
|
||||
iwl_mld_nan_remove_stations(mld, vif);
|
||||
unblock_emlsr:
|
||||
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
|
||||
|
||||
|
|
@ -174,7 +282,6 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
|
|||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_nan_config_cmd cmd = {
|
||||
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
|
||||
};
|
||||
|
|
@ -191,8 +298,7 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
|
|||
/* assume that higher layer guarantees that no additional frames are
|
||||
* added before calling this callback
|
||||
*/
|
||||
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
iwl_mld_nan_remove_stations(mld, vif);
|
||||
|
||||
/* cancel based on object type being NAN, as the NAN objects do
|
||||
* not have a unique identifier associated with them
|
||||
|
|
@ -247,6 +353,39 @@ bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
struct iwl_nan_ulw_attr_notif *notif = (void *)pkt->data;
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: ULW attr update: len=%u\n", notif->attr_len);
|
||||
|
||||
if (IWL_FW_CHECK(mld, !mld->nan_device_vif,
|
||||
"NAN: ULW attr update without NAN vif\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
|
||||
"NAN: ULW attr update without NAN started\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld,
|
||||
notif->attr_len > IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN,
|
||||
"NAN: ULW attr update invalid len %u\n",
|
||||
notif->attr_len))
|
||||
return;
|
||||
|
||||
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
|
||||
cfg80211_nan_ulw_update(wdev, notif->attr, notif->attr_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
|
|
@ -257,7 +396,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
struct wireless_dev *wdev;
|
||||
struct ieee80211_channel *chan;
|
||||
|
||||
IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
|
||||
IWL_DEBUG_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
|
||||
|
||||
if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
|
||||
return;
|
||||
|
|
@ -266,13 +405,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
"NAN: DW end without NAN started\n"))
|
||||
return;
|
||||
|
||||
if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
|
||||
mld_vif->aux_sta.sta_id);
|
||||
|
||||
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
|
||||
iwl_mld_nan_flush(mld, mld->nan_device_vif);
|
||||
|
||||
/* TODO: currently the notification specified the band on which the DW
|
||||
* ended. Need to change that to the actual channel on which the next DW
|
||||
|
|
@ -293,6 +426,581 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
return;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!chan))
|
||||
return;
|
||||
|
||||
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
|
||||
cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_fill_rates(struct iwl_link_config_cmd *cmd)
|
||||
{
|
||||
u32 ofdm = 0;
|
||||
|
||||
/* All OFDM rates - NAN uses OFDM only, no CCK */
|
||||
ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(9) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(18) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(36) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(48) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(54) >> IWL_FIRST_OFDM_RATE;
|
||||
|
||||
cmd->ofdm_rates = cpu_to_le32(ofdm);
|
||||
cmd->short_slot = cpu_to_le32(1);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_fill_qos(struct iwl_ac_qos *ac, __le32 *qos_flags)
|
||||
{
|
||||
/* AC_BK: CWmin=15, CWmax=1023, AIFSN=7, TXOP=0 */
|
||||
ac[AC_BK].cw_min = cpu_to_le16(15);
|
||||
ac[AC_BK].cw_max = cpu_to_le16(1023);
|
||||
ac[AC_BK].aifsn = 7;
|
||||
ac[AC_BK].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BK);
|
||||
ac[AC_BK].edca_txop = 0;
|
||||
|
||||
/* AC_BE: CWmin=15, CWmax=1023, AIFSN=3, TXOP=0 */
|
||||
ac[AC_BE].cw_min = cpu_to_le16(15);
|
||||
ac[AC_BE].cw_max = cpu_to_le16(1023);
|
||||
ac[AC_BE].aifsn = 3;
|
||||
ac[AC_BE].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BE);
|
||||
ac[AC_BE].edca_txop = 0;
|
||||
|
||||
/* AC_VI: CWmin=7, CWmax=15, AIFSN=2, TXOP=3008us */
|
||||
ac[AC_VI].cw_min = cpu_to_le16(7);
|
||||
ac[AC_VI].cw_max = cpu_to_le16(15);
|
||||
ac[AC_VI].aifsn = 2;
|
||||
ac[AC_VI].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VI);
|
||||
ac[AC_VI].edca_txop = cpu_to_le16(3008);
|
||||
|
||||
/* AC_VO: CWmin=3, CWmax=7, AIFSN=2, TXOP=1504us */
|
||||
ac[AC_VO].cw_min = cpu_to_le16(3);
|
||||
ac[AC_VO].cw_max = cpu_to_le16(7);
|
||||
ac[AC_VO].aifsn = 2;
|
||||
ac[AC_VO].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VO);
|
||||
ac[AC_VO].edca_txop = cpu_to_le16(1504);
|
||||
|
||||
*qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_nan_link_prep_cmd(struct iwl_mld *mld,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
u32 modify_flags)
|
||||
{
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
|
||||
if (WARN_ON_ONCE(!vif))
|
||||
return;
|
||||
|
||||
mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
|
||||
if (!nan_link->chanctx) {
|
||||
cmd->phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
|
||||
} else {
|
||||
struct iwl_mld_phy *mld_phy;
|
||||
|
||||
mld_phy = iwl_mld_phy_from_mac80211(nan_link->chanctx);
|
||||
cmd->phy_id = cpu_to_le32(mld_phy->fw_id);
|
||||
}
|
||||
|
||||
if (modify_flags & LINK_CONTEXT_MODIFY_RATES_INFO)
|
||||
iwl_mld_nan_fill_rates(cmd);
|
||||
|
||||
if (modify_flags & LINK_CONTEXT_MODIFY_QOS_PARAMS)
|
||||
iwl_mld_nan_fill_qos(cmd->ac, &cmd->qos_flags);
|
||||
|
||||
cmd->link_id = cpu_to_le32(nan_link->fw_id);
|
||||
cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
|
||||
cmd->active = cpu_to_le32(nan_link->active);
|
||||
|
||||
ether_addr_copy(cmd->local_link_addr, vif->addr);
|
||||
|
||||
cmd->modify_mask = cpu_to_le32(modify_flags);
|
||||
}
|
||||
|
||||
static struct iwl_mld_nan_link *
|
||||
iwl_mld_nan_link_add(struct iwl_mld *mld,
|
||||
struct iwl_mld_vif *mld_vif,
|
||||
struct ieee80211_chanctx_conf *chanctx)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
struct iwl_link_config_cmd cmd;
|
||||
u8 fw_id;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
ret = iwl_mld_allocate_link_fw_id(mld, &fw_id, ERR_PTR(-ENODEV));
|
||||
/*
|
||||
* We should always have enough links. The schedule contains up to 3,
|
||||
* and the BSS vif cannot do EMLSR - so can only have 1.
|
||||
*/
|
||||
if (WARN_ON(ret < 0))
|
||||
return NULL;
|
||||
|
||||
nan_link = &mld_vif->nan.links[fw_id];
|
||||
|
||||
if (WARN_ON_ONCE(nan_link->fw_id != FW_CTXT_ID_INVALID))
|
||||
goto err;
|
||||
|
||||
nan_link->fw_id = fw_id;
|
||||
nan_link->chanctx = chanctx;
|
||||
|
||||
iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
|
||||
LINK_CONTEXT_MODIFY_RATES_INFO |
|
||||
LINK_CONTEXT_MODIFY_QOS_PARAMS);
|
||||
|
||||
ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
|
||||
if (ret) {
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
nan_link->chanctx = NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return nan_link;
|
||||
err:
|
||||
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
bool active)
|
||||
{
|
||||
struct iwl_link_config_cmd cmd;
|
||||
struct ieee80211_sta *sta;
|
||||
int ret;
|
||||
|
||||
if (nan_link->active == active)
|
||||
return 0;
|
||||
|
||||
if (active) {
|
||||
for_each_station(sta, mld->hw) {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_config_tlc(mld, mld_sta->vif, sta);
|
||||
}
|
||||
}
|
||||
|
||||
nan_link->active = active;
|
||||
|
||||
iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
|
||||
LINK_CONTEXT_MODIFY_ACTIVE);
|
||||
|
||||
ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
|
||||
if (ret) {
|
||||
nan_link->active = !nan_link->active;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
nan_link->chanctx = NULL;
|
||||
/* TODO: when FW is ready, Update phy in TLC to invalid after */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_link_remove(struct iwl_mld *mld,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
u32 link_id)
|
||||
{
|
||||
struct iwl_link_config_cmd cmd = {
|
||||
.link_id = cpu_to_le32(link_id),
|
||||
.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID),
|
||||
};
|
||||
|
||||
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
|
||||
|
||||
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link_id], NULL);
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
nan_link->active = false;
|
||||
nan_link->chanctx = NULL;
|
||||
}
|
||||
|
||||
static bool iwl_mld_nan_have_links(struct iwl_mld_vif *mld_vif)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct iwl_mld_nan_link *
|
||||
iwl_mld_nan_find_link(struct iwl_mld_vif *mld_vif,
|
||||
struct ieee80211_chanctx_conf *chanctx)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (nan_link->chanctx == chanctx)
|
||||
return nan_link;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_set_mcast_data_links(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
/* Note that all errors are handled internally so nothing to do
|
||||
* with the return value (used only to silence compilation warnings)
|
||||
*/
|
||||
iwl_mld_update_nan_mcast_data_sta(mld_vif->mld, vif->addr,
|
||||
&mld_vif->nan.mcast_data_sta);
|
||||
}
|
||||
|
||||
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
u64 changes)
|
||||
{
|
||||
struct iwl_nan_schedule_cmd cmd = {};
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
bool previously_empty_schedule = !iwl_mld_nan_have_links(mld_vif);
|
||||
struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
|
||||
struct iwl_mld_nan_link *links[ARRAY_SIZE(sched_cfg->channels)] = {};
|
||||
struct ieee80211_nan_channel **slots = sched_cfg->schedule;
|
||||
bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {};
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
unsigned long remove_link_ids = 0;
|
||||
bool added_links = false;
|
||||
bool empty_schedule = true;
|
||||
int ret, i;
|
||||
u16 cmd_size;
|
||||
u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD);
|
||||
u8 version = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
|
||||
|
||||
if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED))
|
||||
return;
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
if (sched_cfg->deferred) {
|
||||
IWL_ERR(mld,
|
||||
"NAN: deferred schedule not supported by FW\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_size = sizeof(struct iwl_nan_schedule_cmd_v1);
|
||||
break;
|
||||
case 2:
|
||||
cmd_size = sizeof(struct iwl_nan_schedule_cmd);
|
||||
|
||||
if (sched_cfg->deferred)
|
||||
cmd.deferred = 1;
|
||||
|
||||
if (sched_cfg->avail_blob_len &&
|
||||
!WARN_ON(sched_cfg->avail_blob_len >
|
||||
sizeof(cmd.avail_attr.attr))) {
|
||||
cmd.avail_attr.attr_len = sched_cfg->avail_blob_len;
|
||||
memcpy(cmd.avail_attr.attr, sched_cfg->avail_blob,
|
||||
sched_cfg->avail_blob_len);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mld, "NAN: unsupported NAN schedule cmd version %d\n",
|
||||
version);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
if (!sched_cfg->channels[i].chanreq.oper.chan)
|
||||
continue;
|
||||
empty_schedule = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* add the MAC if needed (before adding links) */
|
||||
if (!empty_schedule && previously_empty_schedule) {
|
||||
WARN_ON(mld_vif->nan.mac_added);
|
||||
ret = iwl_mld_add_nan_vif(mld, vif);
|
||||
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "NAN: failed to add MAC (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mld_vif->nan.mac_added) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* this currently just uses the same index */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sched_cfg->channels) !=
|
||||
ARRAY_SIZE(cmd.channels));
|
||||
|
||||
/*
|
||||
* mac80211 removes unused channels before adding new ones, so it may
|
||||
* update an empty schedule with an availability attribute because it
|
||||
* is going to add channels later. Since the firmware does not expect
|
||||
* an availability attribute without channels, ignore it in that case.
|
||||
*/
|
||||
if (empty_schedule)
|
||||
cmd.avail_attr.attr_len = 0;
|
||||
|
||||
/* find links we can keep (same chanctx/PHY) */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
struct iwl_mld_nan_link *link;
|
||||
|
||||
chanctx = sched_cfg->channels[i].chanctx_conf;
|
||||
/* ULW */
|
||||
if (!chanctx)
|
||||
continue;
|
||||
|
||||
link = iwl_mld_nan_find_link(mld_vif, chanctx);
|
||||
links[i] = link;
|
||||
if (link)
|
||||
link_used[link->fw_id] = true;
|
||||
}
|
||||
|
||||
/* add/reassign links for new channels */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
|
||||
/* already have an existing active link */
|
||||
if (links[i])
|
||||
continue;
|
||||
|
||||
chanctx = sched_cfg->channels[i].chanctx_conf;
|
||||
/* ULW or unused slot */
|
||||
if (!chanctx)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* if this fails we still update the schedule, but
|
||||
* without a valid link we'll always ULW it
|
||||
*/
|
||||
links[i] = iwl_mld_nan_link_add(mld, mld_vif, chanctx);
|
||||
|
||||
/* we have a link, activate it */
|
||||
if (links[i]) {
|
||||
added_links = true;
|
||||
link_used[links[i]->fw_id] = true;
|
||||
iwl_mld_nan_link_set_active(mld, vif, links[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill the command */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
cmd.channels[i].link_id = FW_CTXT_ID_INVALID;
|
||||
|
||||
if (!sched_cfg->channels[i].chanreq.oper.chan)
|
||||
continue;
|
||||
|
||||
memcpy(cmd.channels[i].channel_entry,
|
||||
sched_cfg->channels[i].channel_entry, 6);
|
||||
cmd.channels[i].link_id =
|
||||
links[i] ? links[i]->fw_id : FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
for (i = 0; i < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; i++) {
|
||||
int chan_idx;
|
||||
|
||||
if (!slots[i])
|
||||
continue;
|
||||
|
||||
chan_idx = slots[i] - sched_cfg->channels;
|
||||
if (WARN_ON_ONCE(chan_idx < 0 ||
|
||||
chan_idx >= ARRAY_SIZE(cmd.channels)))
|
||||
continue;
|
||||
|
||||
cmd.channels[chan_idx].availability_map |= cpu_to_le32(BIT(i));
|
||||
}
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "NAN: failed to update schedule (%d)\n", ret);
|
||||
|
||||
/* prepare stations for links we'll remove */
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (!link_used[nan_link->fw_id]) {
|
||||
iwl_mld_nan_link_set_active(mld, vif, nan_link, false);
|
||||
remove_link_ids |= BIT(nan_link->fw_id);
|
||||
/* mark unused for STA updates */
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (added_links || remove_link_ids) {
|
||||
struct ieee80211_sta *sta;
|
||||
|
||||
for_each_station(sta, mld->hw) {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI ||
|
||||
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_add_modify_sta_cmd(mld, &sta->deflink);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all the NAN Data interfaces and update the links
|
||||
* for the internal multicast data station.
|
||||
* In recovery - the station will be added later in
|
||||
* drv_add_interface
|
||||
*/
|
||||
if (iwl_mld_nan_use_nan_stations(mld) && !mld->fw_status.in_hw_restart) {
|
||||
struct ieee80211_vif *iter;
|
||||
|
||||
for_each_active_interface(iter, mld->hw)
|
||||
iwl_mld_nan_set_mcast_data_links(iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete unused links */
|
||||
for_each_set_bit(i, &remove_link_ids, ARRAY_SIZE(mld_vif->nan.links))
|
||||
iwl_mld_nan_link_remove(mld, &mld_vif->nan.links[i], i);
|
||||
|
||||
/* remove MAC if needed */
|
||||
if (!previously_empty_schedule && empty_schedule) {
|
||||
/* must have been added */
|
||||
WARN_ON_ONCE(!mld_vif->nan.mac_added);
|
||||
|
||||
/* mac80211 should reconfigure same state */
|
||||
if (!WARN_ON_ONCE(mld->fw_status.in_hw_restart &&
|
||||
!iwl_mld_error_before_recovery(mld)))
|
||||
iwl_mld_rm_vif(mld, vif);
|
||||
}
|
||||
}
|
||||
|
||||
bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
struct iwl_nan_sched_update_completed_notif *notif = (void *)pkt->data;
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !vif,
|
||||
"NAN: schedule update completed without NAN vif\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(vif),
|
||||
"NAN: schedule update completed without NAN started\n"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Deferred schedule update should not fail in firmware since all
|
||||
* channels and links were added.
|
||||
*/
|
||||
IWL_FW_CHECK(mld, notif->status != IWL_NAN_SCHED_UPDATE_SUCCESS,
|
||||
"NAN: deferred schedule update failed\n");
|
||||
|
||||
if (WARN_ON(!vif->cfg.nan_sched.deferred))
|
||||
return;
|
||||
|
||||
ieee80211_nan_sched_update_done(vif);
|
||||
}
|
||||
|
||||
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct ieee80211_nan_peer_sched *sched = sta->nan_sched;
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
struct iwl_nan_peer_cmd cmd = {
|
||||
.nmi_sta_id = mld_sta->deflink.fw_id,
|
||||
.sequence_id = sched->seq_id,
|
||||
.committed_dw_info = cpu_to_le16(sched->committed_dw),
|
||||
.max_channel_switch_time = cpu_to_le16(sched->max_chan_switch),
|
||||
.initial_ulw_size = cpu_to_le32(sched->ulw_size),
|
||||
.per_phy[0 ... NUM_PHY_CTX - 1] = {
|
||||
/* unused by FW if availability_map == 0 */
|
||||
.map_id = CFG80211_NAN_INVALID_MAP_ID,
|
||||
.link_id = FW_CTXT_ID_INVALID,
|
||||
},
|
||||
/* .initial_ulw directly provided below by data[1]/len[1] */
|
||||
};
|
||||
struct iwl_host_cmd hcmd = {
|
||||
.id = WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD),
|
||||
.data[0] = &cmd,
|
||||
.len[0] = sizeof(cmd),
|
||||
.data[1] = sched->init_ulw,
|
||||
.len[1] = sched->ulw_size,
|
||||
.dataflags[1] = IWL_HCMD_DFL_DUP,
|
||||
};
|
||||
struct ieee80211_sta *iter;
|
||||
|
||||
/* Update TLC in case peer channels were added/removed/updated */
|
||||
for_each_station(iter, mld->hw) {
|
||||
struct iwl_mld_sta *tmp = iwl_mld_sta_from_mac80211(iter);
|
||||
|
||||
if (tmp->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_config_tlc(mld, tmp->vif, iter);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(sched->maps); i++) {
|
||||
if (sched->maps[i].map_id == CFG80211_NAN_INVALID_MAP_ID)
|
||||
continue;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sched->maps[i].slots) != 32);
|
||||
for (int slot = 0;
|
||||
slot < ARRAY_SIZE(sched->maps[i].slots);
|
||||
slot++) {
|
||||
struct ieee80211_chanctx_conf *ctx;
|
||||
struct ieee80211_nan_channel *chan;
|
||||
struct iwl_mld_phy *phy;
|
||||
|
||||
chan = sched->maps[i].slots[slot];
|
||||
if (!chan)
|
||||
continue;
|
||||
|
||||
ctx = chan->chanctx_conf;
|
||||
if (!ctx)
|
||||
continue;
|
||||
|
||||
phy = iwl_mld_phy_from_mac80211(ctx);
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (nan_link->chanctx == ctx) {
|
||||
cmd.per_phy[phy->fw_id].link_id =
|
||||
nan_link->fw_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(cmd.per_phy[phy->fw_id].link_id ==
|
||||
FW_CTXT_ID_INVALID))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* each channel can only appear in one map,
|
||||
* upper layers enforce that
|
||||
*/
|
||||
if (WARN_ON(cmd.per_phy[phy->fw_id].map_id != CFG80211_NAN_INVALID_MAP_ID &&
|
||||
cmd.per_phy[phy->fw_id].map_id != sched->maps[i].map_id))
|
||||
continue;
|
||||
|
||||
cmd.per_phy[phy->fw_id].map_id = sched->maps[i].map_id;
|
||||
memcpy(cmd.per_phy[phy->fw_id].channel_entry,
|
||||
chan->channel_entry,
|
||||
sizeof(cmd.per_phy[phy->fw_id].channel_entry));
|
||||
cmd.per_phy[phy->fw_id].availability_map |=
|
||||
cpu_to_le32(BIT(slot));
|
||||
}
|
||||
}
|
||||
|
||||
return iwl_mld_send_cmd(mld, &hcmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_nan_h__
|
||||
#define __iwl_mld_nan_h__
|
||||
#include <net/cfg80211.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
/**
|
||||
* struct iwl_mld_nan_link - struct representing a NAN link
|
||||
* @chanctx: the channel context
|
||||
* @active: indicates the NAN link is currently active
|
||||
* @fw_id: FW link ID
|
||||
*/
|
||||
struct iwl_mld_nan_link {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
bool active;
|
||||
u8 fw_id;
|
||||
};
|
||||
|
||||
/* Cleanup function for struct iwl_mld_nan_link, will be called in restart */
|
||||
static inline void iwl_mld_cleanup_nan_link(struct iwl_mld_nan_link *nan_link)
|
||||
{
|
||||
memset(nan_link, 0, sizeof(*nan_link));
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
bool iwl_mld_nan_supported(struct iwl_mld *mld);
|
||||
int iwl_mld_start_nan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -19,13 +38,33 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
|
|||
struct ieee80211_vif *vif);
|
||||
void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
u64 changes);
|
||||
|
||||
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *sta);
|
||||
|
||||
int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
|
||||
bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld);
|
||||
|
||||
#endif /* __iwl_mld_nan_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -307,11 +307,8 @@ CMD_VERSIONS(tx_resp_notif,
|
|||
CMD_VER_ENTRY(8, iwl_tx_resp)
|
||||
CMD_VER_ENTRY(9, iwl_tx_resp))
|
||||
CMD_VERSIONS(compressed_ba_notif,
|
||||
CMD_VER_ENTRY(5, iwl_compressed_ba_notif)
|
||||
CMD_VER_ENTRY(6, iwl_compressed_ba_notif)
|
||||
CMD_VER_ENTRY(7, iwl_compressed_ba_notif))
|
||||
CMD_VERSIONS(tlc_notif,
|
||||
CMD_VER_ENTRY(3, iwl_tlc_update_notif)
|
||||
CMD_VER_ENTRY(4, iwl_tlc_update_notif))
|
||||
CMD_VERSIONS(mu_mimo_grp_notif,
|
||||
CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif))
|
||||
|
|
@ -330,7 +327,8 @@ CMD_VERSIONS(probe_resp_data_notif,
|
|||
CMD_VERSIONS(datapath_monitor_notif,
|
||||
CMD_VER_ENTRY(1, iwl_datapath_monitor_notif))
|
||||
CMD_VERSIONS(stats_oper_notif,
|
||||
CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper))
|
||||
CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper_v3)
|
||||
CMD_VER_ENTRY(4, iwl_system_statistics_notif_oper))
|
||||
CMD_VERSIONS(stats_oper_part1_notif,
|
||||
CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper))
|
||||
CMD_VERSIONS(bt_coex_notif,
|
||||
|
|
@ -341,8 +339,6 @@ CMD_VERSIONS(emlsr_mode_notif,
|
|||
CMD_VER_ENTRY(2, iwl_esr_mode_notif))
|
||||
CMD_VERSIONS(emlsr_trans_fail_notif,
|
||||
CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))
|
||||
CMD_VERSIONS(uapsd_misbehaving_ap_notif,
|
||||
CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif))
|
||||
CMD_VERSIONS(time_msmt_notif,
|
||||
CMD_VER_ENTRY(1, iwl_time_msmt_notify))
|
||||
CMD_VERSIONS(time_sync_confirm_notif,
|
||||
|
|
@ -350,7 +346,10 @@ CMD_VERSIONS(time_sync_confirm_notif,
|
|||
CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(10, iwl_tof_range_rsp_ntfy))
|
||||
CMD_VERSIONS(beacon_filter_notif, CMD_VER_ENTRY(2, iwl_beacon_filter_notif))
|
||||
CMD_VERSIONS(nan_cluster_notif, CMD_VER_ENTRY(1, iwl_nan_cluster_notif))
|
||||
CMD_VERSIONS(nan_ulw_attr_notif, CMD_VER_ENTRY(1, iwl_nan_ulw_attr_notif))
|
||||
CMD_VERSIONS(nan_dw_end_notif, CMD_VER_ENTRY(1, iwl_nan_dw_end_notif))
|
||||
CMD_VERSIONS(nan_sched_update_completed_notif,
|
||||
CMD_VER_ENTRY(1, iwl_nan_sched_update_completed_notif))
|
||||
|
||||
DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id)
|
||||
|
|
@ -365,8 +364,6 @@ DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
|
|||
DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
|
||||
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
|
||||
mac_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
|
||||
mac_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id)
|
||||
|
||||
|
|
@ -457,8 +454,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
|
|||
emlsr_mode_notif, RX_HANDLER_ASYNC)
|
||||
RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF,
|
||||
emlsr_trans_fail_notif, RX_HANDLER_ASYNC)
|
||||
RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
|
||||
uapsd_misbehaving_ap_notif)
|
||||
RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
|
||||
WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION,
|
||||
time_msmt_notif, RX_HANDLER_SYNC)
|
||||
|
|
@ -471,8 +466,12 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
|
|||
ftm_resp_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_JOINED_CLUSTER_NOTIF,
|
||||
nan_cluster_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_ULW_ATTR_NOTIF,
|
||||
nan_ulw_attr_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_DW_END_NOTIF,
|
||||
nan_dw_end_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_SCHED_UPDATE_COMPLETED_NOTIF,
|
||||
nan_sched_update_completed_notif)
|
||||
};
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers);
|
||||
|
||||
|
|
@ -607,6 +606,11 @@ void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
|
|||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RX_QUEUES_NOTIFICATION)))
|
||||
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RSC_NOTIF)))
|
||||
iwl_mld_handle_rsc_notif(mld, pkt, 0);
|
||||
#endif
|
||||
else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, PHY_AIR_SNIFFER_NOTIF))
|
||||
iwl_mld_handle_phy_air_sniffer_notif(mld, napi, pkt);
|
||||
else
|
||||
|
|
@ -630,6 +634,11 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
|
|||
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue);
|
||||
else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))
|
||||
iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RSC_NOTIF)))
|
||||
iwl_mld_handle_rsc_notif(mld, pkt, queue);
|
||||
#endif
|
||||
}
|
||||
|
||||
void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)
|
||||
|
|
@ -685,10 +694,6 @@ void iwl_mld_cancel_async_notifications(struct iwl_mld *mld)
|
|||
{
|
||||
struct iwl_async_handler_entry *entry, *tmp;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk);
|
||||
|
||||
spin_lock_bh(&mld->async_handlers_lock);
|
||||
list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
|
||||
iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/mac80211.h>
|
||||
|
||||
|
|
@ -99,9 +99,9 @@ iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)
|
|||
/* Maps the driver specific control channel position (relative to the center
|
||||
* freq) definitions to the fw values
|
||||
*/
|
||||
u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
|
||||
static u8 _iwl_mld_get_fw_ctrl_pos(u32 control, u32 cf1)
|
||||
{
|
||||
int offs = chandef->chan->center_freq - chandef->center_freq1;
|
||||
int offs = control - cf1;
|
||||
int abs_offs = abs(offs);
|
||||
u8 ret;
|
||||
|
||||
|
|
@ -127,6 +127,12 @@ u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
|
|||
return ret;
|
||||
}
|
||||
|
||||
u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
return _iwl_mld_get_fw_ctrl_pos(chandef->chan->center_freq,
|
||||
chandef->center_freq1);
|
||||
}
|
||||
|
||||
int iwl_mld_phy_fw_action(struct iwl_mld *mld,
|
||||
struct ieee80211_chanctx_conf *ctx, u32 action)
|
||||
{
|
||||
|
|
@ -150,6 +156,18 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld,
|
|||
cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set NPCA channel if NPCA is used; if not used, just set it to an
|
||||
* arbitrary channel on the other side to help firmware.
|
||||
*/
|
||||
if (chandef->npca_chan)
|
||||
cmd.secondary_ctrl_chnl_loc =
|
||||
_iwl_mld_get_fw_ctrl_pos(chandef->npca_chan->center_freq,
|
||||
chandef->center_freq1);
|
||||
else
|
||||
cmd.secondary_ctrl_chnl_loc =
|
||||
cmd.ci.ctrl_pos ^ IWL_PHY_CTRL_POS_ABOVE;
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/mac80211.h>
|
||||
|
||||
|
|
@ -11,36 +11,11 @@
|
|||
#include "link.h"
|
||||
#include "constants.h"
|
||||
|
||||
static void iwl_mld_vif_ps_iterator(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
bool *ps_enable = (bool *)data;
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
*ps_enable &= !mld_vif->ps_disabled;
|
||||
}
|
||||
|
||||
int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3)
|
||||
{
|
||||
struct iwl_device_power_cmd cmd = {};
|
||||
bool enable_ps = false;
|
||||
|
||||
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) {
|
||||
enable_ps = true;
|
||||
|
||||
/* Disable power save if any STA interface has
|
||||
* power save turned off
|
||||
*/
|
||||
ieee80211_iterate_active_interfaces_mtx(mld->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mld_vif_ps_iterator,
|
||||
&enable_ps);
|
||||
}
|
||||
|
||||
if (enable_ps)
|
||||
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
|
||||
cmd.flags |=
|
||||
cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
|
||||
|
||||
|
|
@ -113,10 +88,10 @@ static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
|
|||
return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;
|
||||
}
|
||||
|
||||
static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
|
||||
struct iwl_mld_link *link,
|
||||
struct iwl_mac_power_cmd *cmd,
|
||||
bool ps_poll)
|
||||
static void iwl_mld_power_configure_uapsd_v2(struct iwl_mld *mld,
|
||||
struct iwl_mld_link *link,
|
||||
struct iwl_mac_power_cmd_v2 *cmd,
|
||||
bool ps_poll)
|
||||
{
|
||||
bool tid_found = false;
|
||||
|
||||
|
|
@ -175,10 +150,54 @@ static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
|
|||
cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
|
||||
}
|
||||
|
||||
static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
|
||||
struct iwl_mld_link *link,
|
||||
struct iwl_mac_power_cmd *cmd,
|
||||
bool ps_poll)
|
||||
{
|
||||
bool tid_found = false;
|
||||
|
||||
/* set advanced pm flag with no uapsd ACs to enable ps-poll */
|
||||
if (ps_poll) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
|
||||
return;
|
||||
}
|
||||
|
||||
for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;
|
||||
ac <= IEEE80211_AC_BK;
|
||||
ac++) {
|
||||
if (!link->queue_params[ac].uapsd)
|
||||
continue;
|
||||
|
||||
cmd->flags |=
|
||||
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
|
||||
cmd->uapsd_ac_flags |= BIT(ac);
|
||||
|
||||
/* QNDP TID - the highest TID with no admission control */
|
||||
if (!tid_found && !link->queue_params[ac].acm) {
|
||||
tid_found = true;
|
||||
switch (ac) {
|
||||
case IEEE80211_AC_VO:
|
||||
cmd->qndp_tid = 6;
|
||||
break;
|
||||
case IEEE80211_AC_VI:
|
||||
cmd->qndp_tid = 5;
|
||||
break;
|
||||
case IEEE80211_AC_BE:
|
||||
cmd->qndp_tid = 0;
|
||||
break;
|
||||
case IEEE80211_AC_BK:
|
||||
cmd->qndp_tid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
|
||||
const struct ieee80211_bss_conf *link_conf,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
u8 *skip_dtim_periods, __le16 *flags)
|
||||
{
|
||||
unsigned int dtimper_tu;
|
||||
unsigned int dtimper;
|
||||
|
|
@ -196,15 +215,15 @@ iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
|
|||
/* configure skip over dtim up to 900 TU DTIM interval */
|
||||
skip = max_t(int, 1, 900 / dtimper_tu);
|
||||
|
||||
cmd->skip_dtim_periods = skip;
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
|
||||
*skip_dtim_periods = skip;
|
||||
*flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
|
||||
}
|
||||
|
||||
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
|
||||
static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd,
|
||||
bool d3)
|
||||
static void iwl_mld_power_build_cmd_v2(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd_v2 *cmd,
|
||||
bool d3)
|
||||
{
|
||||
int dtimper, bi;
|
||||
int keep_alive;
|
||||
|
|
@ -252,9 +271,7 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
return;
|
||||
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
|
||||
|
||||
if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2)
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
|
||||
|
||||
/* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */
|
||||
if (link_conf->beacon_rate &&
|
||||
|
|
@ -265,7 +282,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
if (d3) {
|
||||
iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
|
||||
iwl_mld_power_config_skip_dtim(mld, link_conf,
|
||||
&cmd->skip_dtim_periods,
|
||||
&cmd->flags);
|
||||
cmd->rx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout =
|
||||
|
|
@ -286,6 +305,95 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
* mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
|
||||
* which will look at what mac80211 is saying.
|
||||
*/
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
ps_poll = mld_vif->use_ps_poll;
|
||||
#endif
|
||||
iwl_mld_power_configure_uapsd_v2(mld, link, cmd, ps_poll);
|
||||
}
|
||||
|
||||
static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd,
|
||||
bool d3)
|
||||
{
|
||||
int dtimper, bi;
|
||||
int keep_alive;
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
|
||||
struct iwl_mld_link *link = &mld_vif->deflink;
|
||||
bool ps_poll = false;
|
||||
__le32 fw_id = cpu_to_le32(mld_vif->fw_id);
|
||||
|
||||
if (ieee80211_vif_is_mld(vif)) {
|
||||
int link_id;
|
||||
|
||||
if (WARN_ON(!vif->active_links))
|
||||
return;
|
||||
|
||||
/* The firmware consumes one single configuration for the vif
|
||||
* and can't differentiate between links, just pick the lowest
|
||||
* link_id's configuration and use that.
|
||||
*/
|
||||
link_id = __ffs(vif->active_links);
|
||||
link_conf = link_conf_dereference_check(vif, link_id);
|
||||
link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
|
||||
if (WARN_ON(!link_conf || !link))
|
||||
return;
|
||||
}
|
||||
dtimper = link_conf->dtim_period;
|
||||
bi = link_conf->beacon_int;
|
||||
|
||||
/* Regardless of power management state the driver must set
|
||||
* keep alive period. FW will use it for sending keep alive NDPs
|
||||
* immediately after association. Check that keep alive period
|
||||
* is at least 3 * DTIM
|
||||
*/
|
||||
keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
|
||||
USEC_PER_SEC);
|
||||
keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
|
||||
|
||||
cmd->id_and_color = fw_id;
|
||||
cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
|
||||
|
||||
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
|
||||
|
||||
if (vif->cfg.ps && iwl_mld_tdls_sta_count(mld) == 0) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
|
||||
|
||||
/* firmware supports LPRX for beacons at rate 1 Mbps or
|
||||
* 6 Mbps only
|
||||
*/
|
||||
if (link_conf->beacon_rate &&
|
||||
(link_conf->beacon_rate->bitrate == 10 ||
|
||||
link_conf->beacon_rate->bitrate == 60)) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
|
||||
cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
|
||||
}
|
||||
}
|
||||
|
||||
if (d3) {
|
||||
iwl_mld_power_config_skip_dtim(mld, link_conf,
|
||||
&cmd->skip_dtim_periods,
|
||||
&cmd->flags);
|
||||
cmd->rx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
|
||||
} else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
|
||||
cmd->tx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
|
||||
cmd->rx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
|
||||
} else {
|
||||
cmd->rx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
ps_poll = mld_vif->use_ps_poll;
|
||||
#endif
|
||||
|
|
@ -295,11 +403,23 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
bool d3)
|
||||
{
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0);
|
||||
|
||||
iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
|
||||
if (cmd_ver >= 3) {
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
|
||||
return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
|
||||
iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
|
||||
return iwl_mld_send_cmd_with_flags_pdu(mld,
|
||||
MAC_PM_POWER_TABLE, 0,
|
||||
&cmd, sizeof(cmd));
|
||||
} else {
|
||||
struct iwl_mac_power_cmd_v2 cmd = {};
|
||||
|
||||
iwl_mld_power_build_cmd_v2(mld, vif, &cmd, d3);
|
||||
return iwl_mld_send_cmd_with_flags_pdu(mld,
|
||||
MAC_PM_POWER_TABLE, 0,
|
||||
&cmd, sizeof(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -321,10 +321,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *mld)
|
|||
mld->ptp_data.ptp_clock_info.name,
|
||||
ptp_clock_index(mld->ptp_data.ptp_clock));
|
||||
|
||||
cancel_delayed_work_sync(&mld->ptp_data.dwork);
|
||||
ptp_clock_unregister(mld->ptp_data.ptp_clock);
|
||||
mld->ptp_data.ptp_clock = NULL;
|
||||
mld->ptp_data.last_gp2 = 0;
|
||||
mld->ptp_data.wrap_counter = 0;
|
||||
cancel_delayed_work_sync(&mld->ptp_data.dwork);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
phy_data->phy_info = le16_to_cpu(desc->phy_info);
|
||||
phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags,
|
||||
mld->fw_rates_ver_3);
|
||||
phy_data->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags);
|
||||
phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise);
|
||||
phy_data->energy_a = desc->v3.energy_a;
|
||||
phy_data->energy_b = desc->v3.energy_b;
|
||||
|
|
@ -158,7 +157,7 @@ static bool iwl_mld_used_average_energy(struct iwl_mld *mld, int link_id,
|
|||
guard(rcu)();
|
||||
|
||||
link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]);
|
||||
if (!link_conf)
|
||||
if (IS_ERR_OR_NULL(link_conf))
|
||||
return false;
|
||||
|
||||
mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
|
|
@ -1206,6 +1205,13 @@ static void iwl_mld_decode_eht_non_tb(struct iwl_mld_rx_phy_data *phy_data,
|
|||
iwl_mld_eht_set_ru_alloc(rx_status,
|
||||
le32_get_bits(phy_data->ntfy->sigs.eht.b2,
|
||||
OFDM_RX_FRAME_EHT_STA_RU));
|
||||
|
||||
if (phy_data->with_data)
|
||||
eht->user_info[0] |=
|
||||
cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
|
||||
LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id,
|
||||
OFDM_RX_FRAME_EHT_USER_FIELD_ID,
|
||||
IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
|
||||
}
|
||||
|
||||
static void iwl_mld_decode_eht_phy_data(struct iwl_mld_rx_phy_data *phy_data,
|
||||
|
|
@ -1314,14 +1320,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
|
|||
if (likely(!phy_data->ntfy))
|
||||
return;
|
||||
|
||||
if (phy_data->with_data) {
|
||||
eht->user_info[0] |=
|
||||
cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
|
||||
LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id,
|
||||
OFDM_RX_FRAME_EHT_USER_FIELD_ID,
|
||||
IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
|
||||
}
|
||||
|
||||
iwl_mld_decode_eht_usig(phy_data, skb);
|
||||
iwl_mld_decode_eht_phy_data(phy_data, rx_status, eht);
|
||||
}
|
||||
|
|
@ -2262,6 +2260,30 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
|
|||
wake_up(&mld->rxq_sync.waitq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt, int queue)
|
||||
{
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif = (void *)pkt->data;
|
||||
u32 len = iwl_rx_packet_payload_len(pkt);
|
||||
struct ieee80211_vif *bss_vif;
|
||||
|
||||
if (IWL_FW_CHECK(mld, len != sizeof(*notif),
|
||||
"invalid notification size %u (%zu)\n",
|
||||
len, sizeof(*notif)))
|
||||
return;
|
||||
|
||||
/* for the bss lookup and updating the keys' pn */
|
||||
guard(rcu)();
|
||||
|
||||
bss_vif = iwl_mld_get_bss_vif(mld);
|
||||
if (WARN_ON(!bss_vif))
|
||||
return;
|
||||
|
||||
iwl_mld_process_rsc_notification(mld, bss_vif, notif, queue);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static void iwl_mld_no_data_rx(struct iwl_mld *mld,
|
||||
struct napi_struct *napi,
|
||||
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_rx_h__
|
||||
#define __iwl_mld_rx_h__
|
||||
|
|
@ -61,6 +61,11 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
|
|||
struct napi_struct *napi,
|
||||
struct iwl_rx_packet *pkt, int queue);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt, int queue);
|
||||
#endif
|
||||
|
||||
void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
|
||||
struct napi_struct *napi,
|
||||
struct sk_buff *skb, int queue,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include "key.h"
|
||||
#include "agg.h"
|
||||
#include "tlc.h"
|
||||
#include "nan.h"
|
||||
#include "fw/api/sta.h"
|
||||
#include "fw/api/mac.h"
|
||||
#include "fw/api/rx.h"
|
||||
|
|
@ -43,13 +44,13 @@ int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
|
|||
|
||||
static void
|
||||
iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
|
||||
struct ieee80211_bss_conf *link,
|
||||
bool is_6ghz,
|
||||
__le32 *tx_ampdu_max_size,
|
||||
__le32 *tx_ampdu_spacing)
|
||||
{
|
||||
u32 agg_size = 0, mpdu_dens = 0;
|
||||
|
||||
if (WARN_ON(!link_sta || !link))
|
||||
if (WARN_ON(!link_sta))
|
||||
return;
|
||||
|
||||
/* Note that we always use only legacy & highest supported PPDUs, so
|
||||
|
|
@ -63,7 +64,7 @@ iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
|
|||
mpdu_dens = link_sta->ht_cap.ampdu_density;
|
||||
}
|
||||
|
||||
if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) {
|
||||
if (is_6ghz) {
|
||||
/* overwrite HT values on 6 GHz */
|
||||
mpdu_dens =
|
||||
le16_get_bits(link_sta->he_6ghz_capa.capa,
|
||||
|
|
@ -427,8 +428,14 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
|
|||
|
||||
len = sizeof(struct iwl_sta_cfg_cmd_v2);
|
||||
cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
|
||||
} else if (cmd->station_type ==
|
||||
cpu_to_le32(STATION_TYPE_NAN_MCAST_DATA)) {
|
||||
if (WARN_ON(!hweight32(le32_to_cpu(cmd->link_mask))))
|
||||
return -EINVAL;
|
||||
} else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_BCAST) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_MGMT) &&
|
||||
hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -439,29 +446,61 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_sta)
|
||||
static u32 iwl_mld_get_nan_link_mask(struct iwl_mld *mld)
|
||||
{
|
||||
struct iwl_mld_vif *nan_dev =
|
||||
iwl_mld_vif_from_mac80211(mld->nan_device_vif);
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
u32 link_mask = 0;
|
||||
|
||||
for_each_mld_nan_valid_link(nan_dev, nan_link)
|
||||
link_mask |= BIT(nan_link->fw_id);
|
||||
|
||||
return link_mask;
|
||||
}
|
||||
|
||||
int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_sta)
|
||||
{
|
||||
struct ieee80211_sta *sta = link_sta->sta;
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct ieee80211_bss_conf *link;
|
||||
struct iwl_mld_link *mld_link;
|
||||
struct iwl_sta_cfg_cmd cmd = {};
|
||||
int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
|
||||
bool is_6ghz, uora_exists;
|
||||
u32 link_mask;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
link = link_conf_dereference_protected(mld_sta->vif,
|
||||
link_sta->link_id);
|
||||
|
||||
mld_link = iwl_mld_link_from_mac80211(link);
|
||||
|
||||
if (WARN_ON(!link || !mld_link) || fw_id < 0)
|
||||
if (WARN_ON(fw_id < 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI ||
|
||||
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) {
|
||||
if (WARN_ON(!mld->nan_device_vif))
|
||||
return -EINVAL;
|
||||
|
||||
is_6ghz = false;
|
||||
uora_exists = false;
|
||||
|
||||
link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
} else {
|
||||
struct ieee80211_bss_conf *link;
|
||||
struct iwl_mld_link *mld_link;
|
||||
|
||||
link = link_conf_dereference_protected(mld_sta->vif,
|
||||
link_sta->link_id);
|
||||
mld_link = iwl_mld_link_from_mac80211(link);
|
||||
|
||||
if (WARN_ON(!link || !mld_link))
|
||||
return -EINVAL;
|
||||
|
||||
link_mask = BIT(mld_link->fw_id);
|
||||
is_6ghz = link->chanreq.oper.chan->band == NL80211_BAND_6GHZ;
|
||||
uora_exists = link->uora_exists;
|
||||
}
|
||||
|
||||
cmd.sta_id = cpu_to_le32(fw_id);
|
||||
cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id));
|
||||
cmd.link_mask = cpu_to_le32(link_mask);
|
||||
cmd.station_type = cpu_to_le32(mld_sta->sta_type);
|
||||
|
||||
memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
|
||||
|
|
@ -499,7 +538,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
break;
|
||||
}
|
||||
|
||||
iwl_mld_fill_ampdu_size_and_dens(link_sta, link,
|
||||
iwl_mld_fill_ampdu_size_and_dens(link_sta, is_6ghz,
|
||||
&cmd.tx_ampdu_max_size,
|
||||
&cmd.tx_ampdu_spacing);
|
||||
|
||||
|
|
@ -511,7 +550,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
|
||||
if (link_sta->he_cap.has_he) {
|
||||
cmd.trig_rnd_alloc =
|
||||
cpu_to_le32(link->uora_exists ? 1 : 0);
|
||||
cpu_to_le32(uora_exists ? 1 : 0);
|
||||
|
||||
/* PPE Thresholds */
|
||||
iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext);
|
||||
|
|
@ -525,10 +564,29 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
cmd.ack_enabled = cpu_to_le32(1);
|
||||
}
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) {
|
||||
struct ieee80211_sta *nmi_sta =
|
||||
wiphy_dereference(mld->wiphy, sta->nmi);
|
||||
int nmi_fw_id;
|
||||
|
||||
/* copy the local NDI address */
|
||||
ether_addr_copy(cmd.ndi_local_addr, mld_sta->vif->addr);
|
||||
|
||||
if (WARN_ON(!nmi_sta))
|
||||
return -EINVAL;
|
||||
|
||||
nmi_fw_id = iwl_mld_fw_sta_id_from_link_sta(mld,
|
||||
&nmi_sta->deflink);
|
||||
if (nmi_fw_id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
cmd.nmi_sta_id = (u8) nmi_fw_id;
|
||||
}
|
||||
|
||||
return iwl_mld_send_sta_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link_sta, link_sta)
|
||||
IWL_MLD_ALLOC_FN_STATIC(link_sta, link_sta)
|
||||
|
||||
static int
|
||||
iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
|
||||
|
|
@ -759,10 +817,23 @@ int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
|
|||
{
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct ieee80211_link_sta *link_sta;
|
||||
enum iwl_fw_sta_type type;
|
||||
int link_id;
|
||||
int ret;
|
||||
|
||||
ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_NAN:
|
||||
type = STATION_TYPE_NAN_PEER_NMI;
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
type = STATION_TYPE_NAN_PEER_NDI;
|
||||
break;
|
||||
default:
|
||||
type = STATION_TYPE_PEER;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = iwl_mld_init_sta(mld, sta, vif, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1001,10 +1072,9 @@ static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
static int
|
||||
iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
||||
iwl_mld_set_internal_sta_to_fw(struct iwl_mld *mld,
|
||||
const struct iwl_mld_int_sta *internal_sta,
|
||||
u8 fw_link_id,
|
||||
const u8 *addr)
|
||||
u32 link_mask, const u8 *addr)
|
||||
{
|
||||
struct iwl_sta_cfg_cmd cmd = {};
|
||||
|
||||
|
|
@ -1012,20 +1082,30 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
|||
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
|
||||
|
||||
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
|
||||
cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
|
||||
cmd.link_mask = cpu_to_le32(link_mask);
|
||||
cmd.station_type = cpu_to_le32(internal_sta->sta_type);
|
||||
|
||||
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
|
||||
* On the other hand, FW will never check this flag during RX since
|
||||
* an AP/GO doesn't receive protected broadcast management frames.
|
||||
* So, we can set it unconditionally.
|
||||
*
|
||||
* For NAN stations associated with a NAN Device, the MFP bit must be
|
||||
* set to 1, as otherwise the FW will assert when a key associated with
|
||||
* these stations would be added.
|
||||
*/
|
||||
if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT)
|
||||
if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT ||
|
||||
internal_sta->sta_type == STATION_TYPE_NAN_BCAST ||
|
||||
internal_sta->sta_type == STATION_TYPE_NAN_MGMT)
|
||||
cmd.mfp = cpu_to_le32(1);
|
||||
|
||||
if (addr) {
|
||||
memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
|
||||
memcpy(cmd.peer_link_address, addr, ETH_ALEN);
|
||||
if (internal_sta->sta_type == STATION_TYPE_NAN_MCAST_DATA) {
|
||||
ether_addr_copy(cmd.ndi_local_addr, addr);
|
||||
} else {
|
||||
memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
|
||||
memcpy(cmd.peer_link_address, addr, ETH_ALEN);
|
||||
}
|
||||
}
|
||||
|
||||
return iwl_mld_send_sta_cmd(mld, &cmd);
|
||||
|
|
@ -1034,7 +1114,8 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
|||
static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *internal_sta,
|
||||
enum iwl_fw_sta_type sta_type,
|
||||
u8 fw_link_id, const u8 *addr, u8 tid)
|
||||
u32 link_mask, const u8 *addr,
|
||||
u8 tid, bool add_txq)
|
||||
{
|
||||
int ret, queue_id;
|
||||
|
||||
|
|
@ -1046,11 +1127,14 @@ static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
|
|||
|
||||
internal_sta->sta_type = sta_type;
|
||||
|
||||
ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
|
||||
ret = iwl_mld_set_internal_sta_to_fw(mld, internal_sta, link_mask,
|
||||
addr);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!add_txq)
|
||||
return 0;
|
||||
|
||||
queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid);
|
||||
if (queue_id < 0) {
|
||||
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
|
||||
|
|
@ -1085,8 +1169,8 @@ int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
|
||||
STATION_TYPE_BCAST_MGMT,
|
||||
mld_link->fw_id, addr,
|
||||
IWL_MGMT_TID);
|
||||
BIT(mld_link->fw_id), addr,
|
||||
IWL_MGMT_TID, true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
|
||||
|
|
@ -1105,14 +1189,16 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
|
||||
STATION_TYPE_MCAST,
|
||||
mld_link->fw_id, mcast_addr, 0);
|
||||
BIT(mld_link->fw_id), mcast_addr,
|
||||
0, true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_aux_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *internal_sta)
|
||||
{
|
||||
return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX,
|
||||
0, NULL, IWL_MAX_TID_COUNT);
|
||||
0, NULL, IWL_MAX_TID_COUNT,
|
||||
true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_mon_sta(struct iwl_mld *mld,
|
||||
|
|
@ -1129,23 +1215,25 @@ int iwl_mld_add_mon_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta,
|
||||
STATION_TYPE_BCAST_MGMT,
|
||||
mld_link->fw_id, NULL,
|
||||
IWL_MAX_TID_COUNT);
|
||||
BIT(mld_link->fw_id), NULL,
|
||||
IWL_MAX_TID_COUNT,
|
||||
true);
|
||||
}
|
||||
|
||||
static void iwl_mld_remove_internal_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *internal_sta,
|
||||
bool flush, u8 tid)
|
||||
{
|
||||
if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA ||
|
||||
internal_sta->queue_id == IWL_MLD_INVALID_QUEUE))
|
||||
if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA))
|
||||
return;
|
||||
|
||||
if (flush)
|
||||
if (flush && !WARN_ON_ONCE(internal_sta->queue_id ==
|
||||
IWL_MLD_INVALID_QUEUE))
|
||||
iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id);
|
||||
|
||||
iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
|
||||
tid, internal_sta->queue_id);
|
||||
if (internal_sta->queue_id != IWL_MLD_INVALID_QUEUE)
|
||||
iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
|
||||
tid, internal_sta->queue_id);
|
||||
|
||||
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
|
||||
|
||||
|
|
@ -1346,3 +1434,82 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_BCAST,
|
||||
0, bcast_addr, 0, false);
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_MGMT,
|
||||
0, NULL, IWL_MAX_TID_COUNT, true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
u32 link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
|
||||
/* In case that there are no NAN links, nothing to do */
|
||||
if (!link_mask)
|
||||
return 0;
|
||||
|
||||
return iwl_mld_add_internal_sta(mld, sta,
|
||||
STATION_TYPE_NAN_MCAST_DATA,
|
||||
link_mask, ndi_addr,
|
||||
0, true);
|
||||
}
|
||||
|
||||
int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
u32 link_mask;
|
||||
|
||||
if (WARN_ON(!mld->nan_device_vif))
|
||||
return -EINVAL;
|
||||
|
||||
/* If the sta doesn't exist, add it */
|
||||
if (sta->sta_id == IWL_INVALID_STA)
|
||||
return iwl_mld_add_nan_mcast_data_sta(mld, ndi_addr, sta);
|
||||
|
||||
/* The station was already added */
|
||||
if (WARN_ON(sta->sta_type != STATION_TYPE_NAN_MCAST_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
if (link_mask)
|
||||
return iwl_mld_set_internal_sta_to_fw(mld,
|
||||
sta, link_mask,
|
||||
ndi_addr);
|
||||
|
||||
/* If no links are associated with NAN, remove the station */
|
||||
iwl_mld_remove_nan_mcast_data_sta(mld, sta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, false, 0);
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT);
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, true, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __iwl_mld_sta_h__
|
||||
|
|
@ -28,6 +28,9 @@ struct iwl_mld_rxq_dup_data {
|
|||
* This represents the link-level sta - the driver level equivalent to the
|
||||
* ieee80211_link_sta
|
||||
*
|
||||
* @rx_igtk: FW can only have one IGTK for RX at a time, whereas mac80211 will
|
||||
* have two. This tracks the one IGTK that currently exists in FW, to
|
||||
* remove it there when a new one is installed.
|
||||
* @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif
|
||||
* @signal_avg: the signal average coming from the firmware
|
||||
* @in_fw: whether the link STA is uploaded to the FW (false during restart)
|
||||
|
|
@ -37,6 +40,7 @@ struct iwl_mld_rxq_dup_data {
|
|||
struct iwl_mld_link_sta {
|
||||
/* Add here fields that need clean up on restart */
|
||||
struct_group(zeroed_on_hw_restart,
|
||||
struct ieee80211_key_conf *rx_igtk;
|
||||
u32 last_rate_n_flags;
|
||||
bool in_fw;
|
||||
s8 signal_avg;
|
||||
|
|
@ -195,6 +199,8 @@ void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
|
|||
int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_sta);
|
||||
u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta);
|
||||
int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_sta);
|
||||
int iwl_mld_update_all_link_stations(struct iwl_mld *mld,
|
||||
struct ieee80211_sta *sta);
|
||||
void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta);
|
||||
|
|
@ -270,4 +276,28 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
|
|||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
u16 old_links, u16 new_links);
|
||||
|
||||
int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
#endif /* __iwl_mld_sta_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -40,13 +40,21 @@ iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld,
|
|||
struct iwl_rx_packet *pkt,
|
||||
u8 fw_sta_id, struct station_info *sinfo)
|
||||
{
|
||||
const struct iwl_system_statistics_notif_oper *notif =
|
||||
(void *)&pkt->data;
|
||||
const struct iwl_stats_ntfy_per_sta *per_sta =
|
||||
¬if->per_sta[fw_sta_id];
|
||||
const struct iwl_stats_ntfy_per_sta *per_sta;
|
||||
struct ieee80211_link_sta *link_sta;
|
||||
struct iwl_mld_link_sta *mld_link_sta;
|
||||
|
||||
if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP,
|
||||
STATISTICS_OPER_NOTIF, 3) >= 4) {
|
||||
const struct iwl_system_statistics_notif_oper *notif =
|
||||
(void *)&pkt->data;
|
||||
per_sta = ¬if->per_sta[fw_sta_id];
|
||||
} else {
|
||||
const struct iwl_system_statistics_notif_oper_v3 *notif =
|
||||
(void *)&pkt->data;
|
||||
per_sta = ¬if->per_sta[fw_sta_id];
|
||||
}
|
||||
|
||||
/* 0 isn't a valid value, but FW might send 0.
|
||||
* In that case, set the latest non-zero value we stored
|
||||
*/
|
||||
|
|
@ -303,6 +311,40 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta,
|
|||
}
|
||||
}
|
||||
|
||||
static void iwl_mld_sta_stats_fill_beacon_signal_avg(struct ieee80211_vif *vif,
|
||||
struct station_info *sinfo)
|
||||
{
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct iwl_mld_link *link;
|
||||
u8 link_id;
|
||||
|
||||
if (iwl_mld_emlsr_active(vif))
|
||||
return;
|
||||
|
||||
/* TODO: support statistics for NAN */
|
||||
if (vif->type == NL80211_IFTYPE_NAN ||
|
||||
vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
link_id = iwl_mld_get_primary_link(vif);
|
||||
link_conf = link_conf_dereference_protected(vif, link_id);
|
||||
|
||||
if (WARN_ONCE(!link_conf,
|
||||
"link_conf is NULL for link_id=%u\n", link_id))
|
||||
return;
|
||||
|
||||
link = iwl_mld_link_from_mac80211(link_conf);
|
||||
if (WARN_ONCE(!link,
|
||||
"iwl_mld_link is NULL for link_id=%u\n", link_id))
|
||||
return;
|
||||
|
||||
if (!link->avg_signal)
|
||||
return;
|
||||
|
||||
sinfo->rx_beacon_signal_avg = link->avg_signal;
|
||||
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
|
||||
}
|
||||
|
||||
void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
|
|
@ -321,9 +363,9 @@ void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
|
|||
|
||||
iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo);
|
||||
|
||||
/* TODO: NL80211_STA_INFO_BEACON_RX */
|
||||
iwl_mld_sta_stats_fill_beacon_signal_avg(vif, sinfo);
|
||||
|
||||
/* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */
|
||||
/* TODO: NL80211_STA_INFO_BEACON_RX */
|
||||
}
|
||||
|
||||
#define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */
|
||||
|
|
@ -435,6 +477,8 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
|
|||
fw_id++) {
|
||||
const struct iwl_stats_ntfy_per_link *link_stats;
|
||||
struct ieee80211_bss_conf *bss_conf;
|
||||
struct iwl_mld_link *link;
|
||||
u32 avg_raw;
|
||||
int sig;
|
||||
|
||||
bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id);
|
||||
|
|
@ -448,6 +492,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
|
|||
sig = -le32_to_cpu(link_stats->beacon_filter_average_energy);
|
||||
iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf);
|
||||
|
||||
link = iwl_mld_link_from_mac80211(bss_conf);
|
||||
if (WARN_ON_ONCE(!link))
|
||||
continue;
|
||||
|
||||
avg_raw = le32_to_cpu(link_stats->beacon_average_energy);
|
||||
link->avg_signal = clamp_t(int, -(int)avg_raw, S8_MIN, 0);
|
||||
|
||||
/* TODO: parse more fields here (task=statistics)*/
|
||||
}
|
||||
|
||||
|
|
@ -507,6 +558,10 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw,
|
|||
(old_load >> 1);
|
||||
}
|
||||
|
||||
IWL_DEBUG_EHT(phy->mld,
|
||||
"PHY %d: load_by_us=%u%% load_not_by_us=%u%%\n",
|
||||
phy->fw_id, phy->channel_load_by_us, new_load);
|
||||
|
||||
iwl_mld_emlsr_check_chan_load(hw, phy, old_load);
|
||||
}
|
||||
|
||||
|
|
@ -523,17 +578,42 @@ iwl_mld_process_per_phy_stats(struct iwl_mld *mld,
|
|||
void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
const struct iwl_system_statistics_notif_oper *stats =
|
||||
struct iwl_system_statistics_notif_oper *_notif __free(kfree) = NULL;
|
||||
const struct iwl_system_statistics_notif_oper *notif =
|
||||
(void *)&pkt->data;
|
||||
u32 curr_ts_usec = le32_to_cpu(stats->time_stamp);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) <
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->per_sta) != IWL_STATION_COUNT_MAX);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->per_link) <
|
||||
ARRAY_SIZE(mld->fw_id_to_bss_conf));
|
||||
|
||||
iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec);
|
||||
iwl_mld_process_per_sta_stats(mld, stats->per_sta);
|
||||
iwl_mld_process_per_phy_stats(mld, stats->per_phy);
|
||||
if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP,
|
||||
STATISTICS_OPER_NOTIF, 3) == 3) {
|
||||
const struct iwl_system_statistics_notif_oper_v3 *stats =
|
||||
(void *)&pkt->data;
|
||||
_notif = kzalloc_obj(*_notif);
|
||||
|
||||
if (!_notif)
|
||||
return;
|
||||
|
||||
_notif->time_stamp = stats->time_stamp;
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_link); i++)
|
||||
_notif->per_link[i] = stats->per_link[i];
|
||||
|
||||
BUILD_BUG_ON(sizeof(_notif->per_phy[0]) <
|
||||
sizeof(stats->per_phy[0]));
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_phy); i++)
|
||||
memcpy(&_notif->per_phy[i], &stats->per_phy[i],
|
||||
sizeof(stats->per_phy[i]));
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_sta); i++)
|
||||
_notif->per_sta[i] = stats->per_sta[i];
|
||||
|
||||
notif = _notif;
|
||||
}
|
||||
|
||||
iwl_mld_process_per_link_stats(mld, notif->per_link,
|
||||
le32_to_cpu(notif->time_stamp));
|
||||
iwl_mld_process_per_sta_stats(mld, notif->per_sta);
|
||||
iwl_mld_process_per_phy_stats(mld, notif->per_phy);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o
|
||||
iwlmld-tests-y += chan_load_thresh.o
|
||||
|
||||
ccflags-y += -I$(src)/../
|
||||
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o
|
||||
|
|
|
|||
139
drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c
Normal file
139
drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* KUnit tests for channel helper functions
|
||||
*
|
||||
* Copyright (C) 2026 Intel Corporation
|
||||
*/
|
||||
#include <kunit/static_stub.h>
|
||||
#include "mld.h"
|
||||
#include "link.h"
|
||||
#include "iface.h"
|
||||
#include "utils.h"
|
||||
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
||||
struct test_chan_load_case {
|
||||
const char *desc;
|
||||
u32 load;
|
||||
enum iwl_mld_link_chan_load_level old_lvl;
|
||||
enum iwl_mld_link_chan_load_level expected_lvl;
|
||||
bool expected_scan_trig;
|
||||
};
|
||||
|
||||
static const struct test_chan_load_case test_chan_load_thresh_cases[] = {
|
||||
/* Level-up transitions */
|
||||
{
|
||||
.desc = "Transition NONE->NONE",
|
||||
.load = 20,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL_NONE,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL_NONE,
|
||||
.expected_scan_trig = false,
|
||||
},
|
||||
{
|
||||
.desc = "Transition NONE->LVL1",
|
||||
.load = 50,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL_NONE,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL1,
|
||||
.expected_scan_trig = true,
|
||||
},
|
||||
{
|
||||
.desc = "Transition LVL1->LVL2",
|
||||
.load = 75,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL1,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_scan_trig = true,
|
||||
},
|
||||
{
|
||||
.desc = "Transition LVL2->LVL3",
|
||||
.load = 90,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL3,
|
||||
.expected_scan_trig = true,
|
||||
},
|
||||
|
||||
/* Level-down transitions */
|
||||
{
|
||||
.desc = "Transition LVL1->NONE",
|
||||
.load = 30,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL1,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL_NONE,
|
||||
.expected_scan_trig = false,
|
||||
},
|
||||
{
|
||||
.desc = "Transition LVL2->LVL1",
|
||||
.load = 60,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL1,
|
||||
.expected_scan_trig = false,
|
||||
},
|
||||
{
|
||||
.desc = "Transition LVL3->LVL2",
|
||||
.load = 70,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL3,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_scan_trig = false,
|
||||
},
|
||||
|
||||
/* No change */
|
||||
{
|
||||
.desc = "Transition LVL2->LVL2",
|
||||
.load = 72,
|
||||
.old_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_lvl = LINK_CHAN_LOAD_LVL2,
|
||||
.expected_scan_trig = false,
|
||||
},
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM_DESC(test_chan_load_thresh_cases,
|
||||
test_chan_load_thresh_cases, desc);
|
||||
|
||||
static void test_chan_load_thresholds(struct kunit *test)
|
||||
{
|
||||
const struct test_chan_load_case *tc = test->param_value;
|
||||
struct iwl_mld *mld = test->priv;
|
||||
struct ieee80211_vif *vif;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct iwl_mld_link *mld_link;
|
||||
struct iwl_mld_kunit_link assoc_link = {
|
||||
.id = 0,
|
||||
.chandef = &chandef_6ghz_160mhz,
|
||||
};
|
||||
bool scan_trig;
|
||||
u32 chan_load;
|
||||
|
||||
/* Setup associated non-MLO station */
|
||||
vif = iwlmld_kunit_setup_non_mlo_assoc(&assoc_link);
|
||||
mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
link_conf = &vif->bss_conf;
|
||||
mld_link = &mld_vif->deflink;
|
||||
|
||||
chan_load = tc->load;
|
||||
mld_link->chan_load_lvl = tc->old_lvl;
|
||||
|
||||
/* Execute function under test */
|
||||
wiphy_lock(mld->wiphy);
|
||||
scan_trig = iwl_mld_chan_load_requires_scan(mld, link_conf, chan_load);
|
||||
wiphy_unlock(mld->wiphy);
|
||||
|
||||
/* Check return value */
|
||||
KUNIT_EXPECT_EQ(test, tc->expected_scan_trig, scan_trig);
|
||||
|
||||
/* Check updated channel-load level */
|
||||
KUNIT_EXPECT_EQ(test, tc->expected_lvl, mld_link->chan_load_lvl);
|
||||
}
|
||||
|
||||
static struct kunit_case chan_load_thresh_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(test_chan_load_thresholds,
|
||||
test_chan_load_thresh_cases_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite chan_load_thresh_test_suite = {
|
||||
.name = "iwl_mld_chan_load_threshold_tests",
|
||||
.init = iwlmld_kunit_test_init,
|
||||
.test_cases = chan_load_thresh_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(chan_load_thresh_test_suite);
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* KUnit tests for link selection functions
|
||||
*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include <kunit/static_stub.h>
|
||||
|
||||
|
|
@ -34,6 +34,7 @@ static const struct link_grading_test_case {
|
|||
.link_id = 0,
|
||||
.chandef = &chandef_2ghz_20mhz,
|
||||
.active = false,
|
||||
.signal = -70,
|
||||
.has_chan_util_elem = true,
|
||||
.chan_util = 128,
|
||||
},
|
||||
|
|
@ -45,6 +46,7 @@ static const struct link_grading_test_case {
|
|||
.link_id = 0,
|
||||
.chandef = &chandef_2ghz_20mhz,
|
||||
.active = false,
|
||||
.signal = -70,
|
||||
.has_chan_util_elem = true,
|
||||
.chan_util = 180,
|
||||
},
|
||||
|
|
@ -55,6 +57,7 @@ static const struct link_grading_test_case {
|
|||
.input.link = {
|
||||
.link_id = 0,
|
||||
.chandef = &chandef_2ghz_20mhz,
|
||||
.signal = -70,
|
||||
.has_chan_util_elem = true,
|
||||
.chan_util = 180,
|
||||
.active = true,
|
||||
|
|
@ -67,6 +70,7 @@ static const struct link_grading_test_case {
|
|||
.input.link = {
|
||||
.link_id = 0,
|
||||
.chandef = &chandef_2ghz_20mhz,
|
||||
.signal = -70,
|
||||
.active = true,
|
||||
},
|
||||
.expected_grade = 120,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* KUnit tests for channel helper functions
|
||||
*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
|
|
@ -68,8 +68,6 @@ int iwlmld_kunit_test_init(struct kunit *test)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link, bss_conf)
|
||||
|
||||
static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link,
|
||||
struct iwl_mld_link *mld_link, int link_id)
|
||||
|
|
@ -94,7 +92,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
|
|||
rcu_assign_pointer(vif->link_conf[link_id], link);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(vif, vif)
|
||||
IWL_MLD_ALLOC_FN_STATIC(vif, vif)
|
||||
|
||||
/* Helper function to add and initialize a VIF for KUnit tests */
|
||||
struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
|
||||
|
|
@ -199,7 +197,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
|
|||
vif->active_links |= BIT(link->link_id);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link_sta, link_sta)
|
||||
IWL_MLD_ALLOC_FN_STATIC(link_sta, link_sta)
|
||||
|
||||
static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -15,9 +15,9 @@
|
|||
#include "fw/api/context.h"
|
||||
#include "fw/api/dhc.h"
|
||||
|
||||
static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)
|
||||
static u8 iwl_mld_fw_bw_from_sta_bw(enum ieee80211_sta_rx_bandwidth bandwidth)
|
||||
{
|
||||
switch (link_sta->bandwidth) {
|
||||
switch (bandwidth) {
|
||||
case IEEE80211_STA_RX_BW_320:
|
||||
return IWL_TLC_MNG_CH_WIDTH_320MHZ;
|
||||
case IEEE80211_STA_RX_BW_160:
|
||||
|
|
@ -32,40 +32,61 @@ static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)
|
|||
}
|
||||
}
|
||||
|
||||
struct iwl_mld_tlc_sta_capa {
|
||||
u16 non_ht_rates;
|
||||
u16 max_amsdu_len;
|
||||
u8 rx_nss;
|
||||
enum ieee80211_sta_rx_bandwidth bandwidth;
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
const struct ieee80211_sta_ht_cap *ht_cap;
|
||||
const struct ieee80211_sta_vht_cap *vht_cap;
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
const struct ieee80211_sta_eht_cap *eht_cap;
|
||||
const struct ieee80211_sta_uhr_cap *uhr_cap;
|
||||
|
||||
const struct ieee80211_sta_he_cap *own_he_cap;
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap;
|
||||
const struct ieee80211_sta_uhr_cap *own_uhr_cap;
|
||||
};
|
||||
|
||||
static __le16
|
||||
iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap,
|
||||
const struct ieee80211_sta_uhr_cap *own_uhr_cap)
|
||||
struct iwl_mld_tlc_sta_capa *capa)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
|
||||
struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
|
||||
bool has_vht = vht_cap->vht_supported;
|
||||
const struct ieee80211_sta_ht_cap *ht_cap = capa->ht_cap;
|
||||
const struct ieee80211_sta_vht_cap *vht_cap = capa->vht_cap;
|
||||
const struct ieee80211_sta_he_cap *he_cap = capa->he_cap;
|
||||
const struct ieee80211_sta_eht_cap *eht_cap = capa->eht_cap;
|
||||
const struct ieee80211_sta_uhr_cap *uhr_cap = capa->uhr_cap;
|
||||
const struct ieee80211_sta_he_cap *own_he_cap = capa->own_he_cap;
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap = capa->own_eht_cap;
|
||||
const struct ieee80211_sta_uhr_cap *own_uhr_cap = capa->own_uhr_cap;
|
||||
bool has_vht = vht_cap && vht_cap->vht_supported;
|
||||
u16 flags = 0;
|
||||
|
||||
/* STBC flags */
|
||||
if (mld->cfg->ht_params.stbc &&
|
||||
(hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) {
|
||||
if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
|
||||
IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
|
||||
if (he_cap && he_cap->has_he &&
|
||||
he_cap->he_cap_elem.phy_cap_info[2] &
|
||||
IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
|
||||
else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
|
||||
else if (vht_cap &&
|
||||
vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
|
||||
else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
|
||||
else if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
|
||||
}
|
||||
|
||||
/* LDPC */
|
||||
if (mld->cfg->ht_params.ldpc &&
|
||||
((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
|
||||
((ht_cap && ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
|
||||
(has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
|
||||
|
||||
if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] &
|
||||
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
|
||||
if (he_cap && he_cap->has_he &&
|
||||
(he_cap->he_cap_elem.phy_cap_info[1] &
|
||||
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
|
||||
|
||||
if (own_he_cap &&
|
||||
|
|
@ -74,7 +95,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
|
|||
flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
|
||||
|
||||
/* DCM */
|
||||
if (he_cap->has_he &&
|
||||
if (he_cap && he_cap->has_he &&
|
||||
(he_cap->he_cap_elem.phy_cap_info[3] &
|
||||
IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK &&
|
||||
own_he_cap &&
|
||||
|
|
@ -85,15 +106,15 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
|
|||
/* Extra EHT LTF */
|
||||
if (own_eht_cap &&
|
||||
own_eht_cap->eht_cap_elem.phy_cap_info[5] &
|
||||
IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF &&
|
||||
link_sta->eht_cap.has_eht &&
|
||||
link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
|
||||
IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) {
|
||||
IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF &&
|
||||
eht_cap && eht_cap->has_eht &&
|
||||
eht_cap->eht_cap_elem.phy_cap_info[5] &
|
||||
IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) {
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
|
||||
}
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr && own_uhr_cap &&
|
||||
link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX &&
|
||||
if (uhr_cap && uhr_cap->has_uhr && own_uhr_cap &&
|
||||
uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX &&
|
||||
own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX)
|
||||
flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK |
|
||||
IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK;
|
||||
|
|
@ -114,27 +135,27 @@ static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld)
|
|||
return fw_chains;
|
||||
}
|
||||
|
||||
static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta)
|
||||
static u8 iwl_mld_get_fw_sgi(struct iwl_mld_tlc_sta_capa *capa)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
|
||||
struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
|
||||
const struct ieee80211_sta_ht_cap *ht_cap = capa->ht_cap;
|
||||
const struct ieee80211_sta_vht_cap *vht_cap = capa->vht_cap;
|
||||
const struct ieee80211_sta_he_cap *he_cap = capa->he_cap;
|
||||
u8 sgi_chwidths = 0;
|
||||
|
||||
/* If the association supports HE, HT/VHT rates will never be used for
|
||||
* Tx and therefor there's no need to set the
|
||||
* sgi-per-channel-width-support bits
|
||||
*/
|
||||
if (he_cap->has_he)
|
||||
if (he_cap && he_cap->has_he)
|
||||
return 0;
|
||||
|
||||
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
|
||||
if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
|
||||
sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
|
||||
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
|
||||
if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
|
||||
sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
|
||||
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
|
||||
if (vht_cap && vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
|
||||
sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
|
||||
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
|
||||
if (vht_cap && vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
|
||||
sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
|
||||
|
||||
return sgi_chwidths;
|
||||
|
|
@ -164,31 +185,30 @@ iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap,
|
|||
}
|
||||
|
||||
static void
|
||||
iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_vht_cap *vht_cap,
|
||||
iwl_mld_fill_vht_rates(struct iwl_mld_tlc_sta_capa *capa,
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
u32 supp;
|
||||
int i, highest_mcs;
|
||||
u8 max_nss = link_sta->rx_nss;
|
||||
u8 max_nss = capa->rx_nss;
|
||||
struct ieee80211_vht_cap ieee_vht_cap = {
|
||||
.vht_cap_info = cpu_to_le32(vht_cap->cap),
|
||||
.supp_mcs = vht_cap->vht_mcs,
|
||||
.vht_cap_info = cpu_to_le32(capa->vht_cap->cap),
|
||||
.supp_mcs = capa->vht_cap->vht_mcs,
|
||||
};
|
||||
|
||||
/* the station support only a single receive chain */
|
||||
if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
if (capa->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
max_nss = 1;
|
||||
|
||||
for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) {
|
||||
int nss = i + 1;
|
||||
|
||||
highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss);
|
||||
highest_mcs = iwl_mld_get_highest_fw_mcs(capa->vht_cap, nss);
|
||||
if (!highest_mcs)
|
||||
continue;
|
||||
|
||||
supp = BIT(highest_mcs + 1) - 1;
|
||||
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20)
|
||||
if (capa->bandwidth == IEEE80211_STA_RX_BW_20)
|
||||
supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
|
||||
|
||||
cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le32(supp);
|
||||
|
|
@ -196,7 +216,7 @@ iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
|
|||
* configuration is supported - only for MCS 0 since we already
|
||||
* decoded the MCS bits anyway ourselves.
|
||||
*/
|
||||
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 &&
|
||||
if (capa->bandwidth == IEEE80211_STA_RX_BW_160 &&
|
||||
ieee80211_get_vht_max_nss(&ieee_vht_cap,
|
||||
IEEE80211_VHT_CHANWIDTH_160MHZ,
|
||||
0, true, nss) >= nss)
|
||||
|
|
@ -223,20 +243,19 @@ static u32 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
|
|||
}
|
||||
|
||||
static void
|
||||
iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
iwl_mld_fill_he_rates(struct iwl_mld_tlc_sta_capa *capa,
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
|
||||
const struct ieee80211_sta_he_cap *he_cap = capa->he_cap;
|
||||
u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
|
||||
u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
|
||||
u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80);
|
||||
u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160);
|
||||
u16 tx_mcs_80 = le16_to_cpu(capa->own_he_cap->he_mcs_nss_supp.tx_mcs_80);
|
||||
u16 tx_mcs_160 = le16_to_cpu(capa->own_he_cap->he_mcs_nss_supp.tx_mcs_160);
|
||||
int i;
|
||||
u8 nss = link_sta->rx_nss;
|
||||
u8 nss = capa->rx_nss;
|
||||
|
||||
/* the station support only a single receive chain */
|
||||
if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
if (capa->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
nss = 1;
|
||||
|
||||
for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) {
|
||||
|
|
@ -313,17 +332,15 @@ static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss)
|
|||
|
||||
static void
|
||||
iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap,
|
||||
struct iwl_mld_tlc_sta_capa *capa,
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
/* peer RX mcs capa */
|
||||
const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
|
||||
&link_sta->eht_cap.eht_mcs_nss_supp;
|
||||
&capa->eht_cap->eht_mcs_nss_supp;
|
||||
/* our TX mcs capa */
|
||||
const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =
|
||||
&own_eht_cap->eht_mcs_nss_supp;
|
||||
&capa->own_eht_cap->eht_mcs_nss_supp;
|
||||
|
||||
enum IWL_TLC_MCS_PER_BW bw;
|
||||
struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20;
|
||||
|
|
@ -331,7 +348,7 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
|
|||
|
||||
/* peer is 20 MHz only */
|
||||
if (vif->type == NL80211_IFTYPE_AP &&
|
||||
!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
|
||||
!(capa->he_cap->he_cap_elem.phy_cap_info[0] &
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
|
||||
mcs_rx_20 = eht_rx_mcs->only_20mhz;
|
||||
} else {
|
||||
|
|
@ -346,7 +363,7 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
|
|||
}
|
||||
|
||||
/* NIC is capable of 20 MHz only */
|
||||
if (!(own_he_cap->he_cap_elem.phy_cap_info[0] &
|
||||
if (!(capa->own_he_cap->he_cap_elem.phy_cap_info[0] &
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
|
||||
mcs_tx_20 = eht_tx_mcs->only_20mhz;
|
||||
} else {
|
||||
|
|
@ -402,67 +419,51 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
|
|||
}
|
||||
|
||||
/* the station support only a single receive chain */
|
||||
if (link_sta->smps_mode == IEEE80211_SMPS_STATIC ||
|
||||
link_sta->rx_nss < 2)
|
||||
if (capa->smps_mode == IEEE80211_SMPS_STATIC ||
|
||||
capa->rx_nss < 2)
|
||||
memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,
|
||||
sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
struct ieee80211_supported_band *sband,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap,
|
||||
const struct ieee80211_sta_uhr_cap *own_uhr_cap,
|
||||
iwl_mld_fill_supp_rates(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mld_tlc_sta_capa *capa,
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
int i;
|
||||
u16 non_ht_rates = 0;
|
||||
unsigned long rates_bitmap;
|
||||
const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
|
||||
const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
|
||||
const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
|
||||
|
||||
/* non HT rates */
|
||||
rates_bitmap = link_sta->supp_rates[sband->band];
|
||||
for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG)
|
||||
non_ht_rates |= BIT(sband->bitrates[i].hw_value);
|
||||
|
||||
cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
|
||||
cmd->non_ht_rates = cpu_to_le16(capa->non_ht_rates);
|
||||
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr && own_uhr_cap) {
|
||||
if (capa->uhr_cap && capa->uhr_cap->has_uhr && capa->own_uhr_cap) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_UHR;
|
||||
/*
|
||||
* FIXME: spec currently inherits from EHT but has no
|
||||
* finer MCS bits. Once that's there, need to add them
|
||||
* to the bitmaps (and maybe copy this to UHR, or so.)
|
||||
*/
|
||||
iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
|
||||
own_eht_cap, cmd);
|
||||
} else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
|
||||
iwl_mld_fill_eht_rates(vif, capa, cmd);
|
||||
} else if (capa->eht_cap && capa->eht_cap->has_eht &&
|
||||
capa->own_he_cap && capa->own_eht_cap) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_EHT;
|
||||
iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
|
||||
own_eht_cap, cmd);
|
||||
} else if (he_cap->has_he && own_he_cap) {
|
||||
iwl_mld_fill_eht_rates(vif, capa, cmd);
|
||||
} else if (capa->he_cap && capa->he_cap->has_he && capa->own_he_cap) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_HE;
|
||||
iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd);
|
||||
} else if (vht_cap->vht_supported) {
|
||||
iwl_mld_fill_he_rates(capa, cmd);
|
||||
} else if (capa->vht_cap && capa->vht_cap->vht_supported) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_VHT;
|
||||
iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd);
|
||||
} else if (ht_cap->ht_supported) {
|
||||
iwl_mld_fill_vht_rates(capa, cmd);
|
||||
} else if (capa->ht_cap && capa->ht_cap->ht_supported) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_HT;
|
||||
cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
|
||||
cpu_to_le32(ht_cap->mcs.rx_mask[0]);
|
||||
cpu_to_le32(capa->ht_cap->mcs.rx_mask[0]);
|
||||
|
||||
/* the station support only a single receive chain */
|
||||
if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
if (capa->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
|
||||
0;
|
||||
else
|
||||
cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
|
||||
cpu_to_le32(ht_cap->mcs.rx_mask[1]);
|
||||
cpu_to_le32(capa->ht_cap->mcs.rx_mask[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -527,60 +528,32 @@ static int iwl_mld_convert_tlc_cmd_to_v4(struct iwl_tlc_config_cmd *cmd,
|
|||
|
||||
static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
struct ieee80211_bss_conf *link)
|
||||
struct iwl_mld_sta *mld_sta,
|
||||
int fw_sta_mask, int phy_id,
|
||||
struct iwl_mld_tlc_sta_capa *capa)
|
||||
{
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
|
||||
enum nl80211_band band = link->chanreq.oper.chan->band;
|
||||
struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
|
||||
const struct ieee80211_sta_he_cap *own_he_cap =
|
||||
ieee80211_get_he_iftype_cap_vif(sband, vif);
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap =
|
||||
ieee80211_get_eht_iftype_cap_vif(sband, vif);
|
||||
const struct ieee80211_sta_uhr_cap *own_uhr_cap =
|
||||
ieee80211_get_uhr_iftype_cap_vif(sband, vif);
|
||||
struct iwl_tlc_config_cmd cmd = {
|
||||
/* For AP mode, use 20 MHz until the STA is authorized */
|
||||
.max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
|
||||
iwl_mld_fw_bw_from_sta_bw(link_sta) :
|
||||
iwl_mld_fw_bw_from_sta_bw(capa->bandwidth) :
|
||||
IWL_TLC_MNG_CH_WIDTH_20MHZ,
|
||||
.flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
|
||||
own_he_cap, own_eht_cap,
|
||||
own_uhr_cap),
|
||||
.flags = iwl_mld_get_tlc_cmd_flags(mld, capa),
|
||||
.chains = iwl_mld_get_fw_chains(mld),
|
||||
.sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
|
||||
.max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
|
||||
.sgi_ch_width_supp = iwl_mld_get_fw_sgi(capa),
|
||||
.max_mpdu_len = cpu_to_le16(capa->max_amsdu_len),
|
||||
};
|
||||
int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
|
||||
u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD);
|
||||
u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
|
||||
struct ieee80211_chanctx_conf *chan_ctx;
|
||||
struct iwl_tlc_config_cmd_v5 cmd_v5 = {};
|
||||
struct iwl_tlc_config_cmd_v4 cmd_v4 = {};
|
||||
void *cmd_ptr;
|
||||
u8 cmd_size;
|
||||
u32 phy_id;
|
||||
int ret;
|
||||
|
||||
if (fw_sta_id < 0)
|
||||
return;
|
||||
|
||||
cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
|
||||
|
||||
if (WARN_ON_ONCE(!mld_link))
|
||||
return;
|
||||
|
||||
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
|
||||
if (WARN_ON(!chan_ctx))
|
||||
return;
|
||||
|
||||
phy_id = iwl_mld_phy_from_mac80211(chan_ctx)->fw_id;
|
||||
cmd.sta_mask = cpu_to_le32(fw_sta_mask);
|
||||
cmd.phy_id = cpu_to_le32(phy_id);
|
||||
|
||||
iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
|
||||
own_he_cap, own_eht_cap,
|
||||
own_uhr_cap, &cmd);
|
||||
iwl_mld_fill_supp_rates(mld, vif, capa, &cmd);
|
||||
|
||||
if (cmd_ver == 6) {
|
||||
cmd_ptr = &cmd;
|
||||
|
|
@ -641,16 +614,32 @@ int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void iwl_mld_config_tlc_link(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
struct ieee80211_link_sta *link_sta)
|
||||
static void _iwl_mld_config_tlc_link(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *chan_ctx,
|
||||
u32 fw_sta_mask,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
struct iwl_mld_tlc_sta_capa *capa)
|
||||
{
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
|
||||
struct ieee80211_supported_band *sband;
|
||||
unsigned long rates_bitmap;
|
||||
enum nl80211_band band;
|
||||
int i;
|
||||
|
||||
if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan))
|
||||
/*
|
||||
* Note: Due to NAN, the chan_ctx here might not be the same as the
|
||||
* vif->links[link_sta->link_id] one (which is NULL in NAN), so some
|
||||
* care is needed in this function (and the 'capa' exists to make it
|
||||
* less error-prone, the other functions need not worry about it.)
|
||||
*/
|
||||
|
||||
if (WARN_ON(!chan_ctx))
|
||||
return;
|
||||
|
||||
band = chan_ctx->def.chan->band;
|
||||
sband = mld->hw->wiphy->bands[band];
|
||||
|
||||
/* Before we have information about a station, configure the A-MSDU RC
|
||||
* limit such that iwlmd and mac80211 would not be allowed to build
|
||||
* A-MSDUs.
|
||||
|
|
@ -660,7 +649,21 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
|
|||
ieee80211_sta_recalc_aggregates(link_sta->sta);
|
||||
}
|
||||
|
||||
iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf);
|
||||
/* non HT rates */
|
||||
rates_bitmap = link_sta->supp_rates[sband->band];
|
||||
for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG)
|
||||
capa->non_ht_rates |= BIT(sband->bitrates[i].hw_value);
|
||||
|
||||
capa->max_amsdu_len = link_sta->agg.max_amsdu_len;
|
||||
capa->ht_cap = &link_sta->ht_cap;
|
||||
capa->vht_cap = &link_sta->vht_cap;
|
||||
capa->he_cap = &link_sta->he_cap;
|
||||
capa->eht_cap = &link_sta->eht_cap;
|
||||
capa->uhr_cap = &link_sta->uhr_cap;
|
||||
|
||||
iwl_mld_send_tlc_cmd(mld, vif, mld_sta, fw_sta_mask,
|
||||
iwl_mld_phy_from_mac80211(chan_ctx)->fw_id,
|
||||
capa);
|
||||
}
|
||||
|
||||
void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
|
|
@ -706,6 +709,136 @@ void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
}
|
||||
}
|
||||
|
||||
static void iwl_mld_config_tlc_nan(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
int fw_sta_id)
|
||||
{
|
||||
struct ieee80211_nan_peer_sched *sched;
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
struct ieee80211_sta *nmi, *iter;
|
||||
struct iwl_mld_vif *nan_mld_vif;
|
||||
u32 fw_sta_mask;
|
||||
|
||||
nmi = wiphy_dereference(mld->wiphy, link_sta->sta->nmi);
|
||||
if (WARN_ON(!nmi))
|
||||
return;
|
||||
|
||||
sched = nmi->nan_sched;
|
||||
|
||||
/* This sta might not be in the list yet as it is just getting added */
|
||||
fw_sta_mask = BIT(fw_sta_id);
|
||||
for_each_station(iter, mld->hw) {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(iter);
|
||||
struct ieee80211_sta *iter_nmi;
|
||||
|
||||
iter_nmi = wiphy_dereference(mld->wiphy, iter->nmi);
|
||||
|
||||
if (iter_nmi == nmi)
|
||||
fw_sta_mask |= BIT(mld_sta->deflink.fw_id);
|
||||
}
|
||||
|
||||
if (WARN_ON(!mld->nan_device_vif))
|
||||
return;
|
||||
|
||||
nan_mld_vif = iwl_mld_vif_from_mac80211(mld->nan_device_vif);
|
||||
|
||||
for_each_mld_nan_valid_link(nan_mld_vif, nan_link) {
|
||||
struct ieee80211_chanctx_conf *chan_ctx;
|
||||
struct iwl_mld_tlc_sta_capa capa = {};
|
||||
struct cfg80211_chan_def *chandef;
|
||||
bool common_channel = false;
|
||||
|
||||
chan_ctx = nan_link->chanctx;
|
||||
if (!chan_ctx)
|
||||
continue;
|
||||
|
||||
chandef = iwl_mld_get_chandef_from_chanctx(mld, chan_ctx);
|
||||
|
||||
capa.smps_mode = IEEE80211_SMPS_OFF; /* always off */
|
||||
|
||||
capa.rx_nss = 2; /* maximum we support */
|
||||
capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width);
|
||||
|
||||
for (int j = 0; j < (sched ? sched->n_channels : 0); j++) {
|
||||
enum ieee80211_sta_rx_bandwidth rx_bw;
|
||||
enum nl80211_chan_width width;
|
||||
int chains;
|
||||
|
||||
if (sched->channels[j].chanctx_conf != chan_ctx)
|
||||
continue;
|
||||
|
||||
common_channel = true;
|
||||
|
||||
width = sched->channels[j].chanreq.oper.width;
|
||||
rx_bw = ieee80211_chan_width_to_rx_bw(width);
|
||||
capa.bandwidth = min(capa.bandwidth, rx_bw);
|
||||
|
||||
chains = sched->channels[j].needed_rx_chains;
|
||||
capa.rx_nss = min(capa.rx_nss, chains);
|
||||
}
|
||||
|
||||
/* Apply minimum parameters if there is no common channel */
|
||||
if (!common_channel) {
|
||||
capa.bandwidth = IEEE80211_STA_RX_BW_20;
|
||||
capa.rx_nss = 1;
|
||||
}
|
||||
|
||||
capa.own_he_cap = &mld->wiphy->nan_capa.phy.he;
|
||||
/* no EHT/UHR for NAN */
|
||||
|
||||
_iwl_mld_config_tlc_link(mld, vif, chan_ctx, fw_sta_mask,
|
||||
link_sta, &capa);
|
||||
}
|
||||
}
|
||||
|
||||
void iwl_mld_config_tlc_link(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
struct ieee80211_link_sta *link_sta)
|
||||
{
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
|
||||
struct ieee80211_chanctx_conf *chan_ctx;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct iwl_mld_tlc_sta_capa capa = {};
|
||||
struct cfg80211_chan_def *chandef;
|
||||
enum nl80211_band band;
|
||||
|
||||
if (fw_sta_id < 0)
|
||||
return;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
return;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_config_tlc_nan(mld, vif, link_sta, fw_sta_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!mld_link))
|
||||
return;
|
||||
|
||||
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
|
||||
if (WARN_ON_ONCE(!chan_ctx))
|
||||
return;
|
||||
|
||||
chandef = &iwl_mld_phy_from_mac80211(chan_ctx)->chandef;
|
||||
band = chandef->chan->band;
|
||||
sband = mld->hw->wiphy->bands[band];
|
||||
|
||||
capa.smps_mode = link_sta->smps_mode;
|
||||
capa.rx_nss = link_sta->rx_nss;
|
||||
capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width);
|
||||
capa.bandwidth = min(capa.bandwidth, link_sta->bandwidth);
|
||||
capa.own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
|
||||
capa.own_eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif);
|
||||
capa.own_uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, vif);
|
||||
|
||||
_iwl_mld_config_tlc_link(mld, vif, chan_ctx, BIT(fw_sta_id),
|
||||
link_sta, &capa);
|
||||
}
|
||||
|
||||
void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
|
|
@ -714,6 +847,7 @@ void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
/* Note: for NAN this is only the mac80211-level deflink */
|
||||
for_each_vif_active_link(vif, link, link_id) {
|
||||
struct ieee80211_link_sta *link_sta =
|
||||
link_sta_dereference_check(sta, link_id);
|
||||
|
|
@ -822,9 +956,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
|
|||
if (WARN_ON(!mld_link_sta))
|
||||
return;
|
||||
|
||||
mld_link_sta->last_rate_n_flags =
|
||||
iwl_v3_rate_from_v2_v3(notif->rate,
|
||||
mld->fw_rates_ver_3);
|
||||
mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate);
|
||||
|
||||
rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
|
||||
mld_link_sta->last_rate_n_flags);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 - 2025 Intel Corporation
|
||||
* Copyright (C) 2024 - 2026 Intel Corporation
|
||||
*/
|
||||
#include <net/ip.h>
|
||||
|
||||
|
|
@ -71,14 +71,19 @@ static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
|
|||
{
|
||||
u8 tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : txq->tid;
|
||||
u32 fw_sta_mask = iwl_mld_fw_sta_id_mask(mld, txq->sta);
|
||||
/* We can't know when the station is asleep or awake, so we
|
||||
* must disable the queue hang detection.
|
||||
*/
|
||||
unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ?
|
||||
IWL_WATCHDOG_DISABLED :
|
||||
mld->trans->mac_cfg->base->wd_timeout;
|
||||
unsigned int watchdog_timeout;
|
||||
int queue, size;
|
||||
|
||||
switch (txq->vif->type) {
|
||||
case NL80211_IFTYPE_AP: /* STA might go to PS */
|
||||
case NL80211_IFTYPE_NAN_DATA: /* peer might ULW/break schedule */
|
||||
watchdog_timeout = IWL_WATCHDOG_DISABLED;
|
||||
break;
|
||||
default:
|
||||
watchdog_timeout = mld->trans->mac_cfg->base->wd_timeout;
|
||||
break;
|
||||
}
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (tid == IWL_MGMT_TID)
|
||||
|
|
@ -346,7 +351,8 @@ u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld,
|
|||
iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band);
|
||||
|
||||
if (band >= NUM_NL80211_BANDS) {
|
||||
WARN_ON(vif->type != NL80211_IFTYPE_NAN);
|
||||
WARN_ON(vif->type != NL80211_IFTYPE_NAN &&
|
||||
vif->type != NL80211_IFTYPE_NAN_DATA);
|
||||
return IWL_FIRST_OFDM_RATE;
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +495,7 @@ static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld,
|
|||
rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) |
|
||||
iwl_mld_get_tx_ant(mld, info, sta, fc);
|
||||
|
||||
return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3);
|
||||
return cpu_to_le32(rate);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -552,10 +558,12 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb,
|
|||
flags |= IWL_TX_FLAGS_ENCRYPT_DIS;
|
||||
|
||||
/* For data and mgmt packets rate info comes from the fw.
|
||||
* Only set rate/antenna for injected frames with fixed rate, or
|
||||
* when no sta is given.
|
||||
* Only set rate/antenna for:
|
||||
* - injected frames with fixed rate,
|
||||
* - when no sta is given.
|
||||
* - frames that are sent to an NMI sta, which is only used for management.
|
||||
*/
|
||||
if (unlikely(!sta ||
|
||||
if (unlikely(!sta || mld_sta->vif->type == NL80211_IFTYPE_NAN ||
|
||||
info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) {
|
||||
flags |= IWL_TX_FLAGS_CMD_RATE;
|
||||
rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta,
|
||||
|
|
@ -673,11 +681,16 @@ iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq,
|
|||
WARN_ON(!ieee80211_is_mgmt(fc));
|
||||
return mld_vif->aux_sta.queue_id;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
|
||||
|
||||
WARN_ON(!ieee80211_is_mgmt(fc));
|
||||
return iwl_mld_nan_get_mgmt_queue(mld, info->control.vif);
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
WARN_ON(!ieee80211_is_data(fc));
|
||||
|
||||
return mld_vif->aux_sta.queue_id;
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
break;
|
||||
|
||||
mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
|
||||
return mld_vif->nan.mcast_data_sta.queue_id;
|
||||
default:
|
||||
WARN_ONCE(1, "Unsupported vif type\n");
|
||||
break;
|
||||
|
|
@ -1018,14 +1031,11 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
|
|||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld,
|
||||
__le32 rate_n_flags_fw,
|
||||
static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags,
|
||||
struct ieee80211_tx_info *info)
|
||||
{
|
||||
enum nl80211_band band = info->band;
|
||||
struct ieee80211_tx_rate *tx_rate = &info->status.rates[0];
|
||||
u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw,
|
||||
mld->fw_rates_ver_3);
|
||||
u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK;
|
||||
u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
|
||||
u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
|
||||
|
|
@ -1156,7 +1166,8 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld,
|
|||
iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL);
|
||||
}
|
||||
|
||||
iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info);
|
||||
iwl_mld_hwrate_to_tx_rate(le32_to_cpu(tx_resp->initial_rate),
|
||||
info);
|
||||
|
||||
if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1)))
|
||||
ieee80211_tx_status_skb(mld->hw, skb);
|
||||
|
|
|
|||
|
|
@ -261,20 +261,28 @@ iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,
|
|||
|
||||
switch (peer->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
|
||||
*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
|
||||
*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
|
||||
|
|
@ -282,8 +290,10 @@ iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,
|
|||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
if (cmd_ver >= 13) {
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
|
||||
*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2018-2024, 2026 Intel Corporation
|
||||
*/
|
||||
#include <net/cfg80211.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
|
@ -54,27 +54,37 @@ static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
|
|||
{
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
|
||||
*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
|
||||
*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if (cmd_ver >= 9) {
|
||||
*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
|
||||
*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
|
||||
*format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
*format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -469,7 +469,7 @@ struct iwl_mvm_vif {
|
|||
struct dentry *dbgfs_slink;
|
||||
struct iwl_dbgfs_pm dbgfs_pm;
|
||||
struct iwl_dbgfs_bf dbgfs_bf;
|
||||
struct iwl_mac_power_cmd mac_pwr_cmd;
|
||||
struct iwl_mac_power_cmd_v2 mac_pwr_cmd;
|
||||
int dbgfs_quota_min;
|
||||
bool ftm_unprotected;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -335,7 +335,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
|
|||
RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_NOTIF,
|
||||
iwl_mvm_handle_rx_system_oper_stats,
|
||||
RX_HANDLER_ASYNC_LOCKED_WIPHY,
|
||||
struct iwl_system_statistics_notif_oper),
|
||||
struct iwl_system_statistics_notif_oper_v3),
|
||||
RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF,
|
||||
iwl_mvm_handle_rx_system_oper_part1_stats,
|
||||
RX_HANDLER_ASYNC_LOCKED,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -83,7 +83,7 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
struct iwl_mac_power_cmd_v2 *cmd)
|
||||
{
|
||||
IWL_DEBUG_POWER(mvm,
|
||||
"Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
|
||||
|
|
@ -121,7 +121,7 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
|
|||
|
||||
static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
struct iwl_mac_power_cmd_v2 *cmd)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
enum ieee80211_ac_numbers ac;
|
||||
|
|
@ -296,7 +296,7 @@ static bool iwl_mvm_power_is_radar(struct ieee80211_bss_conf *link_conf)
|
|||
|
||||
static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
struct iwl_mac_power_cmd_v2 *cmd)
|
||||
{
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
unsigned int min_link_skip = ~0;
|
||||
|
|
@ -344,7 +344,7 @@ static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
|
|||
|
||||
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd *cmd)
|
||||
struct iwl_mac_power_cmd_v2 *cmd)
|
||||
{
|
||||
int dtimper, bi;
|
||||
int keep_alive;
|
||||
|
|
@ -466,7 +466,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
|
|||
static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
struct iwl_mac_power_cmd_v2 cmd = {};
|
||||
|
||||
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
|
||||
iwl_mvm_power_log(mvm, &cmd);
|
||||
|
|
@ -717,7 +717,7 @@ int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
|
|||
int bufsz)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
struct iwl_mac_power_cmd_v2 cmd = {};
|
||||
int pos = 0;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
|
|
|||
|
|
@ -325,11 +325,11 @@ void iwl_mvm_ptp_remove(struct iwl_mvm *mvm)
|
|||
mvm->ptp_data.ptp_clock_info.name,
|
||||
ptp_clock_index(mvm->ptp_data.ptp_clock));
|
||||
|
||||
cancel_delayed_work_sync(&mvm->ptp_data.dwork);
|
||||
ptp_clock_unregister(mvm->ptp_data.ptp_clock);
|
||||
mvm->ptp_data.ptp_clock = NULL;
|
||||
memset(&mvm->ptp_data.ptp_clock_info, 0,
|
||||
sizeof(mvm->ptp_data.ptp_clock_info));
|
||||
mvm->ptp_data.last_gp2 = 0;
|
||||
cancel_delayed_work_sync(&mvm->ptp_data.dwork);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -757,8 +757,9 @@ iwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le,
|
|||
spin_unlock(&mvm->tcm.lock);
|
||||
}
|
||||
|
||||
static void iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm,
|
||||
struct iwl_stats_ntfy_per_phy *per_phy)
|
||||
static void
|
||||
iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm,
|
||||
struct iwl_stats_ntfy_per_phy_v1 *per_phy)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -937,7 +938,7 @@ void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm,
|
|||
{
|
||||
u8 average_energy[IWL_STATION_COUNT_MAX];
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
struct iwl_system_statistics_notif_oper *stats;
|
||||
struct iwl_system_statistics_notif_oper_v3 *stats;
|
||||
int i;
|
||||
u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP,
|
||||
STATISTICS_OPER_NOTIF, 0);
|
||||
|
|
|
|||
|
|
@ -537,6 +537,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = {
|
|||
{IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0xA840, 0x1775, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0xA840, 0x1776, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0xA840, 0x1735, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0xA840, 0x1736, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_mac_cfg)},
|
||||
{IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_mac_cfg)},
|
||||
|
||||
|
|
@ -1052,6 +1054,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
|
|||
IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750i_name, SUBDEV(0x1772)),
|
||||
IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790s_name, SUBDEV(0x1791)),
|
||||
IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790i_name, SUBDEV(0x1792)),
|
||||
IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_killer_be1730x_name, SUBDEV(0x1730)),
|
||||
IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_killer_be1730x_name, SUBDEV(0x1731)),
|
||||
|
||||
/* Killer discrete */
|
||||
IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750w_name,
|
||||
|
|
@ -1069,7 +1073,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
|
|||
|
||||
/* PE RF */
|
||||
IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)),
|
||||
IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE),
|
||||
IWL_DEV_INFO(iwl_rf_pe_no_uhr, iwl_be223_name, RF_TYPE(PE),
|
||||
SUBDEV_MASKED(0x0524, 0xFFF)),
|
||||
IWL_DEV_INFO(iwl_rf_pe, iwl_bn203_name, RF_TYPE(PE),
|
||||
SUBDEV_MASKED(0x0324, 0xFFF)),
|
||||
|
|
@ -1077,6 +1081,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
|
|||
/* Killer */
|
||||
IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775s_name, SUBDEV(0x1776)),
|
||||
IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775i_name, SUBDEV(0x1775)),
|
||||
IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_killer_be1735x_name, SUBDEV(0x1735)),
|
||||
IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_killer_be1735x_name, SUBDEV(0x1736)),
|
||||
|
||||
IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850w2_name, SUBDEV(0x1851)),
|
||||
IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850i_name, SUBDEV(0x1852)),
|
||||
|
|
@ -1239,8 +1245,12 @@ static int _iwl_pci_resume(struct device *device, bool restore)
|
|||
u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH);
|
||||
|
||||
if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) ||
|
||||
scratch == ~0U)
|
||||
scratch == ~0U) {
|
||||
device_was_powered_off = true;
|
||||
IWL_DEBUG_WOWLAN(trans,
|
||||
"Scratch 0x%08x indicates device was powered off\n",
|
||||
scratch);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* bh are re-enabled by iwl_trans_pcie_release_nic_access,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2003-2015, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2003-2015, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -287,6 +287,106 @@ enum iwl_pcie_imr_status {
|
|||
IMR_D2S_ERROR,
|
||||
};
|
||||
|
||||
/*
|
||||
* The FH will write back to the first TB only, so we need to copy some data
|
||||
* into the buffer regardless of whether it should be mapped or not.
|
||||
* This indicates how big the first TB must be to include the scratch buffer
|
||||
* and the assigned PN.
|
||||
* Since PN location is 8 bytes at offset 12, it's 20 now.
|
||||
* If we make it bigger then allocations will be bigger and copy slower, so
|
||||
* that's probably not useful.
|
||||
*/
|
||||
#define IWL_FIRST_TB_SIZE 20
|
||||
#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64)
|
||||
|
||||
struct iwl_pcie_txq_entry {
|
||||
void *cmd;
|
||||
struct sk_buff *skb;
|
||||
/* buffer to free after command completes */
|
||||
const void *free_buf;
|
||||
struct iwl_cmd_meta meta;
|
||||
};
|
||||
|
||||
struct iwl_pcie_first_tb_buf {
|
||||
u8 buf[IWL_FIRST_TB_SIZE_ALIGN];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_txq - Tx Queue for DMA
|
||||
* @tfds: transmit frame descriptors (DMA memory)
|
||||
* @first_tb_bufs: start of command headers, including scratch buffers, for
|
||||
* the writeback -- this is DMA memory and an array holding one buffer
|
||||
* for each command on the queue
|
||||
* @first_tb_dma: DMA address for the first_tb_bufs start
|
||||
* @entries: transmit entries (driver state)
|
||||
* @lock: queue lock
|
||||
* @reclaim_lock: reclaim lock
|
||||
* @stuck_timer: timer that fires if queue gets stuck
|
||||
* @trans: pointer back to transport (for timer)
|
||||
* @need_update: indicates need to update read/write index
|
||||
* @ampdu: true if this queue is an ampdu queue for a specific RA/TID
|
||||
* @wd_timeout: queue watchdog timeout (jiffies) - per queue
|
||||
* @frozen: tx stuck queue timer is frozen
|
||||
* @frozen_expiry_remainder: remember how long until the timer fires
|
||||
* @block: queue is blocked
|
||||
* @bc_tbl: byte count table of the queue (relevant only for gen2 transport)
|
||||
* @write_ptr: 1-st empty entry (index) host_w
|
||||
* @read_ptr: last used entry (index) host_r
|
||||
* @dma_addr: physical addr for BDs
|
||||
* @n_window: safe queue window
|
||||
* @id: queue id
|
||||
* @low_mark: low watermark, resume queue if free space more than this
|
||||
* @high_mark: high watermark, stop queue if free space less than this
|
||||
* @overflow_q: overflow queue for handling frames that didn't fit on HW queue
|
||||
* @overflow_tx: need to transmit from overflow
|
||||
*
|
||||
* A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
|
||||
* descriptors) and required locking structures.
|
||||
*
|
||||
* Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
|
||||
* always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
|
||||
* there might be HW changes in the future). For the normal TX
|
||||
* queues, n_window, which is the size of the software queue data
|
||||
* is also 256; however, for the command queue, n_window is only
|
||||
* 32 since we don't need so many commands pending. Since the HW
|
||||
* still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256.
|
||||
* This means that we end up with the following:
|
||||
* HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
|
||||
* SW entries: | 0 | ... | 31 |
|
||||
* where N is a number between 0 and 7. This means that the SW
|
||||
* data is a window overlaid over the HW queue.
|
||||
*/
|
||||
struct iwl_txq {
|
||||
void *tfds;
|
||||
struct iwl_pcie_first_tb_buf *first_tb_bufs;
|
||||
dma_addr_t first_tb_dma;
|
||||
struct iwl_pcie_txq_entry *entries;
|
||||
/* lock for syncing changes on the queue */
|
||||
spinlock_t lock;
|
||||
/* lock to prevent concurrent reclaim */
|
||||
spinlock_t reclaim_lock;
|
||||
unsigned long frozen_expiry_remainder;
|
||||
struct timer_list stuck_timer;
|
||||
struct iwl_trans *trans;
|
||||
bool need_update;
|
||||
bool frozen;
|
||||
bool ampdu;
|
||||
int block;
|
||||
unsigned long wd_timeout;
|
||||
struct sk_buff_head overflow_q;
|
||||
struct iwl_dma_ptr bc_tbl;
|
||||
|
||||
int write_ptr;
|
||||
int read_ptr;
|
||||
dma_addr_t dma_addr;
|
||||
int n_window;
|
||||
u32 id;
|
||||
int low_mark;
|
||||
int high_mark;
|
||||
|
||||
bool overflow_tx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_pcie_txqs - TX queues data
|
||||
*
|
||||
|
|
@ -1086,6 +1186,8 @@ u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg);
|
|||
void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val);
|
||||
int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, int dwords);
|
||||
int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords);
|
||||
int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership);
|
||||
struct iwl_trans_dump_data *
|
||||
iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
|
||||
|
|
@ -1153,6 +1255,9 @@ int iwl_trans_pcie_copy_imr(struct iwl_trans *trans,
|
|||
int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue,
|
||||
struct iwl_trans_rxq_dma_data *data);
|
||||
|
||||
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
|
||||
struct iwl_host_cmd *cmd);
|
||||
|
||||
static inline bool iwl_pcie_gen1_is_pm_supported(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-trans.h"
|
||||
#include "iwl-prph.h"
|
||||
|
|
@ -327,6 +327,13 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
|
|||
else
|
||||
pos = scnprintf(buf, buflen, "WH");
|
||||
break;
|
||||
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_PE):
|
||||
if (SILICON_Z_STEP ==
|
||||
CSR_HW_RFID_STEP(trans->info.hw_rf_id))
|
||||
pos = scnprintf(buf, buflen, "PETC");
|
||||
else
|
||||
pos = scnprintf(buf, buflen, "PE");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2007-2015, 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2007-2015, 2018-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -141,10 +141,10 @@ static void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans,
|
|||
return;
|
||||
|
||||
if (power != max_power)
|
||||
IWL_ERR(trans,
|
||||
"Sorry - debug buffer is only %luK while you requested %luK\n",
|
||||
(unsigned long)BIT(power - 10),
|
||||
(unsigned long)BIT(max_power - 10));
|
||||
IWL_INFO(trans,
|
||||
"Sorry - debug buffer is only %luK while you requested %luK\n",
|
||||
(unsigned long)BIT(power - 10),
|
||||
(unsigned long)BIT(max_power - 10));
|
||||
|
||||
fw_mon->block = block;
|
||||
fw_mon->physical = physical;
|
||||
|
|
@ -2044,7 +2044,7 @@ iwl_trans_pcie_call_prod_reset_dsm(struct pci_dev *pdev, u16 cmd, u16 value)
|
|||
0xDD, 0x26, 0xB5, 0xFD);
|
||||
|
||||
if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &dsm_guid, ACPI_DSM_REV,
|
||||
DSM_INTERNAL_FUNC_PRODUCT_RESET))
|
||||
BIT(DSM_INTERNAL_FUNC_PRODUCT_RESET)))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return iwl_acpi_get_dsm_object(&pdev->dev, ACPI_DSM_REV,
|
||||
|
|
@ -2424,6 +2424,15 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
spin_unlock_bh(&trans_pcie->reg_lock);
|
||||
cond_resched();
|
||||
spin_lock_bh(&trans_pcie->reg_lock);
|
||||
}
|
||||
|
||||
void __releases(nic_access_nobh)
|
||||
iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
|
||||
{
|
||||
|
|
@ -2506,6 +2515,51 @@ int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
#define IWL_MAX_HW_ERRS 5
|
||||
unsigned int num_consec_hw_errors = 0;
|
||||
u32 offs = 0;
|
||||
u32 *vals = buf;
|
||||
|
||||
lockdep_assert_held(&trans_pcie->reg_lock);
|
||||
|
||||
while (offs < dwords) {
|
||||
/* limit the time we spin here under lock to 1/2s */
|
||||
unsigned long end = jiffies + HZ / 2;
|
||||
bool resched = false;
|
||||
|
||||
iwl_write32(trans, HBUS_TARG_MEM_RADDR,
|
||||
addr + 4 * offs);
|
||||
|
||||
while (offs < dwords) {
|
||||
vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
|
||||
|
||||
if (iwl_trans_is_hw_error_value(vals[offs]))
|
||||
num_consec_hw_errors++;
|
||||
else
|
||||
num_consec_hw_errors = 0;
|
||||
|
||||
if (num_consec_hw_errors >= IWL_MAX_HW_ERRS)
|
||||
return -EIO;
|
||||
|
||||
offs++;
|
||||
|
||||
if (time_after(jiffies, end)) {
|
||||
resched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resched)
|
||||
iwl_trans_pcie_resched_with_nic_access(trans);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs,
|
||||
u32 *val)
|
||||
{
|
||||
|
|
@ -3201,8 +3255,7 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file,
|
|||
return -EINVAL;
|
||||
trans->request_top_reset = 1;
|
||||
}
|
||||
iwl_op_mode_nic_error(trans->op_mode, IWL_ERR_TYPE_DEBUGFS);
|
||||
iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_DEBUGFS);
|
||||
iwl_trans_fw_error(trans, IWL_ERR_TYPE_DEBUGFS);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
@ -4000,6 +4053,30 @@ static void get_crf_id(struct iwl_trans *iwl_trans,
|
|||
else
|
||||
sd_reg_ver_addr = SD_REG_VER;
|
||||
|
||||
/* wait until the device is ready to access the prph registers */
|
||||
if (iwl_trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_DR ||
|
||||
iwl_trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_SC) {
|
||||
u32 req = iwl_read_umac_prph_no_grab(iwl_trans,
|
||||
WFPM_RSRCS_4PHS_REQ_STTS);
|
||||
int ret;
|
||||
|
||||
if (!(req & RSRC_REQ_CNVR_TOP)) {
|
||||
IWL_ERR(iwl_trans,
|
||||
"WFPM_RSRCS_4PHS_REQ_STTS bit 6 is clear 0x%x\n",
|
||||
req);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = iwl_poll_umac_prph_bits_no_grab(iwl_trans,
|
||||
WFPM_RSRCS_4PHS_ACK_STTS,
|
||||
RSRC_ACK_CNVR_TOP,
|
||||
RSRC_ACK_CNVR_TOP,
|
||||
50 * 1000);
|
||||
if (ret < 0)
|
||||
IWL_ERR(iwl_trans,
|
||||
"WFPM_RSRCS_4PHS_ACK_STTS bit 6 is clear\n");
|
||||
}
|
||||
|
||||
/* Enable access to peripheral registers */
|
||||
val = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG);
|
||||
val |= WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user