diff options
| author | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
| commit | 1ac731c529cd4d6adbce134754b51ff7d822b145 (patch) | |
| tree | 143ab3f35ca5f3b69f583c84e6964b17139c2ec1 /drivers/net/wireless/intel | |
| parent | 07b4c950f27bef0362dc6ad7ee713aab61d58149 (diff) | |
| parent | 54116d442e001e1b6bd482122043b1870998a1f3 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.6 merge window.
Diffstat (limited to 'drivers/net/wireless/intel')
82 files changed, 9948 insertions, 2265 deletions
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index d382f2017325..dfe0f74369e6 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -377,19 +377,6 @@ static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)  	_ipw_read8(ipw, ofs); \  }) -/* 16-bit direct read (low 4K) */ -static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs) -{ -	return readw(ipw->hw_base + ofs); -} - -/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */ -#define ipw_read16(ipw, ofs) ({ \ -	IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \ -			(u32)(ofs)); \ -	_ipw_read16(ipw, ofs); \ -}) -  /* 32-bit direct read (low 4K) */  static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)  { @@ -1234,9 +1221,9 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)  	u32 base = ipw_read32(priv, IPW_ERROR_LOG);  	u32 elem_len = ipw_read_reg32(priv, base); -	error = kmalloc(sizeof(*error) + -			sizeof(*error->elem) * elem_len + -			sizeof(*error->log) * log_len, GFP_ATOMIC); +	error = kmalloc(size_add(struct_size(error, elem, elem_len), +				 array_size(sizeof(*error->log), log_len)), +			GFP_ATOMIC);  	if (!error) {  		IPW_ERROR("Memory allocation for firmware error log "  			  "failed.\n"); @@ -1247,7 +1234,6 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)  	error->config = priv->config;  	error->elem_len = elem_len;  	error->log_len = log_len; -	error->elem = (struct ipw_error_elem *)error->payload;  	error->log = (struct ipw_event *)(error->elem + elem_len);  	ipw_capture_event_log(priv, log_len, error->log); diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h index 09ddd21608d4..8ebf09121e17 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h @@ -1106,9 +1106,8 @@ struct ipw_fw_error {	 /* XXX */  	u32 config;  	u32 elem_len;  	u32 log_len; -	struct ipw_error_elem *elem;  	struct ipw_event *log; -	u8 payload[]; +	struct ipw_error_elem elem[];  } __packed;  #ifdef CONFIG_IPW2200_PROMISCUOUS diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 3bdd6774716d..b6f82510e980 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -10,7 +10,7 @@  #include "fw/api/txq.h"  /* Highest firmware API version supported */ -#define IWL_22000_UCODE_API_MAX	74 +#define IWL_22000_UCODE_API_MAX	78  /* Lowest firmware API version supported */  #define IWL_22000_UCODE_API_MIN	39 @@ -50,21 +50,35 @@  #define IWL_MA_A_GF4_A_FW_PRE		"iwlwifi-ma-a0-gf4-a0-"  #define IWL_MA_A_MR_A_FW_PRE		"iwlwifi-ma-a0-mr-a0-"  #define IWL_MA_A_FM_A_FW_PRE		"iwlwifi-ma-a0-fm-a0-" +#define IWL_MA_B_HR_B_FW_PRE		"iwlwifi-ma-b0-hr-b0-" +#define IWL_MA_B_GF_A_FW_PRE		"iwlwifi-ma-b0-gf-a0-" +#define IWL_MA_B_GF4_A_FW_PRE		"iwlwifi-ma-b0-gf4-a0-" +#define IWL_MA_B_MR_A_FW_PRE		"iwlwifi-ma-b0-mr-a0-" +#define IWL_MA_B_FM_A_FW_PRE		"iwlwifi-ma-b0-fm-a0-"  #define IWL_SNJ_A_MR_A_FW_PRE		"iwlwifi-SoSnj-a0-mr-a0-" +#define IWL_BZ_A_HR_A_FW_PRE		"iwlwifi-bz-a0-hr-b0-"  #define IWL_BZ_A_HR_B_FW_PRE		"iwlwifi-bz-a0-hr-b0-"  #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_BZ_A_MR_A_FW_PRE		"iwlwifi-bz-a0-mr-a0-"  #define IWL_BZ_A_FM_A_FW_PRE		"iwlwifi-bz-a0-fm-a0-"  #define IWL_BZ_A_FM4_A_FW_PRE		"iwlwifi-bz-a0-fm4-a0-" +#define IWL_BZ_A_FM_B_FW_PRE		"iwlwifi-bz-a0-fm-b0-" +#define IWL_BZ_A_FM4_B_FW_PRE		"iwlwifi-bz-a0-fm4-b0-"  #define IWL_GL_A_FM_A_FW_PRE		"iwlwifi-gl-a0-fm-a0-"  #define IWL_GL_B_FM_B_FW_PRE		"iwlwifi-gl-b0-fm-b0-"  #define IWL_BZ_Z_GF_A_FW_PRE		"iwlwifi-bz-z0-gf-a0-"  #define IWL_BNJ_A_FM_A_FW_PRE		"iwlwifi-BzBnj-a0-fm-a0-"  #define IWL_BNJ_A_FM4_A_FW_PRE		"iwlwifi-BzBnj-a0-fm4-a0-" +#define IWL_BNJ_B_FM4_B_FW_PRE		"iwlwifi-BzBnj-b0-fm4-b0-"  #define IWL_BNJ_A_GF_A_FW_PRE		"iwlwifi-BzBnj-a0-gf-a0-" +#define IWL_BNJ_B_GF_A_FW_PRE		"iwlwifi-BzBnj-b0-gf-a0-"  #define IWL_BNJ_A_GF4_A_FW_PRE		"iwlwifi-BzBnj-a0-gf4-a0-" +#define IWL_BNJ_B_GF4_A_FW_PRE		"iwlwifi-BzBnj-b0-gf4-a0-" +#define IWL_BNJ_A_HR_A_FW_PRE		"iwlwifi-BzBnj-a0-hr-b0-"  #define IWL_BNJ_A_HR_B_FW_PRE		"iwlwifi-BzBnj-a0-hr-b0-" +#define IWL_BNJ_B_HR_A_FW_PRE		"iwlwifi-BzBnj-b0-hr-b0-" +#define IWL_BNJ_B_HR_B_FW_PRE		"iwlwifi-BzBnj-b0-hr-b0-"  #define IWL_BNJ_B_FM_B_FW_PRE		"iwlwifi-BzBnj-b0-fm-b0-" @@ -110,8 +124,20 @@  	IWL_MA_A_MR_A_FW_PRE __stringify(api) ".ucode"  #define IWL_MA_A_FM_A_FW_MODULE_FIRMWARE(api)		\  	IWL_MA_A_FM_A_FW_PRE __stringify(api) ".ucode" +#define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api)		\ +	IWL_MA_B_HR_B_FW_PRE __stringify(api) ".ucode" +#define IWL_MA_B_GF_A_FW_MODULE_FIRMWARE(api)		\ +	IWL_MA_B_GF_A_FW_PRE __stringify(api) ".ucode" +#define IWL_MA_B_GF4_A_FW_MODULE_FIRMWARE(api)		\ +	IWL_MA_B_GF4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_MA_B_MR_A_FW_MODULE_FIRMWARE(api) \ +	IWL_MA_B_MR_A_FW_PRE __stringify(api) ".ucode" +#define IWL_MA_B_FM_A_FW_MODULE_FIRMWARE(api)		\ +	IWL_MA_B_FM_A_FW_PRE __stringify(api) ".ucode"  #define IWL_SNJ_A_MR_A_MODULE_FIRMWARE(api) \  	IWL_SNJ_A_MR_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BZ_A_HR_A_MODULE_FIRMWARE(api) \ +	IWL_BZ_A_HR_A_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_BZ_A_GF_A_MODULE_FIRMWARE(api) \ @@ -121,23 +147,39 @@  #define IWL_BZ_A_MR_A_MODULE_FIRMWARE(api) \  	IWL_BZ_A_MR_A_FW_PRE __stringify(api) ".ucode"  #define IWL_BZ_A_FM_A_MODULE_FIRMWARE(api) \ -		IWL_BZ_A_FM_A_FW_PRE __stringify(api) ".ucode" +	IWL_BZ_A_FM_A_FW_PRE __stringify(api) ".ucode"  #define IWL_BZ_A_FM4_A_MODULE_FIRMWARE(api) \ -		IWL_BZ_A_FM4_A_FW_PRE __stringify(api) ".ucode" +	IWL_BZ_A_FM4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BZ_A_FM_B_MODULE_FIRMWARE(api) \ +	IWL_BZ_A_FM_B_FW_PRE __stringify(api) ".ucode" +#define IWL_BZ_A_FM4_B_MODULE_FIRMWARE(api) \ +	IWL_BZ_A_FM4_B_FW_PRE __stringify(api) ".ucode"  #define IWL_GL_A_FM_A_MODULE_FIRMWARE(api) \ -		IWL_GL_A_FM_A_FW_PRE __stringify(api) ".ucode" +	IWL_GL_A_FM_A_FW_PRE __stringify(api) ".ucode"  #define IWL_GL_B_FM_B_MODULE_FIRMWARE(api) \ -		IWL_GL_B_FM_B_FW_PRE __stringify(api) ".ucode" +	IWL_GL_B_FM_B_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_A_FM_A_MODULE_FIRMWARE(api) \  	IWL_BNJ_A_FM_A_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(api) \  	IWL_BNJ_A_FM4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_FM4_B_MODULE_FIRMWARE(api) \ +	IWL_BNJ_B_FM4_B_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_A_GF_A_MODULE_FIRMWARE(api) \  	IWL_BNJ_A_GF_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_GF_A_MODULE_FIRMWARE(api) \ +	IWL_BNJ_B_GF_A_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(api) \  	IWL_BNJ_A_GF4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_GF4_A_MODULE_FIRMWARE(api) \ +	IWL_BNJ_B_GF4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_A_HR_A_MODULE_FIRMWARE(api) \ +	IWL_BNJ_A_HR_A_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_A_HR_B_MODULE_FIRMWARE(api) \  	IWL_BNJ_A_HR_B_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_HR_A_MODULE_FIRMWARE(api) \ +	IWL_BNJ_B_HR_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_HR_B_MODULE_FIRMWARE(api) \ +	IWL_BNJ_B_HR_B_FW_PRE __stringify(api) ".ucode"  #define IWL_BNJ_B_FM_B_MODULE_FIRMWARE(api) \  	IWL_BNJ_B_FM_B_FW_PRE __stringify(api) ".ucode" @@ -278,7 +320,7 @@ static const struct iwl_ht_params iwl_gl_a_ht_params = {  	.trans.gen2 = true,						\  	.nvm_type = IWL_NVM_EXT,					\  	.dbgc_supported = true,						\ -	.min_umac_error_event_table = 0x400000,				\ +	.min_umac_error_event_table = 0xD0000,				\  	.d3_debug_data_base_addr = 0x401000,				\  	.d3_debug_data_length = 60 * 1024,				\  	.mon_smem_regs = {						\ @@ -864,6 +906,41 @@ const struct iwl_cfg iwl_cfg_ma_a0_ms_a0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_ma_b0_fm_a0 = { +	.fw_name_pre = IWL_MA_B_FM_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_AX210, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_ma_b0_hr_b0 = { +	.fw_name_pre = IWL_MA_B_HR_B_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_AX210, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_ma_b0_gf_a0 = { +	.fw_name_pre = IWL_MA_B_GF_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_AX210, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_ma_b0_gf4_a0 = { +	.fw_name_pre = IWL_MA_B_GF4_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_AX210, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_ma_b0_mr_a0 = { +	.fw_name_pre = IWL_MA_B_MR_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_AX210, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_so_a0_ms_a0 = {  	.fw_name_pre = IWL_SO_A_MR_A_FW_PRE,  	.uhb_supported = false, @@ -910,6 +987,14 @@ const struct iwl_cfg iwl_cfg_quz_a0_hr_b0 = {  	.num_rbds = IWL_NUM_RBDS_22000_HE,  }; +const struct iwl_cfg iwl_cfg_bz_a0_hr_a0 = { +	.fw_name_pre = IWL_BZ_A_HR_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_bz_a0_hr_b0 = {  	.fw_name_pre = IWL_BZ_A_HR_B_FW_PRE,  	.uhb_supported = true, @@ -958,6 +1043,22 @@ const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_bz_a0_fm_b0 = { +	.fw_name_pre = IWL_BZ_A_FM_B_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_bz_a0_fm4_b0 = { +	.fw_name_pre = IWL_BZ_A_FM4_B_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_gl_a0_fm_a0 = {  	.fw_name_pre = IWL_GL_A_FM_A_FW_PRE,  	.uhb_supported = true, @@ -998,6 +1099,14 @@ const struct iwl_cfg iwl_cfg_bnj_a0_fm4_a0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_bnj_b0_fm4_b0 = { +	.fw_name_pre = IWL_BNJ_B_FM4_B_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0 = {  	.fw_name_pre = IWL_BNJ_A_GF_A_FW_PRE,  	.uhb_supported = true, @@ -1006,6 +1115,14 @@ const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_bnj_b0_gf_a0 = { +	.fw_name_pre = IWL_BNJ_B_GF_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0 = {  	.fw_name_pre = IWL_BNJ_A_GF4_A_FW_PRE,  	.uhb_supported = true, @@ -1014,6 +1131,22 @@ const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_bnj_b0_gf4_a0 = { +	.fw_name_pre = IWL_BNJ_B_GF4_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_bnj_a0_hr_a0 = { +	.fw_name_pre = IWL_BNJ_A_HR_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0 = {  	.fw_name_pre = IWL_BNJ_A_HR_B_FW_PRE,  	.uhb_supported = true, @@ -1022,6 +1155,22 @@ const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0 = {  	.num_rbds = IWL_NUM_RBDS_AX210_HE,  }; +const struct iwl_cfg iwl_cfg_bnj_b0_hr_a0 = { +	.fw_name_pre = IWL_BNJ_B_HR_A_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_bnj_b0_hr_b0 = { +	.fw_name_pre = IWL_BNJ_B_HR_B_FW_PRE, +	.uhb_supported = true, +	IWL_DEVICE_BZ, +	.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, +	.num_rbds = IWL_NUM_RBDS_AX210_HE, +}; +  const struct iwl_cfg iwl_cfg_bnj_b0_fm_b0 = {  	.fw_name_pre = IWL_BNJ_B_FM_B_FW_PRE,  	.uhb_supported = true, @@ -1050,18 +1199,31 @@ MODULE_FIRMWARE(IWL_MA_A_GF_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_MA_A_GF4_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_MA_A_MR_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_MA_A_FM_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_GF_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_GF4_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_MR_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_FM_A_FW_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_SNJ_A_MR_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BZ_A_HR_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_MR_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BZ_A_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_GL_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_FM4_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_A_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_HR_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BZ_A_FM4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BZ_A_FM4_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_GL_B_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));  MODULE_FIRMWARE(IWL_BNJ_B_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c index cef43cf80620..8b01ab986cb1 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c @@ -1081,6 +1081,7 @@ static int iwlagn_send_sta_key(struct iwl_priv *priv,  {  	__le16 key_flags;  	struct iwl_addsta_cmd sta_cmd; +	size_t to_copy;  	int i;  	spin_lock_bh(&priv->sta_lock); @@ -1100,7 +1101,9 @@ static int iwlagn_send_sta_key(struct iwl_priv *priv,  		sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;  		for (i = 0; i < 5; i++)  			sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); -		memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); +		/* keyconf may contain MIC rx/tx keys which iwl does not use */ +		to_copy = min_t(size_t, sizeof(sta_cmd.key.key), keyconf->keylen); +		memcpy(sta_cmd.key.key, keyconf->key, to_copy);  		break;  	case WLAN_CIPHER_SUITE_WEP104:  		key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index a02e5a67b706..cb9181f05501 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -38,7 +38,7 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = {  	},  	{ .ident = "ASUS",  	  .matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek COMPUTER INC."), +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),  		},  	},  	{} @@ -1006,8 +1006,10 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)  	union acpi_object *wifi_pkg, *data, *flags;  	int i, j, ret, tbl_rev, num_sub_bands = 0;  	int idx = 2; +	u8 cmd_ver;  	fwrt->ppag_flags = 0; +	fwrt->ppag_table_valid = false;  	data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);  	if (IS_ERR(data)) @@ -1054,8 +1056,15 @@ read_table:  	}  	fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK; - -	if (!fwrt->ppag_flags) { +	cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, +					WIDE_ID(PHY_OPS_GROUP, +						PER_PLATFORM_ANT_GAIN_CMD), +					IWL_FW_CMD_VER_UNKNOWN); +	if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) { +		ret = -EINVAL; +		goto out_free; +	} +	if (!fwrt->ppag_flags && cmd_ver <= 3) {  		ret = 0;  		goto out_free;  	} @@ -1076,21 +1085,22 @@ read_table:  			}  			fwrt->ppag_chains[i].subbands[j] = ent->integer.value; - +			/* from ver 4 the fw deals with out of range values */ +			if (cmd_ver >= 4) +				continue;  			if ((j == 0 &&  				(fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB ||  				 fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) ||  				(j != 0 &&  				(fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB ||  				fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { -					fwrt->ppag_flags = 0;  					ret = -EINVAL;  					goto out_free;  				}  		}  	} - +	fwrt->ppag_table_valid = true;  	ret = 0;  out_free: @@ -1115,19 +1125,22 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c                  IWL_DEBUG_RADIO(fwrt,                                  "PPAG capability not supported by FW, command not sent.\n");                  return -EINVAL; -        } -        if (!fwrt->ppag_flags) { -                IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); -                return -EINVAL; -        } +	} + +	cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, +					WIDE_ID(PHY_OPS_GROUP, +						PER_PLATFORM_ANT_GAIN_CMD), +					IWL_FW_CMD_VER_UNKNOWN); +	if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { +		IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); +		return -EINVAL; +	}          /* The 'flags' field is the same in v1 and in v2 so we can just           * use v1 to access it.           */          cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); -        cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, -                                        WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD), -                                        IWL_FW_CMD_VER_UNKNOWN); +  	if (cmd_ver == 1) {                  num_sub_bands = IWL_NUM_SUB_BANDS_V1;                  gain = cmd->v1.gain[0]; @@ -1138,7 +1151,7 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c                                          fwrt->ppag_ver);                          cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);  		} -	} else if (cmd_ver == 2 || cmd_ver == 3) { +	} else if (cmd_ver >= 2 && cmd_ver <= 4) {                  num_sub_bands = IWL_NUM_SUB_BANDS_V2;                  gain = cmd->v2.gain[0];                  *cmd_size = sizeof(cmd->v2); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 28c87a480246..111d96cbde6f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -266,6 +266,24 @@ enum iwl_legacy_cmds {  	HOT_SPOT_CMD = 0x53,  	/** +	 * @WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION: Time Sync +	 *	measurement notification for TM/FTM. Sent on receipt of +	 *	respective WNM action frame for TM protocol or public action +	 *	frame for FTM protocol from peer device along with additional +	 *	meta data specified in &struct iwl_time_msmt_notify +	 */ +	WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION = 0x67, + +	/** +	 * @WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION: Time Sync +	 *	measurement confirmation notification for TM/FTM. Sent on +	 *	receipt of Ack from peer for previously Tx'ed TM/FTM +	 *	action frame along with additional meta data specified in +	 *	&struct iwl_time_msmt_cfm_notify +	 */ +	WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION = 0x68, + +	/**  	 * @SCAN_OFFLOAD_COMPLETE:  	 * notification, &struct iwl_periodic_scan_complete  	 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index df0833890e55..8a613e150a02 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -767,7 +767,7 @@ struct iwl_wowlan_status_v12 {  } __packed; /* WOWLAN_STATUSES_RSP_API_S_VER_12 */  /** - * struct iwl_wowlan_info_notif - WoWLAN information notification + * struct iwl_wowlan_info_notif_v1 - WoWLAN information notification   * @gtk: GTK data   * @igtk: IGTK data   * @replay_ctr: GTK rekey replay counter @@ -785,7 +785,7 @@ struct iwl_wowlan_status_v12 {   * @station_id: station id   * @reserved2: reserved   */ -struct iwl_wowlan_info_notif { +struct iwl_wowlan_info_notif_v1 {  	struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];  	struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM];  	__le64 replay_ctr; @@ -804,6 +804,39 @@ struct iwl_wowlan_info_notif {  } __packed; /* WOWLAN_INFO_NTFY_API_S_VER_1 */  /** + * struct iwl_wowlan_info_notif - WoWLAN information notification + * @gtk: GTK data + * @igtk: IGTK data + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched patterns + * @reserved1: reserved + * @qos_seq_ctr: QoS sequence counters to use next + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @num_of_gtk_rekeys: number of GTK rekeys + * @transmitted_ndps: number of transmitted neighbor discovery packets + * @received_beacons: number of received beacons + * @tid_tear_down: bit mask of tids whose BA sessions were closed + *	in suspend state + * @station_id: station id + * @reserved2: reserved + */ +struct iwl_wowlan_info_notif { +	struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; +	struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM]; +	__le64 replay_ctr; +	__le16 pattern_number; +	__le16 reserved1; +	__le16 qos_seq_ctr[8]; +	__le32 wakeup_reasons; +	__le32 num_of_gtk_rekeys; +	__le32 transmitted_ndps; +	__le32 received_beacons; +	u8 tid_tear_down; +	u8 station_id; +	u8 reserved2[2]; +} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_2 */ + +/**   * struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification   * @wake_packet_length: wakeup packet length   * @station_id: station id diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 8b38a0073077..6f59381b9f9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -1,6 +1,6 @@  /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */  /* - * Copyright (C) 2012-2014, 2018-2020 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   */ @@ -27,6 +27,17 @@ enum iwl_data_path_subcmd_ids {  	TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2,  	/** +	 * @WNM_PLATFORM_PTM_REQUEST_CMD: &struct iwl_time_sync_cfg_cmd +	 */ +	WNM_PLATFORM_PTM_REQUEST_CMD = 0x3, + +	/** +	 * @WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD: +	 *	&struct iwl_time_sync_cfg_cmd +	 */ +	WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD = 0x4, + +	/**  	 * @STA_HE_CTXT_CMD: &struct iwl_he_sta_context_cmd  	 */  	STA_HE_CTXT_CMD = 0x7, @@ -146,6 +157,177 @@ enum iwl_channel_estimation_flags {  	IWL_CHANNEL_ESTIMATION_COUNTER	= BIT(2),  }; +enum iwl_time_sync_protocol_type { +	IWL_TIME_SYNC_PROTOCOL_TM	= BIT(0), +	IWL_TIME_SYNC_PROTOCOL_FTM	= BIT(1), +}; /* WNM_TIMING_ENABLED_PROTOCOL_API_E_VER_1 */ + +/** + * struct iwl_time_sync_cfg_cmd - TM/FTM time sync measurement configuration + * + * @protocols: The type of frames to raise notifications for. A bitmap + *	of @iwl_time_sync_protocol_type + * @peer_addr: peer address with which TM/FTM measurements are required + * @reserved: for alignment + */ +struct iwl_time_sync_cfg_cmd { +	__le32 protocols; +	u8 peer_addr[ETH_ALEN]; +	u8 reserved[2]; +} __packed; /* WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD_API_S_VER_1 */ + +/** + * enum iwl_synced_time_operation - PTM request options + * + * @IWL_SYNCED_TIME_OPERATION_READ_ARTB: read only the ARTB time + * @IWL_SYNCED_TIME_OPERATION_READ_GP2: read only the GP2 time + * @IWL_SYNCED_TIME_OPERATION_READ_BOTH: latch the ARTB and GP2 clocks and + *	provide timestamps from both clocks for the same time point + */ +enum iwl_synced_time_operation { +	IWL_SYNCED_TIME_OPERATION_READ_ARTB = 1, +	IWL_SYNCED_TIME_OPERATION_READ_GP2, +	IWL_SYNCED_TIME_OPERATION_READ_BOTH, +}; + +/** + * struct iwl_synced_time_cmd - request synced GP2/ARTB timestamps + * + * @operation: one of &enum iwl_synced_time_operation + */ +struct iwl_synced_time_cmd { +	__le32 operation; +} __packed; /* WNM_80211V_TIMING_CMD_API_S_VER_1 */ + +/** + * struct iwl_synced_time_rsp - response to iwl_synced_time_cmd + * + * @operation: one of &enum iwl_synced_time_operation + * @platform_timestamp_hi: high DWORD of the ARTB clock timestamp in nanoseconds + * @platform_timestamp_lo: low DWORD of the ARTB clock timestamp in nanoseconds + * @gp2_timestamp_hi: high DWORD of the GP2 clock timestamp in 10's of + *	nanoseconds + * @gp2_timestamp_lo: low DWORD of the GP2 clock timestamp in 10's of + *	nanoseconds + */ +struct iwl_synced_time_rsp { +	__le32 operation; +	__le32 platform_timestamp_hi; +	__le32 platform_timestamp_lo; +	__le32 gp2_timestamp_hi; +	__le32 gp2_timestamp_lo; +} __packed; /* WNM_80211V_TIMING_RSP_API_S_VER_1 */ + +/* PTP_CTX_MAX_DATA_SIZE_IN_API_D_VER_1 */ +#define PTP_CTX_MAX_DATA_SIZE   128 + +/** + * struct iwl_time_msmt_ptp_ctx - Vendor specific information element + * to allow a space for flexibility for the userspace App + * + * @element_id: element id of vendor specific ie + * @length: length of vendor specific ie + * @reserved: for alignment + * @data: vendor specific data blob + */ +struct iwl_time_msmt_ptp_ctx { +	/* Differentiate between FTM and TM specific Vendor IEs */ +	union { +		struct { +			u8 element_id; +			u8 length; +			__le16 reserved; +			u8 data[PTP_CTX_MAX_DATA_SIZE]; +		} ftm; /* FTM specific vendor IE */ +		struct { +			u8 element_id; +			u8 length; +			u8 data[PTP_CTX_MAX_DATA_SIZE]; +		} tm; /* TM specific vendor IE */ +	}; +} __packed /* PTP_CTX_VER_1 */; + +/** + * struct iwl_time_msmt_notify - Time Sync measurement notification + * for TM/FTM, along with additional meta data. + * + * @peer_addr: peer address + * @reserved: for alignment + * @dialog_token: measurement flow dialog token number + * @followup_dialog_token: Measurement flow previous dialog token number + * @t1_hi: high dword of t1-time of the Tx'ed action frame departure on + *	sender side in units of 10 nano seconds + * @t1_lo: low dword of t1-time of the Tx'ed action frame departure on + *	sender side in units of 10 nano seconds + * @t1_max_err: maximum t1-time error in units of 10 nano seconds + * @t4_hi: high dword of t4-time of the Rx'ed action frame's Ack arrival on + *	sender side in units of 10 nano seconds + * @t4_lo: low dword of t4-time of the Rx'ed action frame's Ack arrival on + *	sender side in units of 10 nano seconds + * @t4_max_err: maximum t4-time error in units of 10 nano seconds + * @t2_hi: high dword of t2-time of the Rx'ed action frame arrival on + *	receiver side in units of 10 nano seconds + * @t2_lo: low dword of t2-time of the Rx'ed action frame arrival on + *	receiver side in units of 10 nano seconds + * @t2_max_err: maximum t2-time error in units of 10 nano seconds + * @t3_hi: high dword of t3-time of the Tx'ed action frame's Ack departure on + *	receiver side in units of 10 nano seconds + * @t3_lo: low dword of t3-time of the Tx'ed action frame's Ack departure on + *	receiver side in units of 10 nano seconds + * @t3_max_err: maximum t3-time error in units of 10 nano seconds + * @ptp: vendor specific information element + */ +struct iwl_time_msmt_notify { +	u8 peer_addr[ETH_ALEN]; +	u8 reserved[2]; +	__le32 dialog_token; +	__le32 followup_dialog_token; +	__le32 t1_hi; +	__le32 t1_lo; +	__le32 t1_max_err; +	__le32 t4_hi; +	__le32 t4_lo; +	__le32 t4_max_err; +	__le32 t2_hi; +	__le32 t2_lo; +	__le32 t2_max_err; +	__le32 t3_hi; +	__le32 t3_lo; +	__le32 t3_max_err; +	struct iwl_time_msmt_ptp_ctx ptp; +} __packed; /* WNM_80211V_TIMING_MEASUREMENT_NTFY_API_S_VER_1 */ + +/** + * struct iwl_time_msmt_cfm_notify - Time Sync measurement confirmation + * notification for TM/FTM. Sent on receipt of 802.11 Ack from peer for the + * Tx'ed TM/FTM measurement action frame. + * + * @peer_addr: peer address + * @reserved: for alignment + * @dialog_token: measurement flow dialog token number + * @t1_hi: high dword of t1-time of the Tx'ed action frame departure on + *	sender side in units of 10 nano seconds + * @t1_lo: low dword of t1-time of the Tx'ed action frame departure on + *	sender side in units of 10 nano seconds + * @t1_max_err: maximum t1-time error in units of 10 nano seconds + * @t4_hi: high dword of t4-time of the Rx'ed action frame's Ack arrival on + *	sender side in units of 10 nano seconds + * @t4_lo: low dword of t4-time of the Rx'ed action frame's Ack arrival on + *	sender side in units of 10 nano seconds + * @t4_max_err: maximum t4-time error in units of 10 nano seconds + */ +struct iwl_time_msmt_cfm_notify { +	u8 peer_addr[ETH_ALEN]; +	u8 reserved[2]; +	__le32 dialog_token; +	__le32 t1_hi; +	__le32 t1_lo; +	__le32 t1_max_err; +	__le32 t4_hi; +	__le32 t4_lo; +	__le32 t4_max_err; +} __packed; /* WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NTFY_API_S_VER_1 */ +  /**   * struct iwl_channel_estimation_cfg - channel estimation reporting config   */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 0c555089e05f..8fef38139bf6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -43,6 +43,12 @@ enum iwl_debug_cmds {  	 */  	BUFFER_ALLOCATION = 0x8,  	/** +	 * @GET_TAS_STATUS: +	 * sends command to fw to get TAS status +	 * the response is &struct iwl_mvm_tas_status_resp +	 */ +	GET_TAS_STATUS = 0xA, +	/**  	 * @FW_DUMP_COMPLETE_CMD:  	 * sends command to fw once dump collection completed  	 * &struct iwl_dbg_dump_complete_cmd @@ -421,4 +427,94 @@ struct iwl_dbg_dump_complete_cmd {  	__le32 tp_data;  } __packed; /* FW_DUMP_COMPLETE_CMD_API_S_VER_1 */ +#define TAS_LMAC_BAND_HB       0 +#define TAS_LMAC_BAND_LB       1 +#define TAS_LMAC_BAND_UHB      2 +#define TAS_LMAC_BAND_INVALID  3 + +/** + * struct iwl_mvm_tas_status_per_mac - tas status per lmac + * @static_status: tas statically enabled or disabled per lmac - TRUE/FALSE + * @static_dis_reason: TAS static disable reason, uses + *	&enum iwl_mvm_tas_statically_disabled_reason + * @dynamic_status: Current TAS  status. uses + *	&enum iwl_mvm_tas_dyna_status + * @near_disconnection: is TAS currently near disconnection per lmac? - TRUE/FALSE + * @max_reg_pwr_limit: Regulatory power limits in dBm + * @sar_limit: SAR limits per lmac in dBm + * @band: Band per lmac + * @reserved: reserved + */ +struct iwl_mvm_tas_status_per_mac { +	u8 static_status; +	u8 static_dis_reason; +	u8 dynamic_status; +	u8 near_disconnection; +	__le16 max_reg_pwr_limit; +	__le16 sar_limit; +	u8 band; +	u8 reserved[3]; +} __packed; /*DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1*/ + +/** + * struct iwl_mvm_tas_status_resp - Response to GET_TAS_STATUS + * @tas_fw_version: TAS FW version + * @is_uhb_for_usa_enable: is UHB enabled in USA? - TRUE/FALSE + * @curr_mcc: current mcc + * @block_list: country block list + * @tas_status_mac: TAS status per lmac, uses + *	&struct iwl_mvm_tas_status_per_mac + * @in_dual_radio: is TAS in dual radio? - TRUE/FALSE + * @reserved: reserved + */ +struct iwl_mvm_tas_status_resp { +	u8 tas_fw_version; +	u8 is_uhb_for_usa_enable; +	__le16 curr_mcc; +	__le16 block_list[16]; +	struct iwl_mvm_tas_status_per_mac tas_status_mac[2]; +	u8 in_dual_radio; +	u8 reserved[3]; +} __packed; /*DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3*/ + +/** + * enum iwl_mvm_tas_dyna_status - TAS current running status + * @TAS_DYNA_INACTIVE: TAS status is inactive + * @TAS_DYNA_INACTIVE_MVM_MODE: TAS is disabled due because FW is in MVM mode + *	or is in softap mode. + * @TAS_DYNA_INACTIVE_TRIGGER_MODE: TAS is disabled because FW is in + *	multi user trigger mode + * @TAS_DYNA_INACTIVE_BLOCK_LISTED: TAS is disabled because  current mcc + *	is blocklisted mcc + * @TAS_DYNA_INACTIVE_UHB_NON_US: TAS is disabled because current band is UHB + *	and current mcc is USA + * @TAS_DYNA_ACTIVE: TAS is currently active + * @TAS_DYNA_STATUS_MAX: TAS status max value + */ +enum iwl_mvm_tas_dyna_status { +	TAS_DYNA_INACTIVE, +	TAS_DYNA_INACTIVE_MVM_MODE, +	TAS_DYNA_INACTIVE_TRIGGER_MODE, +	TAS_DYNA_INACTIVE_BLOCK_LISTED, +	TAS_DYNA_INACTIVE_UHB_NON_US, +	TAS_DYNA_ACTIVE, + +	TAS_DYNA_STATUS_MAX, +}; /*_TAS_DYNA_STATUS_E*/ + +/** + * enum iwl_mvm_tas_statically_disabled_reason - TAS statically disabled reason + * @TAS_DISABLED_DUE_TO_BIOS: TAS is disabled because TAS is disabled in BIOS + * @TAS_DISABLED_DUE_TO_SAR_6DBM: TAS is disabled because SAR limit is less than 6 Dbm + * @TAS_DISABLED_REASON_INVALID: TAS disable reason is invalid + * @TAS_DISABLED_REASON_MAX: TAS disable reason max value + */ +enum iwl_mvm_tas_statically_disabled_reason { +	TAS_DISABLED_DUE_TO_BIOS, +	TAS_DISABLED_DUE_TO_SAR_6DBM, +	TAS_DISABLED_REASON_INVALID, + +	TAS_DISABLED_REASON_MAX, +}; /*_TAS_STATICALLY_DISABLED_REASON_E*/ +  #endif /* __iwl_fw_api_debug_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 712532f17630..74f2efbad34e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -1,12 +1,14 @@  /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */  /* - * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2022 Intel Corporation   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH   * Copyright (C) 2016-2017 Intel Deutschland GmbH   */  #ifndef __iwl_fw_api_mac_cfg_h__  #define __iwl_fw_api_mac_cfg_h__ +#include "mac.h" +  /**   * enum iwl_mac_conf_subcmd_ids - mac configuration command IDs   */ @@ -31,7 +33,30 @@ enum iwl_mac_conf_subcmd_ids {  	 * @CANCEL_CHANNEL_SWITCH_CMD: &struct iwl_cancel_channel_switch_cmd  	 */  	CANCEL_CHANNEL_SWITCH_CMD = 0x6, - +	/** +	 * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd +	 */ +	MAC_CONFIG_CMD = 0x8, +	/** +	 * @LINK_CONFIG_CMD: &struct iwl_link_config_cmd +	 */ +	LINK_CONFIG_CMD = 0x9, +	/** +	 * @STA_CONFIG_CMD: &struct iwl_mvm_sta_cfg_cmd +	 */ +	STA_CONFIG_CMD = 0xA, +	/** +	 * @AUX_STA_CMD: &struct iwl_mvm_aux_sta_cmd +	 */ +	AUX_STA_CMD = 0xB, +	/** +	 * @STA_REMOVE_CMD: &struct iwl_mvm_remove_sta_cmd +	 */ +	STA_REMOVE_CMD = 0xC, +	/** +	 * @STA_DISABLE_TX_CMD: &struct iwl_mvm_sta_disable_tx_cmd +	 */ +	STA_DISABLE_TX_CMD = 0xD,  	/**  	 * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif  	 */ @@ -182,4 +207,393 @@ struct iwl_mac_low_latency_cmd {  	__le16 reserved;  } __packed; /* MAC_LOW_LATENCY_API_S_VER_1 */ +/** + * struct iwl_mac_client_data - configuration data for client MAC context + * + * @is_assoc: 1 for associated state, 0 otherwise + * @assoc_id: unique ID assigned by the AP during association + * @data_policy: see &enum iwl_mac_data_policy + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + *	0 indicates that there is no CT window. + */ +struct iwl_mac_client_data { +	__le32 is_assoc; +	__le32 assoc_id; +	__le32 data_policy; +	__le32 ctwin; +} __packed; /* MAC_CONTEXT_CONFIG_CLIENT_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_p2p_dev_data  - configuration data for P2P device MAC context + * + * @is_disc_extended: if set to true, P2P Device discoverability is enabled on + *	other channels as well. This should be to true only in case that the + *	device is discoverable and there is an active GO. Note that setting this + *	field when not needed, will increase the number of interrupts and have + *	effect on the platform power, as this setting opens the Rx filters on + *	all macs. + */ +struct iwl_mac_p2p_dev_data { +	__le32 is_disc_extended; +} __packed; /* MAC_CONTEXT_CONFIG_P2P_DEV_DATA_API_S_VER_1 */ + +/** + * enum iwl_mac_config_filter_flags - MAC context configuration filter flags + * + * @MAC_CFG_FILTER_PROMISC: accept all data frames + * @MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT: pass all management and + *	control frames to the host + * @MAC_CFG_FILTER_ACCEPT_GRP: accept multicast frames + * @MAC_CFG_FILTER_ACCEPT_BEACON: accept beacon frames + * @MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe response + * @MAC_CFG_FILTER_ACCEPT_PROBE_REQ: accept probe requests + */ +enum iwl_mac_config_filter_flags { +	MAC_CFG_FILTER_PROMISC			= BIT(0), +	MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT	= BIT(1), +	MAC_CFG_FILTER_ACCEPT_GRP		= BIT(2), +	MAC_CFG_FILTER_ACCEPT_BEACON		= BIT(3), +	MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP	= BIT(4), +	MAC_CFG_FILTER_ACCEPT_PROBE_REQ		= BIT(5), +}; /* MAC_FILTER_FLAGS_MASK_E_VER_1 */ + +/** + * struct iwl_mac_config_cmd - command structure to configure MAC contexts in + *	MLD API + * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) + * + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @mac_type: one of &enum iwl_mac_types + * @local_mld_addr: mld address + * @reserved_for_local_mld_addr: reserved + * @filter_flags: combination of &enum iwl_mac_config_filter_flags + * @he_support: does this MAC support HE + * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling + * @eht_support: does this MAC support EHT. Requires he_support + * @nic_not_ack_enabled: mark that the NIC doesn't support receiving + *	ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG). + *	If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0 + *	len delim to determine if AGG or single. + * @client: client mac data + * @go_ibss: mac data for go or ibss + * @p2p_dev: mac data for p2p device + */ +struct iwl_mac_config_cmd { +	/* COMMON_INDEX_HDR_API_S_VER_1 */ +	__le32 id_and_color; +	__le32 action; +	/* MAC_CONTEXT_TYPE_API_E */ +	__le32 mac_type; +	u8 local_mld_addr[6]; +	__le16 reserved_for_local_mld_addr; +	__le32 filter_flags; +	__le16 he_support; +	__le16 he_ap_support; +	__le32 eht_support; +	__le32 nic_not_ack_enabled; +	/* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_1 */ +	union { +		struct iwl_mac_client_data client; +		struct iwl_mac_p2p_dev_data p2p_dev; +	}; +} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + +/** + * enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being + *	modified in &iwl_link_ctx_cfg_cmd + * + * @LINK_CONTEXT_MODIFY_ACTIVE: covers iwl_link_ctx_cfg_cmd::active + * @LINK_CONTEXT_MODIFY_RATES_INFO: covers iwl_link_ctx_cfg_cmd::cck_rates, + *	iwl_link_ctx_cfg_cmd::ofdm_rates, + *	iwl_link_ctx_cfg_cmd::cck_short_preamble, + *	iwl_link_ctx_cfg_cmd::short_slot + * @LINK_CONTEXT_MODIFY_PROTECT_FLAGS: covers + *	iwl_link_ctx_cfg_cmd::protection_flags + * @LINK_CONTEXT_MODIFY_QOS_PARAMS: covers iwl_link_ctx_cfg_cmd::qos_flags, + *	iwl_link_ctx_cfg_cmd::ac, + * @LINK_CONTEXT_MODIFY_BEACON_TIMING: covers iwl_link_ctx_cfg_cmd::bi, + *	iwl_link_ctx_cfg_cmd::dtim_interval, + *	iwl_link_ctx_cfg_cmd::dtim_time, + *	iwl_link_ctx_cfg_cmd::dtim_tsf, + *	iwl_link_ctx_cfg_cmd::assoc_beacon_arrive_time. + *	This flag can be set only once after assoc. + * @LINK_CONTEXT_MODIFY_HE_PARAMS: covers + *	iwl_link_ctx_cfg_cmd::htc_trig_based_pkt_ext + *	iwl_link_ctx_cfg_cmd::rand_alloc_ecwmin, + *	iwl_link_ctx_cfg_cmd::rand_alloc_ecwmax, + *	iwl_link_ctx_cfg_cmd::trig_based_txf, + *	iwl_link_ctx_cfg_cmd::bss_color, + *	iwl_link_ctx_cfg_cmd::ndp_fdbk_buff_th_exp, + *	iwl_link_ctx_cfg_cmd::ref_bssid_addr + *	iwl_link_ctx_cfg_cmd::bssid_index, + *	iwl_link_ctx_cfg_cmd::frame_time_rts_th. + *	This flag can be set any time. + * @LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE: covers + *	iwl_link_ctx_cfg_cmd::bss_color_disable + * @LINK_CONTEXT_MODIFY_EHT_PARAMS: covers iwl_link_ctx_cfg_cmd::puncture_mask. + *	This flag can be set only if the MAC that this link relates to has + *	eht_support set to true. + * @LINK_CONTEXT_MODIFY_ALL: set all above flags + */ +enum iwl_link_ctx_modify_flags { +	LINK_CONTEXT_MODIFY_ACTIVE		= BIT(0), +	LINK_CONTEXT_MODIFY_RATES_INFO		= BIT(1), +	LINK_CONTEXT_MODIFY_PROTECT_FLAGS	= BIT(2), +	LINK_CONTEXT_MODIFY_QOS_PARAMS		= BIT(3), +	LINK_CONTEXT_MODIFY_BEACON_TIMING	= BIT(4), +	LINK_CONTEXT_MODIFY_HE_PARAMS		= BIT(5), +	LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE	= BIT(6), +	LINK_CONTEXT_MODIFY_EHT_PARAMS		= BIT(7), +	LINK_CONTEXT_MODIFY_ALL			= 0xff, +}; /* LINK_CONTEXT_MODIFY_MASK_E_VER_1 */ + +/** + * enum iwl_link_ctx_protection_flags - link protection flags + * @LINK_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, + *	this will require CCK RTS/CTS2self. + *	RTS/CTS will protect full burst time. + * @LINK_PROT_FLG_HT_PROT: enable HT protection + * @LINK_PROT_FLG_FAT_PROT: protect 40 MHz transmissions + * @LINK_PROT_FLG_SELF_CTS_EN: allow CTS2self + */ +enum iwl_link_ctx_protection_flags { +	LINK_PROT_FLG_TGG_PROTECT	= BIT(0), +	LINK_PROT_FLG_HT_PROT		= BIT(1), +	LINK_PROT_FLG_FAT_PROT		= BIT(2), +	LINK_PROT_FLG_SELF_CTS_EN	= BIT(3), +}; /* LINK_PROTECT_FLAGS_E_VER_1 */ + +/** + * enum iwl_link_ctx_flags - link context flags + * + * @LINK_FLG_BSS_COLOR_DIS: BSS color disable, don't use the BSS + *	color for RX filter but use MAC header + *	enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @LINK_FLG_MU_EDCA_CW: indicates that there is an element of MU EDCA + *	parameter set, i.e. the backoff counters for trig-based ACs + * @LINK_FLG_RU_2MHZ_BLOCK: indicates that 26-tone RU OFDMA transmission are + *      not allowed (as there are OBSS that might classify such transmissions as + *      radar pulses). + * @LINK_FLG_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change + *	of threshold + */ +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_CONTEXT_FLAG_E_VER_1 */ + +/** + * struct iwl_link_config_cmd - command structure to configure the LINK context + *	in MLD API + * ( LINK_CONFIG_CMD =0x9 ) + * + * @action: action to perform, one of FW_CTXT_ACTION_* + * @link_id: the id of the link that this cmd configures + * @mac_id: interface ID. Relevant only if action is FW_CTXT_ACTION_ADD + * @phy_id: PHY index. Can be changed only if the link was inactive + *	(and stays inactive). If the link is active (or becomes active), + *	this field is ignored. + * @local_link_addr: the links MAC address. Can be changed only if the link was + *	inactive (and stays inactive). If the link is active + *	(or becomes active), this field is ignored. + * @reserved_for_local_link_addr: reserved + * @modify_mask: from &enum iwl_link_ctx_modify_flags, selects what to change. + *	Relevant only if action is FW_CTXT_ACTION_MODIFY + * @active: indicates whether the link is active or not + * @listen_lmac: indicates whether the link should be allocated on the Listen + *	Lmac or on the Main Lmac. Cannot be changed on an active Link. + *	Relevant only for eSR. + * @cck_rates: basic rates available for CCK + * @ofdm_rates: basic rates available for OFDM + * @cck_short_preamble: 1 for enabling short preamble, 0 otherwise + * @short_slot: 1 for enabling short slots, 0 otherwise + * @protection_flags: combination of &enum iwl_link_ctx_protection_flags + * @qos_flags: from &enum iwl_mac_qos_flags + * @ac: one iwl_mac_qos configuration for each AC + * @htc_trig_based_pkt_ext: default PE in 4us units + * @rand_alloc_ecwmin: random CWmin = 2**ECWmin-1 + * @rand_alloc_ecwmax: random CWmax = 2**ECWmax-1 + * @ndp_fdbk_buff_th_exp: set exponent for the NDP feedback buffered threshold + * @trig_based_txf: MU EDCA Parameter set for the trigger based traffic queues + * @bi: beacon interval in TU, applicable only when associated + * @dtim_interval: DTIM interval in TU. + *	Relevant only for GO, otherwise this is offloaded. + * @puncture_mask: puncture mask for EHT + * @frame_time_rts_th: HE duration RTS threshold, in units of 32us + * @flags: a combination from &enum iwl_link_ctx_flags + * @flags_mask: what of %flags have changed. Also &enum iwl_link_ctx_flags + * Below fields are for multi-bssid: + * @ref_bssid_addr: reference BSSID used by the AP + * @reserved_for_ref_bssid_addr: reserved + * @bssid_index: index of the associated VAP + * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame + * @reserved: alignment + * @ibss_bssid_addr: bssid for ibss + * @reserved_for_ibss_bssid_addr: reserved + * @reserved1: reserved for future use + */ +struct iwl_link_config_cmd { +	__le32 action; +	__le32 link_id; +	__le32 mac_id; +	__le32 phy_id; +	u8 local_link_addr[6]; +	__le16 reserved_for_local_link_addr; +	__le32 modify_mask; +	__le32 active; +	__le32 listen_lmac; +	__le32 cck_rates; +	__le32 ofdm_rates; +	__le32 cck_short_preamble; +	__le32 short_slot; +	__le32 protection_flags; +	/* MAC_QOS_PARAM_API_S_VER_1 */ +	__le32 qos_flags; +	struct iwl_ac_qos ac[AC_NUM + 1]; +	u8 htc_trig_based_pkt_ext; +	u8 rand_alloc_ecwmin; +	u8 rand_alloc_ecwmax; +	u8 ndp_fdbk_buff_th_exp; +	struct iwl_he_backoff_conf trig_based_txf[AC_NUM]; +	__le32 bi; +	__le32 dtim_interval; +	__le16 puncture_mask; +	__le16 frame_time_rts_th; +	__le32 flags; +	__le32 flags_mask; +	/* The below fields are for multi-bssid */ +	u8 ref_bssid_addr[6]; +	__le16 reserved_for_ref_bssid_addr; +	u8 bssid_index; +	u8 bss_color; +	u8 reserved[2]; +	u8 ibss_bssid_addr[6]; +	__le16 reserved_for_ibss_bssid_addr; +	__le32 reserved1[8]; +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + +/* Currently FW supports link ids in the range 0-3 and can have + * at most two active links for each vif. + */ +#define IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM 2 +#define IWL_MVM_FW_MAX_LINK_ID 3 + +/** + * enum iwl_fw_sta_type - FW station types + * @STATION_TYPE_PEER: represents a peer - AP in BSS, a TDLS sta, a client in + *	P2P. + * @STATION_TYPE_BCAST_MGMT: The station used to send beacons and + *	probe responses. Also used for traffic injection in sniffer mode + * @STATION_TYPE_MCAST: the station used for BCAST / MCAST in GO. Will be + *	suspended / resumed at the right timing depending on the clients' + *	power save state and the DTIM timing + * @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. + */ +enum iwl_fw_sta_type { +	STATION_TYPE_PEER, +	STATION_TYPE_BCAST_MGMT, +	STATION_TYPE_MCAST, +	STATION_TYPE_AUX, +}; /* STATION_TYPE_E_VER_1 */ + +/** + * struct iwl_mvm_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's + *	station table + * ( STA_CONFIG_CMD = 0xA ) + * + * @sta_id: index of station in uCode's station table + * @link_id: the id of the link that is used to communicate with this sta + * @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 + *	with this sta + * @reserved_for_peer_link_address: reserved + * @station_type: type of this station. See &enum iwl_fw_sta_type + * @assoc_id: for GO only + * @beamform_flags: beam forming controls + * @mfp: indicates whether the STA uses management frame protection or not. + * @mimo: indicates whether the sta uses mimo or not + * @mimo_protection: indicates whether the sta uses mimo protection or not + * @ack_enabled: indicates that the AP supports receiving ACK- + *	enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @trig_rnd_alloc: indicates that trigger based random allocation + *	is enabled according to UORA element existence + * @tx_ampdu_spacing: minimum A-MPDU spacing: + *	4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density + * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K, + *	3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K. + * @sp_length: the size of the SP in actual number of frames + * @uapsd_acs:  4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + *	enabled ACs. + * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY + *	capa + * @htc_flags: which features are supported in HTC + */ +struct iwl_mvm_sta_cfg_cmd { +	__le32 sta_id; +	__le32 link_id; +	u8 peer_mld_address[ETH_ALEN]; +	__le16 reserved_for_peer_mld_address; +	u8 peer_link_address[ETH_ALEN]; +	__le16 reserved_for_peer_link_address; +	__le32 station_type; +	__le32 assoc_id; +	__le32 beamform_flags; +	__le32 mfp; +	__le32 mimo; +	__le32 mimo_protection; +	__le32 ack_enabled; +	__le32 trig_rnd_alloc; +	__le32 tx_ampdu_spacing; +	__le32 tx_ampdu_max_size; +	__le32 sp_length; +	__le32 uapsd_acs; +	struct iwl_he_pkt_ext_v2 pkt_ext; +	__le32 htc_flags; +} __packed; /* STA_CMD_API_S_VER_1 */ + +/** + * struct iwl_mvm_aux_sta_cmd - command for AUX STA configuration + * ( AUX_STA_CMD = 0xB ) + * + * @sta_id: index of aux sta to configure + * @lmac_id: ? + * @mac_addr: mac addr of the auxilary sta + * @reserved_for_mac_addr: reserved + */ +struct iwl_mvm_aux_sta_cmd { +	__le32 sta_id; +	__le32 lmac_id; +	u8 mac_addr[ETH_ALEN]; +	__le16 reserved_for_mac_addr; + +} __packed; /* AUX_STA_CMD_API_S_VER_1 */ + +/** + * struct iwl_mvm_remove_sta_cmd - a cmd structure to remove a sta added by + *	STA_CONFIG_CMD or AUX_STA_CONFIG_CMD + * ( STA_REMOVE_CMD = 0xC ) + * + * @sta_id: index of station to remove + */ +struct iwl_mvm_remove_sta_cmd { +	__le32 sta_id; +} __packed; /* REMOVE_STA_API_S_VER_1 */ + +/** + * struct iwl_mvm_sta_disable_tx_cmd - disable / re-enable tx to a sta + * ( STA_DISABLE_TX_CMD = 0xD ) + * + * @sta_id: index of the station to disable tx to + * @disable: indicates if to disable or re-enable tx + */ +struct iwl_mvm_sta_disable_tx_cmd { +	__le32 sta_id; +	__le32 disable; +} __packed; /* STA_DISABLE_TX_API_S_VER_1 */ +  #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index ddacd5b45aea..c9a48fc5fac8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -373,9 +373,6 @@ enum {  /* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */  #define RATE_VHT_MCS_RATE_CODE_MSK	0xf -#define RATE_VHT_MCS_NSS_POS		4 -#define RATE_VHT_MCS_NSS_MSK		(3 << RATE_VHT_MCS_NSS_POS) -#define RATE_VHT_MCS_MIMO2_MSK		BIT(RATE_VHT_MCS_NSS_POS)  /*   * Legacy OFDM rate format for bits 7:0 @@ -449,11 +446,16 @@ enum {   *	1			2xLTF+0.8us   *	2			2xLTF+1.6us   *	3			4xLTF+3.2us - * HE TRIG: + * HE-EHT TRIG:   *	0			1xLTF+1.6us   *	1			2xLTF+1.6us   *	2			4xLTF+3.2us   *	3			(does not occur) + * EHT MU: + *	0			2xLTF+0.8us + *	1			2xLTF+1.6us + *	2			4xLTF+0.8us + *	3			4xLTF+3.2us   */  #define RATE_MCS_HE_GI_LTF_POS		20  #define RATE_MCS_HE_GI_LTF_MSK_V1		(3 << RATE_MCS_HE_GI_LTF_POS) @@ -546,12 +548,17 @@ enum {  /*   * Bits 13-11: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz, (4) 320MHz   */ -#define RATE_MCS_CHAN_WIDTH_MSK			(0x7 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_20			(0 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_40			(1 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_80			(2 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_160			(3 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_320			(4 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_MSK		(0x7 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_20_VAL	0 +#define RATE_MCS_CHAN_WIDTH_20		(RATE_MCS_CHAN_WIDTH_20_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_40_VAL	1 +#define RATE_MCS_CHAN_WIDTH_40		(RATE_MCS_CHAN_WIDTH_40_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_80_VAL	2 +#define RATE_MCS_CHAN_WIDTH_80		(RATE_MCS_CHAN_WIDTH_80_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_160_VAL	3 +#define RATE_MCS_CHAN_WIDTH_160		(RATE_MCS_CHAN_WIDTH_160_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_320_VAL	4 +#define RATE_MCS_CHAN_WIDTH_320		(RATE_MCS_CHAN_WIDTH_320_VAL << RATE_MCS_CHAN_WIDTH_POS)  /* Bit 15-14: Antenna selection:   * Bit 14: Ant A active diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 1c4e84932058..fdd8b01f09e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -367,7 +367,8 @@ enum iwl_rx_phy_eht_data1 {  	/* number of EHT-LTF symbols 0 - 1 EHT-LTF, 1 - 2 EHT-LTFs, 2 - 4 EHT-LTFs,  	 * 3 - 6 EHT-LTFs, 4 - 8 EHT-LTFs */  	IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM		= 0x000000e0, -	IWL_RX_PHY_DATA1_EHT_RU_ALLOC			= 0x0000ff00, +	IWL_RX_PHY_DATA1_EHT_B0				= 0x00000100, +	IWL_RX_PHY_DATA1_EHT_RU_B1_B7_ALLOC		= 0x0000fe00,  };  /* goes into Metadata DW 7 */ @@ -413,7 +414,7 @@ enum iwl_rx_phy_eht_data2 {  	/* OFDM_RX_VECTOR_COMMON_RU_ALLOC_0_OUT */  	IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A1	= 0x000001ff,  	IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A2	= 0x0003fe00, -	IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A3	= 0x01fc0000, +	IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_B1	= 0x07fc0000,  	/* info type: EHT-TB-EXT */  	IWL_RX_PHY_DATA2_EHT_TB_EXT_TRIG_SIGA1	= 0xffffffff, @@ -423,19 +424,18 @@ enum iwl_rx_phy_eht_data2 {  enum iwl_rx_phy_eht_data3 {  	/* info type: EHT-MU-EXT */  	/* OFDM_RX_VECTOR_COMMON_RU_ALLOC_1_OUT */ -	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B1	= 0x000001ff, -	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B2	= 0x0003fe00, -	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B3	= 0x01fc0000, +	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B2	= 0x000001ff, +	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_C1	= 0x0003fe00, +	IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_C2	= 0x07fc0000,  };  /* goes into Metadata DW 4 */  enum iwl_rx_phy_eht_data4 {  	/* info type: EHT-MU-EXT */  	/* OFDM_RX_VECTOR_COMMON_RU_ALLOC_2_OUT */ -	IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C1	= 0x000001ff, -	IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C2	= 0x0003fe00, -	IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C3	= 0x01fc0000, -	IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS	= 0x18000000, +	IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_D1	= 0x000001ff, +	IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_D2	= 0x0003fe00, +	IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS	= 0x000c0000,  };  /* goes into Metadata DW 16 */ @@ -673,22 +673,31 @@ struct iwl_rx_mpdu_desc {  	 * @mac_phy_idx: MAC/PHY index  	 */  	u8 mac_phy_idx; -	/* DW4 - carries csum data only when rpa_en == 1 */ -	/** -	 * @raw_csum: raw checksum (alledgedly unreliable) -	 */ -	__le16 raw_csum; - +	/* DW4 */  	union { +		struct { +			/* carries csum data only when rpa_en == 1 */ +			/** +			 * @raw_csum: raw checksum (alledgedly unreliable) +			 */ +			__le16 raw_csum; + +			union { +				/** +				 * @l3l4_flags: &enum iwl_rx_l3l4_flags +				 */ +				__le16 l3l4_flags; + +				/** +				 * @phy_data4: depends on info type, see phy_data1 +				 */ +				__le16 phy_data4; +			}; +		};  		/** -		 * @l3l4_flags: &enum iwl_rx_l3l4_flags -		 */ -		__le16 l3l4_flags; - -		/** -		 * @phy_data4: depends on info type, see phy_data1 +		 * @phy_eht_data4: depends on info type, see phy_data1  		 */ -		__le16 phy_data4; +		__le32 phy_eht_data4;  	};  	/* DW5 */  	/** @@ -725,7 +734,7 @@ struct iwl_rx_mpdu_desc {  #define RX_NO_DATA_INFO_TYPE_RX_ERR	1  #define RX_NO_DATA_INFO_TYPE_NDP	2  #define RX_NO_DATA_INFO_TYPE_MU_UNMATCHED	3 -#define RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED	4 +#define RX_NO_DATA_INFO_TYPE_TB_UNMATCHED	4  #define RX_NO_DATA_INFO_ERR_POS		8  #define RX_NO_DATA_INFO_ERR_MSK		(0xff << RX_NO_DATA_INFO_ERR_POS) @@ -743,6 +752,35 @@ struct iwl_rx_mpdu_desc {  #define RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK	0x38000000  #define RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK	0x00f00000 +/* content of OFDM_RX_VECTOR_USIG_A1_OUT */ +enum iwl_rx_usig_a1 { +	IWL_RX_USIG_A1_ENHANCED_WIFI_VER_ID	= 0x00000007, +	IWL_RX_USIG_A1_BANDWIDTH		= 0x00000038, +	IWL_RX_USIG_A1_UL_FLAG			= 0x00000040, +	IWL_RX_USIG_A1_BSS_COLOR		= 0x00001f80, +	IWL_RX_USIG_A1_TXOP_DURATION		= 0x000fe000, +	IWL_RX_USIG_A1_DISREGARD		= 0x01f00000, +	IWL_RX_USIG_A1_VALIDATE			= 0x02000000, +	IWL_RX_USIG_A1_EHT_BW320_SLOT		= 0x04000000, +	IWL_RX_USIG_A1_EHT_TYPE			= 0x18000000, +	IWL_RX_USIG_A1_RDY			= 0x80000000, +}; + +/* content of OFDM_RX_VECTOR_USIG_A2_EHT_OUT */ +enum iwl_rx_usig_a2_eht { +	IWL_RX_USIG_A2_EHT_PPDU_TYPE		= 0x00000003, +	IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2	= 0x00000004, +	IWL_RX_USIG_A2_EHT_PUNC_CHANNEL		= 0x000000f8, +	IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8	= 0x00000100, +	IWL_RX_USIG_A2_EHT_SIG_MCS		= 0x00000600, +	IWL_RX_USIG_A2_EHT_SIG_SYM_NUM		= 0x0000f800, +	IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1 = 0x000f0000, +	IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2 = 0x00f00000, +	IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD	= 0x1f000000, +	IWL_RX_USIG_A2_EHT_CRC_OK		= 0x40000000, +	IWL_RX_USIG_A2_EHT_RDY			= 0x80000000, +}; +  /**   * struct iwl_rx_no_data - RX no data descriptor   * @info: 7:0 frame type, 15:8 RX error type @@ -780,7 +818,7 @@ struct iwl_rx_no_data {   * @rx_vec: DW-12:9 raw RX vectors from DSP according to modulation type.   *	for VHT: OFDM_RX_VECTOR_SIGA1_OUT, OFDM_RX_VECTOR_SIGA2_OUT   *	for HE: OFDM_RX_VECTOR_HE_SIGA1_OUT, OFDM_RX_VECTOR_HE_SIGA2_OUT - *	for EHT: OFDM_RX_VECTOR_USIG_A1_OUT, OFDM_RX_VECTOR_USIG_A2_OUT, + *	for EHT: OFDM_RX_VECTOR_USIG_A1_OUT, OFDM_RX_VECTOR_USIG_A2_EHT_OUT,   *	OFDM_RX_VECTOR_EHT_OUT, OFDM_RX_VECTOR_EHT_USER_FIELD_OUT   */  struct iwl_rx_no_data_ver_3 { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 7ba0e3409199..ec96ba053a5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -709,10 +709,13 @@ enum iwl_umac_scan_general_flags_v2 {   *     should be aware of a P2P GO operation on the 2GHz band.   * @IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB: scan event scheduling   *     should be aware of a P2P GO operation on the 5GHz or 6GHz band. + * @IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT: don't toggle between + *     valid antennas, and use the same antenna as in previous scan   */  enum iwl_umac_scan_general_params_flags2 {  	IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB = BIT(0),  	IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB = BIT(1), +	IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT   = BIT(2),  };  /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index ecc6706f66ed..97edf5477ba7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -800,7 +800,7 @@ enum iwl_mac_beacon_flags {   *	is &enum iwl_mac_beacon_flags.   * @short_ssid: Short SSID   * @reserved: reserved - * @template_id: currently equal to the mac context id of the coresponding mac. + * @link_id: the firmware id of the link that will use this beacon   * @tim_idx: the offset of the tim IE in the beacon   * @tim_size: the length of the tim IE   * @ecsa_offset: offset to the ECSA IE if present @@ -812,15 +812,17 @@ struct iwl_mac_beacon_cmd {  	__le16 flags;  	__le32 short_ssid;  	__le32 reserved; -	__le32 template_id; +	__le32 link_id;  	__le32 tim_idx;  	__le32 tim_size;  	__le32 ecsa_offset;  	__le32 csa_offset;  	struct ieee80211_hdr frame[];  } __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_10, -	       BEACON_TEMPLATE_CMD_API_S_VER_11, -	       BEACON_TEMPLATE_CMD_API_S_VER_12 */ +	     * BEACON_TEMPLATE_CMD_API_S_VER_11, +	     * BEACON_TEMPLATE_CMD_API_S_VER_12, +	     * BEACON_TEMPLATE_CMD_API_S_VER_13 +	     */  struct iwl_beacon_notif {  	struct iwl_mvm_tx_resp beacon_notify_hdr; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index abf49022edbe..55219974b92b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1038,7 +1038,7 @@ iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt,  	range->range_data_size = reg->dev_addr.size;  	for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) {  		prph_val = iwl_read_prph(fwrt->trans, addr + i); -		if (prph_val == 0x5a5a5a5a) +		if ((prph_val & ~0xf) == 0xa5a5a5a0)  			return -EBUSY;  		*val++ = cpu_to_le32(prph_val);  	} @@ -1388,13 +1388,13 @@ static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt,  	if (!data)  		return; +	memset(data, 0, sizeof(*data)); +  	/* make sure only one bit is set in only one fid */  	if (WARN_ONCE(hweight_long(fid1) + hweight_long(fid2) != 1,  		      "fid1=%x, fid2=%x\n", fid1, fid2))  		return; -	memset(data, 0, sizeof(*data)); -  	if (fid1) {  		fifo_idx = ffs(fid1) - 1;  		if (WARN_ONCE(fifo_idx >= MAX_NUM_LMAC, "fifo_idx=%d\n", @@ -1562,7 +1562,7 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt,  		prph_data = iwl_read_prph_no_grab(fwrt->trans, (i % 2) ?  					  DBGI_SRAM_TARGET_ACCESS_RDATA_MSB :  					  DBGI_SRAM_TARGET_ACCESS_RDATA_LSB); -		if (prph_data == 0x5a5a5a5a) { +		if ((prph_data & ~0xf) == 0xa5a5a5a0) {  			iwl_trans_release_nic_access(fwrt->trans);  			return -EBUSY;  		} @@ -1664,14 +1664,10 @@ static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id,  }  static void * -iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, -			     struct iwl_dump_ini_region_data *reg_data, +iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id,  			     struct iwl_fw_ini_monitor_dump *data,  			     const struct iwl_fw_mon_regs *addrs)  { -	struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; -	u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); -  	if (!iwl_trans_grab_nic_access(fwrt->trans)) {  		IWL_ERR(fwrt, "Failed to get monitor header\n");  		return NULL; @@ -1702,8 +1698,10 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt,  				  void *data, u32 data_len)  {  	struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; +	struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; +	u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); -	return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, +	return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,  					    &fwrt->trans->cfg->mon_dram_regs);  } @@ -1713,8 +1711,10 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt,  				  void *data, u32 data_len)  {  	struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; +	struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; +	u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id); -	return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, +	return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,  					    &fwrt->trans->cfg->mon_smem_regs);  } @@ -1725,7 +1725,10 @@ iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt,  {  	struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; -	return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, +	return iwl_dump_ini_mon_fill_header(fwrt, +					    /* no offset calculation later */ +					    IWL_FW_INI_ALLOCATION_ID_DBGC1, +					    mon_dump,  					    &fwrt->trans->cfg->mon_dbgi_regs);  } @@ -2320,6 +2323,36 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,  	return entry->size;  } +static u32 iwl_dump_ini_file_name_info(struct iwl_fw_runtime *fwrt, +				       struct list_head *list) +{ +	struct iwl_fw_ini_dump_entry *entry; +	struct iwl_dump_file_name_info *tlv; +	u32 len = strnlen(fwrt->trans->dbg.dump_file_name_ext, +			  IWL_FW_INI_MAX_NAME); + +	if (!fwrt->trans->dbg.dump_file_name_ext_valid) +		return 0; + +	entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + len); +	if (!entry) +		return 0; + +	entry->size = sizeof(*tlv) + len; + +	tlv = (void *)entry->data; +	tlv->type = cpu_to_le32(IWL_INI_DUMP_NAME_TYPE); +	tlv->len = cpu_to_le32(len); +	memcpy(tlv->data, fwrt->trans->dbg.dump_file_name_ext, len); + +	/* add the dump file name extension tlv to the list */ +	list_add_tail(&entry->list, list); + +	fwrt->trans->dbg.dump_file_name_ext_valid = false; + +	return entry->size; +} +  static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = {  	[IWL_FW_INI_REGION_INVALID] = {},  	[IWL_FW_INI_REGION_INTERNAL_BUFFER] = { @@ -2495,8 +2528,10 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,  		size += iwl_dump_ini_mem(fwrt, list, ®_data,  					 &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); -	if (size) +	if (size) { +		size += iwl_dump_ini_file_name_info(fwrt, list);  		size += iwl_dump_ini_info(fwrt, trigger, list); +	}  	return size;  } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index 43e997283db0..607e07ed2477 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -317,8 +317,10 @@ static void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq,  	const struct iwl_fw *fw = priv->fwrt->fw;  	*pos = ++state->pos; -	if (*pos >= fw->ucode_capa.n_cmd_versions) +	if (*pos >= fw->ucode_capa.n_cmd_versions) { +		kfree(state);  		return NULL; +	}  	return state;  } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index 792f7fee1840..f86f7b4baa18 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -14,6 +14,13 @@  #include "iwl-csr.h"  #include "pnvm.h" +#define FW_ASSERT_LMAC_FATAL			0x70 +#define FW_ASSERT_LMAC2_FATAL			0x72 +#define FW_ASSERT_UMAC_FATAL			0x71 +#define UMAC_RT_NMI_LMAC2_FATAL			0x72 +#define RT_NMI_INTERRUPT_OTHER_LMAC_FATAL	0x73 +#define FW_ASSERT_NMI_UNKNOWN			0x84 +  /*   * Note: This structure is read from the device with IO accesses,   * and the reading already does the endian conversion. As it is @@ -96,6 +103,17 @@ struct iwl_umac_error_event_table {  #define ERROR_START_OFFSET  (1 * sizeof(u32))  #define ERROR_ELEM_SIZE     (7 * sizeof(u32)) +static bool iwl_fwrt_if_errorid_other_cpu(u32 err_id) +{ +	err_id &= 0xFF; + +	if ((err_id >= FW_ASSERT_LMAC_FATAL && +	     err_id <= RT_NMI_INTERRUPT_OTHER_LMAC_FATAL) || +	    err_id == FW_ASSERT_NMI_UNKNOWN) +		return  true; +	return false; +} +  static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)  {  	struct iwl_trans *trans = fwrt->trans; @@ -113,6 +131,13 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)  	if (table.valid)  		fwrt->dump.umac_err_id = table.error_id; +	if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.umac_err_id) && +	    !fwrt->trans->dbg.dump_file_name_ext_valid) { +		fwrt->trans->dbg.dump_file_name_ext_valid = true; +		snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, +			 "0x%x", fwrt->dump.umac_err_id); +	} +  	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {  		IWL_ERR(trans, "Start IWL Error Log Dump:\n");  		IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", @@ -189,6 +214,13 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu  	if (table.valid)  		fwrt->dump.lmac_err_id[lmac_num] = table.error_id; +	if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.lmac_err_id[lmac_num]) && +	    !fwrt->trans->dbg.dump_file_name_ext_valid) { +		fwrt->trans->dbg.dump_file_name_ext_valid = true; +		snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, +			 "0x%x", fwrt->dump.lmac_err_id[lmac_num]); +	} +  	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {  		IWL_ERR(trans, "Start IWL Error Log Dump:\n");  		IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", @@ -274,6 +306,16 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)  	iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); +	if (table.valid) +		fwrt->dump.tcm_err_id[idx] = table.error_id; + +	if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.tcm_err_id[idx]) && +	    !fwrt->trans->dbg.dump_file_name_ext_valid) { +		fwrt->trans->dbg.dump_file_name_ext_valid = true; +		snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, +			 "0x%x", fwrt->dump.tcm_err_id[idx]); +	} +  	IWL_ERR(fwrt, "TCM%d status:\n", idx + 1);  	IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);  	IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2); @@ -337,6 +379,16 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx)  	iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); +	if (table.valid) +		fwrt->dump.rcm_err_id[idx] = table.error_id; + +	if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.rcm_err_id[idx]) && +	    !fwrt->trans->dbg.dump_file_name_ext_valid) { +		fwrt->trans->dbg.dump_file_name_ext_valid = true; +		snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, +			 "0x%x", fwrt->dump.rcm_err_id[idx]); +	} +  	IWL_ERR(fwrt, "RCM%d status:\n", idx + 1);  	IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);  	IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2); @@ -432,6 +484,9 @@ static void iwl_fwrt_dump_fseq_regs(struct iwl_fw_runtime *fwrt)  void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt)  { +	struct iwl_pc_data *pc_data; +	u8 count; +  	if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) {  		IWL_ERR(fwrt,  			"DEVICE_ENABLED bit is not set. Aborting dump.\n"); @@ -444,10 +499,20 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt)  	iwl_fwrt_dump_umac_error_log(fwrt);  	iwl_fwrt_dump_tcm_error_log(fwrt, 0);  	iwl_fwrt_dump_rcm_error_log(fwrt, 0); -	iwl_fwrt_dump_tcm_error_log(fwrt, 1); -	iwl_fwrt_dump_rcm_error_log(fwrt, 1); +	if (fwrt->trans->dbg.tcm_error_event_table[1]) +		iwl_fwrt_dump_tcm_error_log(fwrt, 1); +	if (fwrt->trans->dbg.rcm_error_event_table[1]) +		iwl_fwrt_dump_rcm_error_log(fwrt, 1);  	iwl_fwrt_dump_iml_error_log(fwrt);  	iwl_fwrt_dump_fseq_regs(fwrt); +	if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { +		pc_data = fwrt->trans->dbg.pc_data; +		for (count = 0; count < fwrt->trans->dbg.num_pc; +		     count++, pc_data++) +			IWL_ERR(fwrt, "%s: 0x%x\n", +				pc_data->pc_name, +				pc_data->pc_address); +	}  	if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {  		u32 scratch = iwl_read32(fwrt->trans, CSR_FUNC_SCRATCH); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index c62576e442bd..f5e08988dc7b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@  /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */  /* - * Copyright (C) 2014, 2018-2021 Intel Corporation + * Copyright (C) 2014, 2018-2022 Intel Corporation   * Copyright (C) 2014-2015 Intel Mobile Communications GmbH   * Copyright (C) 2016-2017 Intel Deutschland GmbH   */ @@ -76,6 +76,18 @@ struct iwl_fw_error_dump_data {  } __packed;  /** + * struct iwl_dump_file_name_info - data for dump file name addition + * @type: region type with reserved bits + * @len: the length of file name string to be added to dump file + * @data: the string need to be added to dump file + */ +struct iwl_dump_file_name_info { +	__le32 type; +	__le32 len; +	__u8 data[]; +} __packed; + +/**   * struct iwl_fw_error_dump_file - the layout of the header of the file   * @barker: must be %IWL_FW_ERROR_DUMP_BARKER   * @file_len: the length of all the file starting from %barker @@ -231,6 +243,9 @@ struct iwl_fw_error_dump_mem {  /* Use bit 31 as dump info type to avoid colliding with region types */  #define IWL_INI_DUMP_INFO_TYPE BIT(31) +/* Use bit 31 and bit 24 as dump name type to avoid colliding with region types */ +#define IWL_INI_DUMP_NAME_TYPE (BIT(31) | BIT(24)) +  /**   * struct iwl_fw_error_dump_data - data for one type   * @type: &enum iwl_fw_ini_region_type diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index a7817d952022..cddf09d6be1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -101,8 +101,10 @@ enum iwl_ucode_tlv_type {  	IWL_UCODE_TLV_SEC_TABLE_ADDR		= 66,  	IWL_UCODE_TLV_D3_KEK_KCK_ADDR		= 67, +	IWL_UCODE_TLV_CURRENT_PC		= 68,  	IWL_UCODE_TLV_FW_NUM_STATIONS		= IWL_UCODE_TLV_CONST_BASE + 0, +	IWL_UCODE_TLV_FW_NUM_BEACONS		= IWL_UCODE_TLV_CONST_BASE + 2,  	IWL_UCODE_TLV_TYPE_DEBUG_INFO		= IWL_UCODE_TLV_DEBUG_BASE + 0,  	IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION	= IWL_UCODE_TLV_DEBUG_BASE + 1, @@ -455,6 +457,11 @@ enum iwl_ucode_tlv_capa {  	IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT		= (__force iwl_ucode_tlv_capa_t)100,  	IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT		= (__force iwl_ucode_tlv_capa_t)104,  	IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT	= (__force iwl_ucode_tlv_capa_t)105, +	IWL_UCODE_TLV_CAPA_SYNCED_TIME			= (__force iwl_ucode_tlv_capa_t)106, +	IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM        = (__force iwl_ucode_tlv_capa_t)108, +	IWL_UCODE_TLV_CAPA_BIGTK_TX_SUPPORT		= (__force iwl_ucode_tlv_capa_t)109, +	IWL_UCODE_TLV_CAPA_MLD_API_SUPPORT		= (__force iwl_ucode_tlv_capa_t)110, +	IWL_UCODE_TLV_CAPA_SCAN_DONT_TOGGLE_ANT         = (__force iwl_ucode_tlv_capa_t)111,  #ifdef __CHECKER__  	/* sparse says it cannot increment the previous enum member */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index f878ac508801..8d0d58d61892 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -51,6 +51,7 @@ struct iwl_ucode_capabilities {  	u32 error_log_addr;  	u32 error_log_size;  	u32 num_stations; +	u32 num_beacons;  	unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];  	unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; @@ -182,10 +183,10 @@ struct iwl_dump_exclude {   * @enhance_sensitivity_table: device can do enhanced sensitivity.   * @init_evtlog_ptr: event log offset for init ucode.   * @init_evtlog_size: event log size for init ucode. - * @init_errlog_ptr: error log offfset for init ucode. + * @init_errlog_ptr: error log offset for init ucode.   * @inst_evtlog_ptr: event log offset for runtime ucode.   * @inst_evtlog_size: event log size for runtime ucode. - * @inst_errlog_ptr: error log offfset for runtime ucode. + * @inst_errlog_ptr: error log offset for runtime ucode.   * @type: firmware type (&enum iwl_fw_type)   * @human_readable: human readable version   *	we get the ALIVE from the uCode diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index b6d3ac6ed440..c6f2672fdc73 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright(c) 2020-2021 Intel Corporation + * Copyright(c) 2020-2022 Intel Corporation   */  #include "iwl-drv.h" @@ -318,7 +318,6 @@ parse:  	kfree(data);  skip_parse: -	data = NULL;  	/* now try to get the reduce power table, if not loaded yet */  	if (!trans->reduce_power_loaded) {  		data = iwl_uefi_get_reduced_power(trans, &len); @@ -329,19 +328,16 @@ skip_parse:  			 * trying again over and over.  			 */  			trans->reduce_power_loaded = true; - -			goto skip_reduce_power; +		} else { +			ret = iwl_trans_set_reduce_power(trans, data, len); +			if (ret) +				IWL_DEBUG_FW(trans, +					     "Failed to set reduce power table %d\n", +					     ret); +			kfree(data);  		}  	} -	ret = iwl_trans_set_reduce_power(trans, data, len); -	if (ret) -		IWL_DEBUG_FW(trans, -			     "Failed to set reduce power table %d\n", -			     ret); -	kfree(data); - -skip_reduce_power:  	iwl_init_notification_wait(notif_wait, &pnvm_wait,  				   ntf_cmds, ARRAY_SIZE(ntf_cmds),  				   iwl_pnvm_complete_fn, trans); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index e128d2e07f38..b09e68dbf5a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2021 Intel Corporation + * Copyright (C) 2021-2022 Intel Corporation   */  #include <net/mac80211.h> @@ -126,7 +126,7 @@ u32 iwl_new_rate_from_v1(u32 rate_v1)  		   rate_v1 & RATE_MCS_HE_MSK_V1) {  		rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; -		rate_v2 |= rate_v1 & RATE_VHT_MCS_MIMO2_MSK; +		rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK;  		if (rate_v1 & RATE_MCS_HE_MSK_V1) {  			u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index d3cb1ae68a96..df689a9b7e2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -24,6 +24,8 @@ struct iwl_fw_runtime_ops {  };  #define MAX_NUM_LMAC 2 +#define MAX_NUM_TCM 2 +#define MAX_NUM_RCM 2  struct iwl_fwrt_shared_mem_cfg {  	int num_lmacs;  	int num_txfifo_entries; @@ -129,6 +131,8 @@ struct iwl_fw_runtime {  		unsigned long non_collect_ts_start[IWL_FW_INI_TIME_POINT_NUM];  		u32 *d3_debug_data;  		u32 lmac_err_id[MAX_NUM_LMAC]; +		u32 tcm_err_id[MAX_NUM_TCM]; +		u32 rcm_err_id[MAX_NUM_RCM];  		u32 umac_err_id;  		struct iwl_txf_iter_data txf_iter_data; @@ -161,6 +165,7 @@ struct iwl_fw_runtime {  	struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];  	u32 ppag_flags;  	u32 ppag_ver; +	bool ppag_table_valid;  	struct iwl_sar_offset_mapping_cmd sgom_table;  	bool sgom_enabled;  	u8 reduced_power_flags; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 0b6f694cf30d..01afea33c38c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -222,7 +222,7 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)  		return ERR_PTR(-ENOMEM);  	status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID, -				  NULL, &package_size, data); +				  NULL, &package_size, package);  	if (status != EFI_SUCCESS) {  		IWL_DEBUG_FW(trans,  			     "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index cfa5e1b3c3f6..411b7d4fcc9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -469,6 +469,7 @@ struct iwl_dev_info {  	u16 mac_type;  	u16 rf_type;  	u8 mac_step; +	u8 rf_step;  	u8 rf_id;  	u8 no_160;  	u8 cores; @@ -639,26 +640,40 @@ extern const struct iwl_cfg iwl_cfg_ma_a0_gf4_a0;  extern const struct iwl_cfg iwl_cfg_ma_a0_mr_a0;  extern const struct iwl_cfg iwl_cfg_ma_a0_ms_a0;  extern const struct iwl_cfg iwl_cfg_ma_a0_fm_a0; +extern const struct iwl_cfg iwl_cfg_ma_b0_hr_b0; +extern const struct iwl_cfg iwl_cfg_ma_b0_gf_a0; +extern const struct iwl_cfg iwl_cfg_ma_b0_gf4_a0; +extern const struct iwl_cfg iwl_cfg_ma_b0_mr_a0; +extern const struct iwl_cfg iwl_cfg_ma_b0_fm_a0;  extern const struct iwl_cfg iwl_cfg_snj_a0_mr_a0;  extern const struct iwl_cfg iwl_cfg_snj_a0_ms_a0;  extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0;  extern const struct iwl_cfg iwl_cfg_so_a0_ms_a0;  extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; +extern const struct iwl_cfg iwl_cfg_bz_a0_hr_a0;  extern const struct iwl_cfg iwl_cfg_bz_a0_hr_b0;  extern const struct iwl_cfg iwl_cfg_bz_a0_gf_a0;  extern const struct iwl_cfg iwl_cfg_bz_a0_gf4_a0;  extern const struct iwl_cfg iwl_cfg_bz_a0_mr_a0;  extern const struct iwl_cfg iwl_cfg_bz_a0_fm_a0;  extern const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0; +extern const struct iwl_cfg iwl_cfg_bz_a0_fm_b0; +extern const struct iwl_cfg iwl_cfg_bz_a0_fm4_b0;  extern const struct iwl_cfg iwl_cfg_gl_a0_fm_a0;  extern const struct iwl_cfg iwl_cfg_gl_b0_fm_b0;  extern const struct iwl_cfg iwl_cfg_bz_z0_gf_a0;  extern const struct iwl_cfg iwl_cfg_bnj_a0_fm_a0;  extern const struct iwl_cfg iwl_cfg_bnj_a0_fm4_a0;  extern const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_gf_a0;  extern const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_gf4_a0; +extern const struct iwl_cfg iwl_cfg_bnj_a0_hr_a0;  extern const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_hr_a0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_hr_b0;  extern const struct iwl_cfg iwl_cfg_bnj_b0_fm_b0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_fm4_b0;  #endif /* CONFIG_IWLMVM */  #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 3e1f011e93aa..587368a0ad4a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -102,6 +102,8 @@  #define CSR_LTR_LONG_VAL_AD_SNOOP_VAL		0x000003ff  #define CSR_LTR_LONG_VAL_AD_SCALE_USEC		2 +#define CSR_LTR_LAST_MSG			(CSR_BASE + 0x0DC) +  /* GIO Chicken Bits (PCI Express bus link power management) */  #define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100) @@ -309,6 +311,8 @@ enum {  	SILICON_A_STEP = 0,  	SILICON_B_STEP,  	SILICON_C_STEP, +	SILICON_D_STEP, +	SILICON_E_STEP,  	SILICON_Z_STEP = 0xf,  }; @@ -348,6 +352,7 @@ enum {  #define CSR_HW_RF_ID_TYPE_HRCDB		(0x00109F00)  #define CSR_HW_RF_ID_TYPE_GF		(0x0010D000)  #define CSR_HW_RF_ID_TYPE_GF4		(0x0010E000) +#define CSR_HW_RF_ID_TYPE_MS		(0x00111000)  /* HW_RF CHIP STEP  */  #define CSR_HW_RF_STEP(_val) (((_val) >> 8) & 0xF) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 48e7376a5fea..898d5dcf1012 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -138,6 +138,12 @@ static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,  	    alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)  		goto err; +	if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH && +	    alloc->req_size == 0) { +		IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n"); +		return -EINVAL; +	} +  	trans->dbg.fw_mon_cfg[alloc_id] = *alloc;  	return 0; @@ -350,9 +356,9 @@ void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,  	ret = dbg_tlv_alloc[tlv_idx](trans, tlv);  	if (ret) { -		IWL_ERR(trans, -			"WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n", -			type, ret, ext); +		IWL_WARN(trans, +			 "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n", +			 type, ret, ext);  		goto out_err;  	} @@ -797,7 +803,7 @@ static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt)  		if (!ret)  			dram_alloc = true;  		else -			IWL_WARN(fwrt, +			IWL_INFO(fwrt,  				 "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n",  				 i, ret);  	} @@ -1218,11 +1224,12 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,  		}  		fwrt->trans->dbg.restart_required = FALSE; -		IWL_DEBUG_INFO(fwrt, "WRT: tp %d, reset_fw %d\n", -			       tp, dump_data.trig->reset_fw); -		IWL_DEBUG_INFO(fwrt, "WRT: restart_required %d, last_tp_resetfw %d\n", -			       fwrt->trans->dbg.restart_required, -			       fwrt->trans->dbg.last_tp_resetfw); +		IWL_DEBUG_FW(fwrt, "WRT: tp %d, reset_fw %d\n", +			     tp, dump_data.trig->reset_fw); +		IWL_DEBUG_FW(fwrt, +			     "WRT: restart_required %d, last_tp_resetfw %d\n", +			     fwrt->trans->dbg.restart_required, +			     fwrt->trans->dbg.last_tp_resetfw);  		if (fwrt->trans->trans_cfg->device_family ==  		    IWL_DEVICE_FAMILY_9000) { @@ -1235,18 +1242,19 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,  			IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n");  		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==  			   IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) { -			IWL_DEBUG_INFO(fwrt, "WRT: stop and reload firmware\n"); +			IWL_DEBUG_FW(fwrt, "WRT: stop and reload firmware\n");  			fwrt->trans->dbg.restart_required = TRUE;  		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==  			   IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { -			IWL_DEBUG_INFO(fwrt, "WRT: stop only and no reload firmware\n"); +			IWL_DEBUG_FW(fwrt, +				     "WRT: stop only and no reload firmware\n");  			fwrt->trans->dbg.restart_required = FALSE;  			fwrt->trans->dbg.last_tp_resetfw =  				le32_to_cpu(dump_data.trig->reset_fw);  		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==  			   IWL_FW_INI_RESET_FW_MODE_NOTHING) { -			IWL_DEBUG_INFO(fwrt, -				       "WRT: nothing need to be done after debug collection\n"); +			IWL_DEBUG_FW(fwrt, +				     "WRT: nothing need to be done after debug collection\n");  		} else {  			IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",  				le32_to_cpu(dump_data.trig->reset_fw)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c index ae4c2a3d63d5..3a3c13a41fc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2005-2011, 2021 Intel Corporation + * Copyright (C) 2005-2011, 2021-2022 Intel Corporation   */  #include <linux/device.h>  #include <linux/interrupt.h> @@ -57,6 +57,7 @@ void __iwl_err(struct device *dev, enum iwl_err_mode mode, const char *fmt, ...)  	default:  		break;  	} +	vaf.va = &args;  	trace_iwlwifi_err(&vaf);  	va_end(args);  } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c index 999b7c652289..e46639b097f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -12,6 +12,9 @@  #include "iwl-trans.h"  #define CREATE_TRACE_POINTS +#ifdef CONFIG_CC_IS_GCC +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format" +#endif  #include "iwl-devtrace.h"  EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 4c977ba9cd85..34feb4d29adc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -127,6 +127,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)  	kfree(drv->fw.iml);  	kfree(drv->fw.ucode_capa.cmd_versions);  	kfree(drv->fw.phy_integration_ver); +	kfree(drv->trans->dbg.pc_data);  	for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)  		iwl_free_fw_img(drv, drv->fw.img + i); @@ -894,7 +895,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,  				drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus =  					true;  			} else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { -				IWL_ERR(drv, "Driver support upto 2 CPUs\n"); +				IWL_ERR(drv, "Driver support up to 2 CPUs\n");  				return -EINVAL;  			}  			break; @@ -1154,6 +1155,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,  			capa->num_stations =  				le32_to_cpup((const __le32 *)tlv_data);  			break; +		case IWL_UCODE_TLV_FW_NUM_BEACONS: +			if (tlv_len != sizeof(u32)) +				goto invalid_tlv_len; +			capa->num_beacons = +				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; @@ -1232,6 +1239,14 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,  			iwl_drv_set_dump_exclude(drv, tlv_type,  						 tlv_data, tlv_len);  			break; +		case IWL_UCODE_TLV_CURRENT_PC: +			if (tlv_len < sizeof(struct iwl_pc_data)) +				goto invalid_tlv_len; +			drv->trans->dbg.num_pc = +				tlv_len / sizeof(struct iwl_pc_data); +			drv->trans->dbg.pc_data = +				kmemdup(tlv_data, tlv_len, GFP_KERNEL); +			break;  		default:  			IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);  			break; @@ -1406,6 +1421,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)  			IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;  	fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;  	fw->ucode_capa.num_stations = IWL_MVM_STATION_COUNT_MAX; +	fw->ucode_capa.num_beacons = 1;  	/* dump all fw memory areas by default */  	fw->dbg.dump_mask = 0xffffffff; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index baa643386018..0e8ca761d24b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -47,13 +47,12 @@ struct iwl_nvm_data {  	struct ieee80211_supported_band bands[NUM_NL80211_BANDS];  	/* -	 * iftype data for low (2.4 GHz) and high (5 and 6 GHz) bands, -	 * we can use the same for 5 and 6 GHz bands because they have -	 * the same data +	 * iftype data for low (2.4 GHz) high (5 GHz) and uhb (6 GHz) bands  	 */  	struct {  		struct ieee80211_sband_iftype_data low[2];  		struct ieee80211_sband_iftype_data high[2]; +		struct ieee80211_sband_iftype_data uhb[2];  	} iftd;  	struct ieee80211_channel channels[]; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index aa8e08487b52..7dcb1c3ab728 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -81,7 +81,7 @@ static const u16 iwl_nvm_channels[] = {  	/* 2.4 GHz */  	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,  	/* 5 GHz */ -	36, 40, 44 , 48, 52, 56, 60, 64, +	36, 40, 44, 48, 52, 56, 60, 64,  	100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,  	149, 153, 157, 161, 165  }; @@ -860,7 +860,10 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,  	/* Advertise an A-MPDU exponent extension based on  	 * operating band  	 */ -	if (sband->band != NL80211_BAND_2GHZ) +	if (sband->band == NL80211_BAND_6GHZ && iftype_data->eht_cap.has_eht) +		iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= +			IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2; +	else if (sband->band != NL80211_BAND_2GHZ)  		iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |=  			IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1;  	else @@ -876,16 +879,13 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,  				       IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);  		break;  	case NL80211_BAND_6GHZ: -		if (!is_ap || iwlwifi_mod_params.nvm_file) -			iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= -				IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; +		iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= +			IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;  		fallthrough;  	case NL80211_BAND_5GHZ:  		iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= -			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; -		if (!is_ap || iwlwifi_mod_params.nvm_file) -			iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= -				IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; +			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | +			IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;  		break;  	default:  		WARN_ON(1); @@ -938,6 +938,10 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,  		}  	} +	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap) +		iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= +			IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; +  	switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {  	case IWL_CFG_RF_TYPE_GF:  	case IWL_CFG_RF_TYPE_MR: @@ -999,15 +1003,18 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,  	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));  	switch (sband->band) {  	case NL80211_BAND_2GHZ:  		iftype_data = data->iftd.low;  		break;  	case NL80211_BAND_5GHZ: -	case NL80211_BAND_6GHZ:  		iftype_data = data->iftd.high;  		break; +	case NL80211_BAND_6GHZ: +		iftype_data = data->iftd.uhb; +		break;  	default:  		WARN_ON(1);  		return; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 62ce116d3783..0dfe00eae05d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -350,6 +350,11 @@  #define WFPM_OTP_CFG1_ADDR		0x00a03098  #define WFPM_OTP_CFG1_IS_JACKET_BIT	BIT(4)  #define WFPM_OTP_CFG1_IS_CDB_BIT	BIT(5) +#define WFPM_OTP_BZ_BNJ_JACKET_BIT	5 +#define WFPM_OTP_BZ_BNJ_CDB_BIT		4 +#define WFPM_OTP_CFG1_IS_JACKET(_val)   (((_val) & 0x00000020) >> WFPM_OTP_BZ_BNJ_JACKET_BIT) +#define WFPM_OTP_CFG1_IS_CDB(_val)      (((_val) & 0x00000010) >> WFPM_OTP_BZ_BNJ_CDB_BIT) +  #define WFPM_GP2			0xA030B4 @@ -445,6 +450,8 @@ enum {  #define REG_CRF_ID_TYPE_GF_TC			0xF08  #define REG_CRF_ID_TYPE_MR			0x810  #define REG_CRF_ID_TYPE_FM			0x910 +#define REG_CRF_ID_TYPE_FMI			0x930 +#define REG_CRF_ID_TYPE_FMR			0x900  #define HPM_DEBUG			0xA03440  #define PERSISTENCE_BIT			BIT(12) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 9aced3e44bc2..9f1228b5a384 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -748,6 +748,18 @@ struct iwl_imr_data {  	__le64 imr_base_addr;  }; +#define IWL_TRANS_CURRENT_PC_NAME_MAX_BYTES      32 + +/** + * struct iwl_pc_data - program counter details + * @pc_name: cpu name + * @pc_address: cpu program counter + */ +struct iwl_pc_data { +	u8  pc_name[IWL_TRANS_CURRENT_PC_NAME_MAX_BYTES]; +	u32 pc_address; +}; +  /**   * struct iwl_trans_debug - transport debug related data   * @@ -775,6 +787,10 @@ struct iwl_imr_data {   * @periodic_trig_list: periodic triggers list   * @domains_bitmap: bitmap of active domains other than &IWL_FW_INI_DOMAIN_ALWAYS_ON   * @ucode_preset: preset based on ucode + * @dump_file_name_ext: dump file name extension + * @dump_file_name_ext_valid: dump file name extension if valid or not + * @num_pc: number of program counter for cpu + * @pc_data: details of the program counter   */  struct iwl_trans_debug {  	u8 n_dest_reg; @@ -813,6 +829,10 @@ struct iwl_trans_debug {  	bool restart_required;  	u32 last_tp_resetfw;  	struct iwl_imr_data imr_data; +	u8 dump_file_name_ext[IWL_FW_INI_MAX_NAME]; +	bool dump_file_name_ext_valid; +	u32 num_pc; +	struct iwl_pc_data *pc_data;  };  struct iwl_dma_ptr { @@ -977,7 +997,7 @@ struct iwl_trans_txqs {   *	0 indicates that frag SKBs (NETIF_F_SG) aren't supported.   * @hw_rf_id a u32 with the device RF ID   * @hw_crf_id a u32 with the device CRF ID - * @hw_cdb_id a u32 with the device CDB ID + * @hw_wfpm_id a u32 with the device wfpm ID   * @hw_id: a u32 with the ID of the device / sub-device.   *	Set during transport allocation.   * @hw_id_str: a string with info about HW ID. Set during transport allocation. @@ -1020,7 +1040,8 @@ struct iwl_trans {  	u32 hw_rev_step;  	u32 hw_rf_id;  	u32 hw_crf_id; -	u32 hw_cdb_id; +	u32 hw_cnv_id; +	u32 hw_wfpm_id;  	u32 hw_id;  	char hw_id_str[52];  	u32 sku_id[3]; diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index ae66192feefe..655d95d3a068 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h @@ -1,6 +1,6 @@  /* SPDX-License-Identifier: GPL-2.0-only */  /* - * Copyright (C) 2021 Intel Corporation + * Copyright (C) 2021 - 2022 Intel Corporation   */  #ifndef __iwl_mei_h__ @@ -301,7 +301,7 @@ struct iwl_mei_colloc_info {  struct iwl_mei_ops {  	void (*me_conn_status)(void *priv,  			       const struct iwl_mei_conn_info *conn_info); -	void (*rfkill)(void *priv, bool blocked); +	void (*rfkill)(void *priv, bool blocked, bool csme_taking_ownership);  	void (*roaming_forbidden)(void *priv, bool forbidden);  	void (*sap_connected)(void *priv);  	void (*nic_stolen)(void *priv); diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index 67dfb77fedf7..0a29fb013005 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -31,6 +31,11 @@ MODULE_LICENSE("GPL");  #define MEI_WLAN_UUID UUID_LE(0x13280904, 0x7792, 0x4fcb, \  			      0xa1, 0xaa, 0x5e, 0x70, 0xcb, 0xb1, 0xe8, 0x65) +/* After CSME takes ownership, it won't release it for 60 seconds to avoid + * frequent ownership transitions. + */ +#define MEI_OWNERSHIP_RETAKE_TIMEOUT_MS	msecs_to_jiffies(60000) +  /*   * Since iwlwifi calls iwlmei without any context, hold a pointer to the   * mei_cl_device structure here. @@ -156,6 +161,8 @@ struct iwl_mei_filters {   *	accessed without the mutex.   * @netdev_work: used to defer registering and unregistering of the netdev to   *	avoid taking the rtnl lock in the SAP messages handlers. + * @ownership_dwork: used to re-ask for NIC ownership after ownership was taken + *	by CSME or when a previous ownership request failed.   * @sap_seq_no: the sequence number for the SAP messages   * @seq_no: the sequence number for the SAP messages   * @dbgfs_dir: the debugfs dir entry @@ -179,6 +186,7 @@ struct iwl_mei {  	bool pldr_active;  	spinlock_t data_q_lock;  	struct work_struct netdev_work; +	struct delayed_work ownership_dwork;  	atomic_t sap_seq_no;  	atomic_t seq_no; @@ -716,7 +724,7 @@ iwl_mei_handle_conn_status(struct mei_cl_device *cldev,  						     status->link_prot_state);  	else  		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, -					  status->link_prot_state); +					  status->link_prot_state, false);  }  static void iwl_mei_set_init_conf(struct iwl_mei *mei) @@ -788,7 +796,7 @@ static void iwl_mei_handle_amt_state(struct mei_cl_device *cldev,  	if (mei->amt_enabled)  		iwl_mei_set_init_conf(mei);  	else if (iwl_mei_cache.ops) -		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false); +		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);  	schedule_work(&mei->netdev_work); @@ -829,10 +837,12 @@ static void iwl_mei_handle_csme_taking_ownership(struct mei_cl_device *cldev,  		 */  		mei->csme_taking_ownership = true; -		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true); +		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true, true);  	} else {  		iwl_mei_send_sap_msg(cldev,  				     SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED); +		schedule_delayed_work(&mei->ownership_dwork, +				      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);  	}  } @@ -882,7 +892,7 @@ static void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev,  	/* We can now start the connection, unblock rfkill */  	if (iwl_mei_cache.ops) -		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false); +		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);  }  static void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev, @@ -1447,7 +1457,13 @@ int iwl_mei_get_ownership(void)  	ret = wait_event_timeout(mei->get_ownership_wq,  				 mei->got_ownership, HZ / 2); -	return (!ret) ? -ETIMEDOUT : 0; +	if (!ret) { +		schedule_delayed_work(&mei->ownership_dwork, +				      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS); +		return -ETIMEDOUT; +	} + +	return 0;  out:  	mutex_unlock(&iwl_mei_mutex);  	return ret; @@ -1738,6 +1754,8 @@ void iwl_mei_device_state(bool up)  	iwl_mei_send_sap_msg(mei->cldev,  			     SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED);  	mei->csme_taking_ownership = false; +	schedule_delayed_work(&mei->ownership_dwork, +			      MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);  out:  	mutex_unlock(&iwl_mei_mutex);  } @@ -1773,7 +1791,8 @@ int iwl_mei_register(void *priv, const struct iwl_mei_ops *ops)  		if (iwl_mei_is_connected()) {  			if (mei->amt_enabled)  				iwl_mei_send_sap_msg(mei->cldev, -						     SAP_MSG_NOTIF_WIFIDR_UP); +						     SAP_MSG_NOTIF_WIFIDR_UP, +						     false);  			ops->rfkill(priv, mei->link_prot_state);  		}  	} @@ -1894,6 +1913,11 @@ static void iwl_mei_dbgfs_unregister(struct iwl_mei *mei) {}  #endif /* CONFIG_DEBUG_FS */ +static void iwl_mei_ownership_dwork(struct work_struct *wk) +{ +	iwl_mei_get_ownership(); +} +  #define ALLOC_SHARED_MEM_RETRY_MAX_NUM	3  /* @@ -1923,6 +1947,7 @@ static int iwl_mei_probe(struct mei_cl_device *cldev,  	init_waitqueue_head(&mei->pldr_wq);  	spin_lock_init(&mei->data_q_lock);  	INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work); +	INIT_DELAYED_WORK(&mei->ownership_dwork, iwl_mei_ownership_dwork);  	mei_cldev_set_drvdata(cldev, mei);  	mei->cldev = cldev; @@ -2087,7 +2112,7 @@ static void iwl_mei_remove(struct mei_cl_device *cldev)  	spin_unlock_bh(&mei->data_q_lock);  	if (iwl_mei_cache.ops) -		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false); +		iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);  	/*  	 * mei_cldev_disable will return only after all the MEI Rx is done. @@ -2105,6 +2130,7 @@ static void iwl_mei_remove(struct mei_cl_device *cldev)  	cancel_work_sync(&mei->send_csa_msg_wk);  	cancel_delayed_work_sync(&mei->csa_throttle_end_wk);  	cancel_work_sync(&mei->netdev_work); +	cancel_delayed_work_sync(&mei->ownership_dwork);  	/*  	 * If someone waits for the ownership, let him know that we are going diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index b28fcf0cf9cf..593fe28d89cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,7 +7,9 @@ iwlmvm-y += power.o coex.o  iwlmvm-y += tt.o offloading.o tdls.o  iwlmvm-y += ftm-responder.o ftm-initiator.o  iwlmvm-y += rfi.o -iwlmvm-y += mld-key.o +iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o mld-mac80211.o +iwlmvm-y += ptp.o +iwlmvm-y += time-sync.o  iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o  iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o  iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 0aac306304cb..ef50ccabcc73 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -2,6 +2,7 @@  /*   * Copyright (C) 2012-2014, 2020 Intel Corporation   * Copyright (C) 2016 Intel Deutschland GmbH + * Copyright (C) 2022 Intel Corporation   */  #include <net/mac80211.h>  #include "fw-api.h" @@ -75,7 +76,7 @@ static void iwl_mvm_iface_iterator(void *_data, u8 *mac,  	if (vif == data->ignore_vif)  		return; -	if (mvmvif->phy_ctxt != data->phyctxt) +	if (mvmvif->deflink.phy_ctxt != data->phyctxt)  		return;  	if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING)) @@ -132,7 +133,7 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) +	if (WARN_ON_ONCE(!mvmvif->deflink.phy_ctxt))  		return -EINVAL;  	/* @@ -142,7 +143,8 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	if (iwl_mvm_sf_update(mvm, vif, false))  		return -EINVAL; -	return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); +	return iwl_mvm_binding_update(mvm, vif, mvmvif->deflink.phy_ctxt, +				      true);  }  int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -150,10 +152,11 @@ int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	int ret; -	if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) +	if (WARN_ON_ONCE(!mvmvif->deflink.phy_ctxt))  		return -EINVAL; -	ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); +	ret = iwl_mvm_binding_update(mvm, vif, mvmvif->deflink.phy_ctxt, +				     false);  	if (!ret)  		if (iwl_mvm_sf_update(mvm, vif, true)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index ee3c8a786199..5a5b1128e75c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -194,7 +194,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,  	if (mvmsta->bt_reduced_txpower == enable)  		return 0; -	value = mvmsta->sta_id; +	value = mvmsta->deflink.sta_id;  	if (enable)  		value |= BT_REDUCED_TX_POWER_BIT; @@ -257,33 +257,35 @@ static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,  	swap(data->primary, data->secondary);  } -/* must be called under rcu_read_lock */ -static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, -				      struct ieee80211_vif *vif) +static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct iwl_bt_iterator_data *data, +				      unsigned int link_id)  { -	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct iwl_bt_iterator_data *data = _data; -	struct iwl_mvm *mvm = data->mvm; -	struct ieee80211_chanctx_conf *chanctx_conf;  	/* default smps_mode is AUTOMATIC - only used for client modes */  	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	u32 bt_activity_grading, min_ag_for_static_smps; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct iwl_mvm_vif_link_info *link_info; +	struct ieee80211_bss_conf *link_conf;  	int ave_rssi;  	lockdep_assert_held(&mvm->mutex); -	switch (vif->type) { -	case NL80211_IFTYPE_STATION: -		break; -	case NL80211_IFTYPE_AP: -		if (!mvmvif->ap_ibss_active) -			return; -		break; -	default: +	link_info = mvmvif->link[link_id]; +	if (!link_info)  		return; -	} -	chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); +	link_conf = rcu_dereference(vif->link_conf[link_id]); +	/* This can happen due to races: if we receive the notification +	 * and have the mutex held, while mac80211 is stuck on our mutex +	 * in the middle of removing the link. +	 */ +	if (!link_conf) +		return; + +	chanctx_conf = rcu_dereference(link_conf->chanctx_conf);  	/* If channel context is invalid or not on 2.4GHz .. */  	if ((!chanctx_conf || @@ -291,9 +293,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  		if (vif->type == NL80211_IFTYPE_STATION) {  			/* ... relax constraints and disable rssi events */  			iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, -					    smps_mode); -			iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, +					    smps_mode, link_id); +			iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id,  						    false); +			/* FIXME: should this be per link? */  			iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);  		}  		return; @@ -314,17 +317,18 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  	if (!vif->cfg.assoc)  		smps_mode = IEEE80211_SMPS_AUTOMATIC; -	if (mvmvif->phy_ctxt && -	    (mvm->last_bt_notif.rrc_status & BIT(mvmvif->phy_ctxt->id))) +	if (link_info->phy_ctxt && +	    (mvm->last_bt_notif.rrc_status & BIT(link_info->phy_ctxt->id)))  		smps_mode = IEEE80211_SMPS_AUTOMATIC;  	IWL_DEBUG_COEX(data->mvm, -		       "mac %d: bt_activity_grading %d smps_req %d\n", -		       mvmvif->id, bt_activity_grading, smps_mode); +		       "mac %d link %d: bt_activity_grading %d smps_req %d\n", +		       mvmvif->id, link_info->fw_link_id, +		       bt_activity_grading, smps_mode);  	if (vif->type == NL80211_IFTYPE_STATION)  		iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, -				    smps_mode); +				    smps_mode, link_id);  	/* low latency is always primary */  	if (iwl_mvm_vif_low_latency(mvmvif)) { @@ -353,6 +357,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  			data->secondary = chanctx_conf;  		} +		/* FIXME: TCM load per interface? or need something per link? */  		if (data->primary == chanctx_conf)  			data->primary_load = mvm->tcm.result.load[mvmvif->id];  		else if (data->secondary == chanctx_conf) @@ -370,6 +375,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  		/* if secondary is not NULL, it might be a GO */  		data->secondary = chanctx_conf; +	/* FIXME: TCM load per interface? or need something per link? */  	if (data->primary == chanctx_conf)  		data->primary_load = mvm->tcm.result.load[mvmvif->id];  	else if (data->secondary == chanctx_conf) @@ -384,7 +390,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  	if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||  	    mvm->cfg->bt_shared_single_ant || !vif->cfg.assoc ||  	    le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { -		iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); +		iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, false); +		/* FIXME: should this be per link? */  		iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);  		return;  	} @@ -396,10 +403,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  	if (!ave_rssi)  		ave_rssi = -100;  	if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { -		if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) +		if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, +						true))  			IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");  	} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { -		if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) +		if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, +						false))  			IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");  	} @@ -407,6 +416,32 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,  	iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);  } +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, +				      struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_bt_iterator_data *data = _data; +	struct iwl_mvm *mvm = data->mvm; +	unsigned int link_id; + +	lockdep_assert_held(&mvm->mutex); + +	switch (vif->type) { +	case NL80211_IFTYPE_STATION: +		break; +	case NL80211_IFTYPE_AP: +		if (!mvmvif->ap_ibss_active) +			return; +		break; +	default: +		return; +	} + +	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) +		iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id); +} +  static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)  {  	struct iwl_bt_iterator_data data = { @@ -521,7 +556,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	 * Rssi update while not associated - can happen since the statistics  	 * are handled asynchronously  	 */ -	if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) +	if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA)  		return;  	/* No BT - reports should be disabled */ @@ -537,10 +572,13 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	 */  	if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||  	    iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) -		ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, +		ret = iwl_mvm_bt_coex_reduced_txp(mvm, +						  mvmvif->deflink.ap_sta_id,  						  false);  	else -		ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); +		ret = iwl_mvm_bt_coex_reduced_txp(mvm, +						  mvmvif->deflink.ap_sta_id, +						  true);  	if (ret)  		IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); @@ -554,7 +592,7 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,  {  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); -	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; +	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt;  	enum iwl_bt_coex_lut_type lut_type;  	if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) @@ -578,7 +616,7 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,  {  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); -	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; +	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt;  	enum iwl_bt_coex_lut_type lut_type;  	if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index c5ad34b063df..6d1007f24b4a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -470,7 +470,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,  		for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++)  			data.rsc->mcast_key_id_map[i] =  				IWL_MCAST_KEY_MAP_INVALID; -		data.rsc->sta_id = cpu_to_le32(mvmvif->ap_sta_id); +		data.rsc->sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id);  		ieee80211_iter_keys(mvm->hw, vif,  				    iwl_mvm_wowlan_get_rsc_v5_data, @@ -493,7 +493,8 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,  		if (ver == 4) {  			size = sizeof(*data.rsc_tsc); -			data.rsc_tsc->sta_id = cpu_to_le32(mvmvif->ap_sta_id); +			data.rsc_tsc->sta_id = +				cpu_to_le32(mvmvif->deflink.ap_sta_id);  		} else {  			/* ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN */  			size = sizeof(data.rsc_tsc->params); @@ -563,6 +564,7 @@ static void iwl_mvm_wowlan_get_tkip_data(struct ieee80211_hw *hw,  		}  		for (i = 0; i < IWL_NUM_RSC; i++) { +			ieee80211_get_key_rx_seq(key, i, &seq);  			/* wrapping isn't allowed, AP must rekey */  			if (seq.tkip.iv32 > cur_rx_iv32)  				cur_rx_iv32 = seq.tkip.iv32; @@ -691,7 +693,7 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,  	pattern_cmd->n_patterns = wowlan->n_patterns;  	if (ver >= 3) -		pattern_cmd->sta_id = mvmvif->ap_sta_id; +		pattern_cmd->sta_id = mvmvif->deflink.ap_sta_id;  	for (i = 0; i < wowlan->n_patterns; i++) {  		int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); @@ -732,7 +734,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		return -EINVAL;  	/* add back the PHY */ -	if (WARN_ON(!mvmvif->phy_ctxt)) +	if (WARN_ON(!mvmvif->deflink.phy_ctxt))  		return -EINVAL;  	rcu_read_lock(); @@ -746,7 +748,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	chains_dynamic = ctx->rx_chains_dynamic;  	rcu_read_unlock(); -	ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, +	ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->deflink.phy_ctxt, &chandef,  				   chains_static, chains_dynamic);  	if (ret)  		return ret; @@ -763,12 +765,12 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	/* add back binding - XXX refactor? */  	binding_cmd.id_and_color = -		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, -						mvmvif->phy_ctxt->color)); +		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->deflink.phy_ctxt->id, +						mvmvif->deflink.phy_ctxt->color));  	binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);  	binding_cmd.phy = -		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, -						mvmvif->phy_ctxt->color)); +		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->deflink.phy_ctxt->id, +						mvmvif->deflink.phy_ctxt->color));  	binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,  							      mvmvif->color));  	for (i = 1; i < MAX_MACS_IN_BINDING; i++) @@ -791,7 +793,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false, 0);  	if (ret)  		return ret; -	rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); +	rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id], +			   ap_sta);  	ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);  	if (ret) @@ -800,8 +803,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	/* and some quota */  	quota = iwl_mvm_quota_cmd_get_quota(mvm, "a_cmd, 0);  	quota->id_and_color = -		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, -						mvmvif->phy_ctxt->color)); +		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->deflink.phy_ctxt->id, +						mvmvif->deflink.phy_ctxt->color));  	quota->quota = cpu_to_le32(IWL_MVM_MAX_QUOTA);  	quota->max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); @@ -1027,7 +1030,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,  		if (ver == 2) {  			size = sizeof(tkip_data.tkip);  			tkip_data.tkip.sta_id = -				cpu_to_le32(mvmvif->ap_sta_id); +				cpu_to_le32(mvmvif->deflink.ap_sta_id);  		} else if (ver == 1 || ver == IWL_FW_CMD_VER_UNKNOWN) {  			size = sizeof(struct iwl_wowlan_tkip_params_cmd_ver_1);  		} else { @@ -1076,7 +1079,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,  		kek_kck_cmd.kek_len = cpu_to_le16(mvmvif->rekey_data.kek_len);  		kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;  		kek_kck_cmd.akm = cpu_to_le32(mvmvif->rekey_data.akm); -		kek_kck_cmd.sta_id = cpu_to_le32(mvmvif->ap_sta_id); +		kek_kck_cmd.sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id);  		if (cmd_ver == 4) {  			cmd_size = sizeof(struct iwl_wowlan_kek_kck_material_cmd_v4); @@ -1269,7 +1272,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,  	mvmvif = iwl_mvm_vif_from_mac80211(vif); -	if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) { +	if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA) {  		/* if we're not associated, this must be netdetect */  		if (!wowlan->nd_config) {  			ret = 1; @@ -1285,10 +1288,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,  	} else {  		struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; -		wowlan_config_cmd.sta_id = mvmvif->ap_sta_id; +		wowlan_config_cmd.sta_id = mvmvif->deflink.ap_sta_id;  		ap_sta = rcu_dereference_protected( -			mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], +			mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id],  			lockdep_is_held(&mvm->mutex));  		if (IS_ERR_OR_NULL(ap_sta)) {  			ret = -EINVAL; @@ -2015,6 +2018,12 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,  {  	u32 i; +	if (!data) { +		IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n"); +		status = NULL; +		return; +	} +  	if (len < sizeof(*data)) {  		IWL_ERR(mvm, "Invalid WoWLAN info notification!\n");  		status = NULL; @@ -2575,7 +2584,8 @@ iwl_mvm_choose_query_wakeup_reasons(struct iwl_mvm *mvm,  	/* if FW uses status notification, status shouldn't be NULL here */  	if (!d3_data->status) {  		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -		u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA : mvmvif->ap_sta_id; +		u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA : +					      mvmvif->deflink.ap_sta_id;  		d3_data->status = iwl_mvm_send_wowlan_get_status(mvm, sta_id);  	} @@ -2702,10 +2712,15 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,  	struct iwl_d3_data *d3_data = data;  	u32 len;  	int ret; +	int wowlan_info_ver = iwl_fw_lookup_notif_ver(mvm->fw, +						      PROT_OFFLOAD_GROUP, +						      WOWLAN_INFO_NOTIFICATION, +						      IWL_FW_CMD_VER_UNKNOWN); +  	switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) {  	case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { -		struct iwl_wowlan_info_notif *notif = (void *)pkt->data; +		struct iwl_wowlan_info_notif *notif;  		if (d3_data->notif_received & IWL_D3_NOTIF_WOWLAN_INFO) {  			/* We might get two notifications due to dual bss */ @@ -2714,10 +2729,28 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,  			break;  		} +		if (wowlan_info_ver < 2) { +			struct iwl_wowlan_info_notif_v1 *notif_v1 = (void *)pkt->data; + +			notif = kmemdup(notif_v1, sizeof(*notif), GFP_ATOMIC); +			if (!notif) +				return false; + +			notif->tid_tear_down = notif_v1->tid_tear_down; +			notif->station_id = notif_v1->station_id; +			memset_after(notif, 0, station_id); +		} else { +			notif = (void *)pkt->data; +		} +  		d3_data->notif_received |= IWL_D3_NOTIF_WOWLAN_INFO;  		len = iwl_rx_packet_payload_len(pkt);  		iwl_mvm_parse_wowlan_info_notif(mvm, notif, d3_data->status,  						len); + +		if (wowlan_info_ver < 2) +			kfree(notif); +  		if (d3_data->status &&  		    d3_data->status->wakeup_reasons & IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT)  			/* We are supposed to get also wake packet notif */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 3779ac040ba0..3613b1fdc5d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -179,7 +179,7 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,  	mutex_lock(&mvm->mutex); -	ap_sta_id = mvmvif->ap_sta_id; +	ap_sta_id = mvmvif->deflink.ap_sta_id;  	switch (ieee80211_vif_type_p2p(vif)) {  	case NL80211_IFTYPE_ADHOC: @@ -211,14 +211,14 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,  	pos += scnprintf(buf+pos, bufsz-pos, "Load: %d\n",  			 mvm->tcm.result.load[mvmvif->id]);  	pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); -	for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) +	for (i = 0; i < ARRAY_SIZE(mvmvif->deflink.queue_params); i++)  		pos += scnprintf(buf+pos, bufsz-pos,  				 "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", -				 i, mvmvif->queue_params[i].txop, -				 mvmvif->queue_params[i].cw_min, -				 mvmvif->queue_params[i].cw_max, -				 mvmvif->queue_params[i].aifs, -				 mvmvif->queue_params[i].uapsd); +				 i, mvmvif->deflink.queue_params[i].txop, +				 mvmvif->deflink.queue_params[i].cw_min, +				 mvmvif->deflink.queue_params[i].cw_max, +				 mvmvif->deflink.queue_params[i].aifs, +				 mvmvif->deflink.queue_params[i].uapsd);  	if (vif->type == NL80211_IFTYPE_STATION &&  	    ap_sta_id != IWL_MVM_INVALID_STA) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 85b99316d029..84a488538427 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023 Intel Corporation   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH   * Copyright (C) 2016-2017 Intel Deutschland GmbH   */ @@ -8,6 +8,7 @@  #include <linux/err.h>  #include <linux/ieee80211.h>  #include <linux/netdevice.h> +#include <linux/dmi.h>  #include "mvm.h"  #include "sta.h" @@ -15,6 +16,7 @@  #include "debugfs.h"  #include "iwl-modparams.h"  #include "fw/error-dump.h" +#include "fw/api/phy-ctxt.h"  static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file,  					  char __user *user_buf, @@ -214,9 +216,9 @@ static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file,  	int pos;  	if (!mvm->temperature_test) -		pos = scnprintf(buf , sizeof(buf), "disabled\n"); +		pos = scnprintf(buf, sizeof(buf), "disabled\n");  	else -		pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); +		pos = scnprintf(buf, sizeof(buf), "%d\n", mvm->temperature);  	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);  } @@ -261,7 +263,7 @@ static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm,  		mvm->temperature = temperature;  	}  	IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", -		       mvm->temperature_test ? "En" : "Dis" , +		       mvm->temperature_test ? "En" : "Dis",  		       mvm->temperature);  	/* handle the temperature change */  	iwl_mvm_tt_handler(mvm); @@ -291,7 +293,7 @@ static ssize_t iwl_dbgfs_nic_temp_read(struct file *file,  	if (ret)  		return -EIO; -	pos = scnprintf(buf , sizeof(buf), "%d\n", temp); +	pos = scnprintf(buf, sizeof(buf), "%d\n", temp);  	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);  } @@ -338,6 +340,26 @@ static ssize_t iwl_dbgfs_sar_geo_profile_read(struct file *file,  	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);  } + +static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct file *file, +					     char __user *user_buf, +					     size_t count, loff_t *ppos) +{ +	struct iwl_mvm *mvm = file->private_data; +	int err, pos; +	char buf[12]; +	u32 value; + +	err = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, +				   DSM_FUNC_ENABLE_6E, +				   &iwl_guid, &value); +	if (err) +		return err; + +	pos = sprintf(buf, "0x%08x\n", value); + +	return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +}  #endif  static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, @@ -374,7 +396,7 @@ static ssize_t iwl_dbgfs_rs_data_read(struct file *file, char __user *user_buf,  {  	struct ieee80211_sta *sta = file->private_data;  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; +	struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->deflink.lq_sta.rs_fw;  	struct iwl_mvm *mvm = lq_sta->pers.drv;  	static const size_t bufsz = 2048;  	char *buff; @@ -714,6 +736,190 @@ static ssize_t iwl_dbgfs_fw_ver_read(struct file *file, char __user *user_buf,  	return ret;  } +static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, +					     char __user *user_buf, +					     size_t count, loff_t *ppos) +{ +	struct iwl_mvm *mvm = file->private_data; +	struct iwl_mvm_tas_status_resp tas_rsp; +	struct iwl_mvm_tas_status_resp *rsp = &tas_rsp; +	static const size_t bufsz = 1024; +	char *buff, *pos, *endpos; +	const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { +		[TAS_DISABLED_DUE_TO_BIOS] = +			"Due To BIOS", +		[TAS_DISABLED_DUE_TO_SAR_6DBM] = +			"Due To SAR Limit Less Than 6 dBm", +		[TAS_DISABLED_REASON_INVALID] = +			"N/A", +	}; +	const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { +		[TAS_DYNA_INACTIVE] = "INACTIVE", +		[TAS_DYNA_INACTIVE_MVM_MODE] = +			"inactive due to mvm mode", +		[TAS_DYNA_INACTIVE_TRIGGER_MODE] = +			"inactive due to trigger mode", +		[TAS_DYNA_INACTIVE_BLOCK_LISTED] = +			"inactive due to block listed", +		[TAS_DYNA_INACTIVE_UHB_NON_US] = +			"inactive due to uhb non US", +		[TAS_DYNA_ACTIVE] = "ACTIVE", +	}; +	struct iwl_host_cmd hcmd = { +		.id = WIDE_ID(DEBUG_GROUP, GET_TAS_STATUS), +		.flags = CMD_WANT_SKB, +		.len = { 0, }, +		.data = { NULL, }, +	}; +	int ret, i, tmp; +	bool tas_enabled = false; +	unsigned long dyn_status; + +	if (!iwl_mvm_firmware_running(mvm)) +		return -ENODEV; + +	mutex_lock(&mvm->mutex); +	ret = iwl_mvm_send_cmd(mvm, &hcmd); +	mutex_unlock(&mvm->mutex); +	if (ret < 0) +		return ret; + +	buff = kzalloc(bufsz, GFP_KERNEL); +	if (!buff) +		return -ENOMEM; +	pos = buff; +	endpos = pos + bufsz; + +	rsp = (void *)hcmd.resp_pkt->data; + +	pos += scnprintf(pos, endpos - pos, "TAS Conclusion:\n"); +	for (i = 0; i < rsp->in_dual_radio + 1; i++) { +		if (rsp->tas_status_mac[i].band != TAS_LMAC_BAND_INVALID && +		    rsp->tas_status_mac[i].dynamic_status & BIT(TAS_DYNA_ACTIVE)) { +			pos += scnprintf(pos, endpos - pos, "\tON for "); +			switch (rsp->tas_status_mac[i].band) { +			case TAS_LMAC_BAND_HB: +				pos += scnprintf(pos, endpos - pos, "HB\n"); +				break; +			case TAS_LMAC_BAND_LB: +				pos += scnprintf(pos, endpos - pos, "LB\n"); +				break; +			case TAS_LMAC_BAND_UHB: +				pos += scnprintf(pos, endpos - pos, "UHB\n"); +				break; +			case TAS_LMAC_BAND_INVALID: +				pos += scnprintf(pos, endpos - pos, +						 "INVALID BAND\n"); +				break; +			default: +				pos += scnprintf(pos, endpos - pos, +						 "Unsupported band (%d)\n", +						 rsp->tas_status_mac[i].band); +				goto out; +			} +			tas_enabled = true; +		} +	} +	if (!tas_enabled) +		pos += scnprintf(pos, endpos - pos, "\tOFF\n"); + +	pos += scnprintf(pos, endpos - pos, "TAS Report\n"); +	pos += scnprintf(pos, endpos - pos, "TAS FW version: %d\n", +			 rsp->tas_fw_version); +	pos += scnprintf(pos, endpos - pos, "Is UHB enabled for USA?: %s\n", +			 rsp->is_uhb_for_usa_enable ? "True" : "False"); +	pos += scnprintf(pos, endpos - pos, "Current MCC: 0x%x\n", +			 le16_to_cpu(rsp->curr_mcc)); + +	pos += scnprintf(pos, endpos - pos, "Block list entries:"); +	for (i = 0; i < APCI_WTAS_BLACK_LIST_MAX; i++) +		pos += scnprintf(pos, endpos - pos, " 0x%x", +				 le16_to_cpu(rsp->block_list[i])); + +	pos += scnprintf(pos, endpos - pos, "\nOEM name: %s\n", +			 dmi_get_system_info(DMI_SYS_VENDOR)); +	pos += scnprintf(pos, endpos - pos, "\tVendor In Approved List: %s\n", +			 iwl_mvm_is_vendor_in_approved_list() ? "YES" : "NO"); +	pos += scnprintf(pos, endpos - pos, +			 "\tDo TAS Support Dual Radio?: %s\n", +			 rsp->in_dual_radio ? "TRUE" : "FALSE"); + +	for (i = 0; i < rsp->in_dual_radio + 1; i++) { +		if (rsp->tas_status_mac[i].static_status == 0) { +			pos += scnprintf(pos, endpos - pos, +					 "Static status: disabled\n"); +			pos += scnprintf(pos, endpos - pos, +					 "Static disabled reason: %s (0)\n", +					 tas_dis_reason[0]); +			goto out; +		} + +		pos += scnprintf(pos, endpos - pos, "TAS status for "); +		switch (rsp->tas_status_mac[i].band) { +		case TAS_LMAC_BAND_HB: +			pos += scnprintf(pos, endpos - pos, "High band\n"); +			break; +		case TAS_LMAC_BAND_LB: +			pos += scnprintf(pos, endpos - pos, "Low band\n"); +			break; +		case TAS_LMAC_BAND_UHB: +			pos += scnprintf(pos, endpos - pos, +					 "Ultra high band\n"); +			break; +		case TAS_LMAC_BAND_INVALID: +			pos += scnprintf(pos, endpos - pos, +					 "INVALID band\n"); +			break; +		default: +			pos += scnprintf(pos, endpos - pos, +					 "Unsupported band (%d)\n", +					 rsp->tas_status_mac[i].band); +			goto out; +		} +		pos += scnprintf(pos, endpos - pos, "Static status: %sabled\n", +				 rsp->tas_status_mac[i].static_status ? +				 "En" : "Dis"); +		pos += scnprintf(pos, endpos - pos, +				 "\tStatic Disabled Reason: "); +		if (rsp->tas_status_mac[i].static_dis_reason < TAS_DISABLED_REASON_MAX) +			pos += scnprintf(pos, endpos - pos, "%s (%d)\n", +					 tas_dis_reason[rsp->tas_status_mac[i].static_dis_reason], +					 rsp->tas_status_mac[i].static_dis_reason); +		else +			pos += scnprintf(pos, endpos - pos, +					 "unsupported value (%d)\n", +					 rsp->tas_status_mac[i].static_dis_reason); + +		pos += scnprintf(pos, endpos - pos, "Dynamic status:\n"); +		dyn_status = (rsp->tas_status_mac[i].dynamic_status); +		for_each_set_bit(tmp, &dyn_status, sizeof(dyn_status)) { +			if (tmp >= 0 && tmp < TAS_DYNA_STATUS_MAX) +				pos += scnprintf(pos, endpos - pos, +						 "\t%s (%d)\n", +						 tas_current_status[tmp], tmp); +		} + +		pos += scnprintf(pos, endpos - pos, +				 "Is near disconnection?: %s\n", +				 rsp->tas_status_mac[i].near_disconnection ? +				 "True" : "False"); +		tmp = le16_to_cpu(rsp->tas_status_mac[i].max_reg_pwr_limit); +		pos += scnprintf(pos, endpos - pos, +				 "Max. regulatory pwr limit (dBm): %d.%03d\n", +				 tmp / 8, 125 * (tmp % 8)); +		tmp = le16_to_cpu(rsp->tas_status_mac[i].sar_limit); +		pos += scnprintf(pos, endpos - pos, +				 "SAR limit (dBm): %d.%03d\n", +				 tmp / 8, 125 * (tmp % 8)); +	} + +out: +	ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); +	kfree(buff); +	iwl_free_resp(&hcmd); +	return ret; +} +  static ssize_t iwl_dbgfs_phy_integration_ver_read(struct file *file,  						  char __user *user_buf,  						  size_t count, loff_t *ppos) @@ -1202,6 +1408,7 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len)  	struct sk_buff *beacon;  	struct ieee80211_tx_info *info;  	struct iwl_mac_beacon_cmd beacon_cmd = {}; +	unsigned int link_id;  	u8 rate;  	int i; @@ -1250,17 +1457,24 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len)  	info = IEEE80211_SKB_CB(beacon);  	rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif); -	beacon_cmd.flags = -		cpu_to_le16(iwl_mvm_mac_ctxt_get_beacon_flags(mvm->fw, rate)); -	beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); -	beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); +	for_each_mvm_vif_valid_link(mvmvif, link_id) { +		beacon_cmd.flags = +			cpu_to_le16(iwl_mvm_mac_ctxt_get_beacon_flags(mvm->fw, +								      rate)); +		beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); +		if (iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) > 12) +			beacon_cmd.link_id = +				cpu_to_le32(mvmvif->link[link_id]->fw_link_id); +		else +			beacon_cmd.link_id = cpu_to_le32((u32)mvmvif->id); -	iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, -				 &beacon_cmd.tim_size, -				 beacon->data, beacon->len); +		iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, +					 &beacon_cmd.tim_size, +					 beacon->data, beacon->len); -	iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, -					 sizeof(beacon_cmd)); +		iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, +						 sizeof(beacon_cmd)); +	}  	mutex_unlock(&mvm->mutex);  	dev_kfree_skb(beacon); @@ -1685,6 +1899,7 @@ MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);  MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);  MVM_DEBUGFS_READ_FILE_OPS(fw_ver);  MVM_DEBUGFS_READ_FILE_OPS(phy_integration_ver); +MVM_DEBUGFS_READ_FILE_OPS(tas_get_status);  MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);  MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);  MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); @@ -1703,6 +1918,7 @@ MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids);  #ifdef CONFIG_ACPI  MVM_DEBUGFS_READ_FILE_OPS(sar_geo_profile); +MVM_DEBUGFS_READ_FILE_OPS(wifi_6e_enable);  #endif  MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(amsdu_len, 16); @@ -1745,6 +1961,11 @@ static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,  	if (ret < 0)  		return ret; +	if (iwl_rx_packet_payload_len(hcmd.resp_pkt) < sizeof(*rsp)) { +		ret = -EIO; +		goto out; +	} +  	rsp = (void *)hcmd.resp_pkt->data;  	if (le32_to_cpu(rsp->status) != DEBUG_MEM_STATUS_SUCCESS) {  		ret = -ENXIO; @@ -1821,6 +2042,11 @@ static ssize_t iwl_dbgfs_mem_write(struct file *file,  	if (ret < 0)  		return ret; +	if (iwl_rx_packet_payload_len(hcmd.resp_pkt) < sizeof(*rsp)) { +		ret = -EIO; +		goto out; +	} +  	rsp = (void *)hcmd.resp_pkt->data;  	if (rsp->status != DEBUG_MEM_STATUS_SUCCESS) {  		ret = -ENXIO; @@ -1894,8 +2120,10 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm)  	if (mvm->fw->phy_integration_ver)  		MVM_DEBUGFS_ADD_FILE(phy_integration_ver, mvm->debugfs_dir, 0400); +	MVM_DEBUGFS_ADD_FILE(tas_get_status, mvm->debugfs_dir, 0400);  #ifdef CONFIG_ACPI  	MVM_DEBUGFS_ADD_FILE(sar_geo_profile, mvm->debugfs_dir, 0400); +	MVM_DEBUGFS_ADD_FILE(wifi_6e_enable, mvm->debugfs_dir, 0400);  #endif  	MVM_DEBUGFS_ADD_FILE(he_sniffer_params, mvm->debugfs_dir, 0600); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 05f3136b1c43..652a603c4500 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -25,6 +25,10 @@ struct iwl_mvm_smooth_entry {  	u64 host_time;  }; +enum iwl_mvm_pasn_flags { +	IWL_MVM_PASN_FLAG_HAS_HLTK = BIT(0), +}; +  struct iwl_mvm_ftm_pasn_entry {  	struct list_head list;  	u8 addr[ETH_ALEN]; @@ -33,6 +37,7 @@ struct iwl_mvm_ftm_pasn_entry {  	u8 cipher;  	u8 tx_pn[IEEE80211_CCMP_PN_LEN];  	u8 rx_pn[IEEE80211_CCMP_PN_LEN]; +	u32 flags;  };  int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -73,20 +78,30 @@ int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		struct ieee80211_sta *sta;  		rcu_read_lock(); -		sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); +		sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id]);  		if (!IS_ERR_OR_NULL(sta) && sta->mfp)  			expected_tk_len = 0;  		rcu_read_unlock();  	} -	if (tk_len != expected_tk_len || hltk_len != sizeof(pasn->hltk)) { +	if (tk_len != expected_tk_len || +	    (hltk_len && hltk_len != sizeof(pasn->hltk))) {  		IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n",  			tk_len, hltk_len);  		goto out;  	} +	if (!expected_tk_len && !hltk_len) { +		IWL_ERR(mvm, "TK and HLTK not set\n"); +		goto out; +	} +  	memcpy(pasn->addr, addr, sizeof(pasn->addr)); -	memcpy(pasn->hltk, hltk, sizeof(pasn->hltk)); + +	if (hltk_len) { +		memcpy(pasn->hltk, hltk, sizeof(pasn->hltk)); +		pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK; +	}  	if (tk && tk_len)  		memcpy(pasn->tk, tk, sizeof(pasn->tk)); @@ -510,13 +525,18 @@ iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		rcu_read_lock(); -		sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); +		sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id]); +		if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { +			rcu_read_unlock(); +			return PTR_ERR_OR_ZERO(sta); +		} +  		if (sta->mfp && (peer->ftm.trigger_based || peer->ftm.non_trigger_based))  			FTM_PUT_FLAG(PMF);  		rcu_read_unlock(); -		target->sta_id = mvmvif->ap_sta_id; +		target->sta_id = mvmvif->deflink.ap_sta_id;  	} else {  		target->sta_id = IWL_MVM_INVALID_STA;  	} @@ -691,7 +711,11 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			continue;  		target->cipher = entry->cipher; -		memcpy(target->hltk, entry->hltk, sizeof(target->hltk)); + +		if (entry->flags & IWL_MVM_PASN_FLAG_HAS_HLTK) +			memcpy(target->hltk, entry->hltk, sizeof(target->hltk)); +		else +			memset(target->hltk, 0, sizeof(target->hltk));  		if (vif->cfg.assoc &&  		    !memcmp(vif->bss_conf.bssid, target->bssid, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index e862d1b43f21..1b6fb73ddfc7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -119,7 +119,7 @@ iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,  			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |  				    IWL_TOF_RESPONDER_CMD_VALID_BSSID |  				    IWL_TOF_RESPONDER_CMD_VALID_STA_ID), -		.sta_id = mvmvif->bcast_sta.sta_id, +		.sta_id = mvmvif->deflink.bcast_sta.sta_id,  	};  	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 6);  	int err; @@ -317,6 +317,8 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,  		.addr = addr,  		.hltk = hltk,  	}; +	struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL; +  	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,  					   WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),  					   2); @@ -328,12 +330,21 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,  		return -ENOTSUPP;  	} -	hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); -	if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { -		IWL_ERR(mvm, "invalid cipher: %u\n", cipher); +	if ((!hltk || !hltk_len) && (!tk || !tk_len)) { +		IWL_ERR(mvm, "TK and HLTK not set\n");  		return -EINVAL;  	} +	if (hltk && hltk_len) { +		hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); +		if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { +			IWL_ERR(mvm, "invalid cipher: %u\n", cipher); +			return -EINVAL; +		} + +		hltk_data_ptr = &hltk_data; +	} +  	if (tk && tk_len) {  		sta = kzalloc(sizeof(*sta), GFP_KERNEL);  		if (!sta) @@ -350,7 +361,7 @@ int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,  		list_add_tail(&sta->list, &mvm->resp_pasn_list);  	} -	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data); +	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr);  	if (ret && sta)  		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0c6b49fcb00d..205c09bc9863 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -21,6 +21,7 @@  #include "iwl-phy-db.h"  #include "iwl-modparams.h"  #include "iwl-nvm-parse.h" +#include "time-sync.h"  #define MVM_UCODE_ALIVE_TIMEOUT	(HZ)  #define MVM_UCODE_CALIB_TIMEOUT	(2 * HZ) @@ -122,8 +123,6 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,  	u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,  					      UCODE_ALIVE_NTFY, 0);  	u32 i; -	struct iwl_trans *trans = mvm->trans; -	enum iwl_device_family device_family = trans->trans_cfg->device_family;  	if (version == 6) { @@ -233,8 +232,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,  	if (umac_error_table) {  		if (umac_error_table >= -		    mvm->trans->cfg->min_umac_error_event_table || -		    device_family >= IWL_DEVICE_FAMILY_BZ) { +		    mvm->trans->cfg->min_umac_error_event_table) {  			iwl_fw_umac_set_alive_err_table(mvm->trans,  							umac_error_table);  		} else { @@ -323,6 +321,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,  	static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };  	bool run_in_rfkill =  		ucode_type == IWL_UCODE_INIT || iwl_mvm_has_unified_ucode(mvm); +	u8 count; +	struct iwl_pc_data *pc_data;  	if (ucode_type == IWL_UCODE_REGULAR &&  	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) && @@ -395,6 +395,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,  		/* LMAC/UMAC PC info */  		if (trans->trans_cfg->device_family >= +					IWL_DEVICE_FAMILY_22000) { +			pc_data = trans->dbg.pc_data; +			for (count = 0; count < trans->dbg.num_pc; +			     count++, pc_data++) +				IWL_ERR(mvm, "%s: 0x%x\n", +					pc_data->pc_name, +					pc_data->pc_address); +		} else if (trans->trans_cfg->device_family >=  					IWL_DEVICE_FAMILY_9000) {  			IWL_ERR(mvm, "UMAC PC: 0x%x\n",  				iwl_read_umac_prph(trans, @@ -469,111 +477,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,  	return 0;  } -static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) -{ -	struct iwl_notification_wait init_wait; -	struct iwl_nvm_access_complete_cmd nvm_complete = {}; -	struct iwl_init_extended_cfg_cmd init_cfg = { -		.init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)), -	}; -	static const u16 init_complete[] = { -		INIT_COMPLETE_NOTIF, -	}; -	int ret; - -	if (mvm->trans->cfg->tx_with_siso_diversity) -		init_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY)); - -	lockdep_assert_held(&mvm->mutex); - -	mvm->rfkill_safe_init_done = false; - -	iwl_init_notification_wait(&mvm->notif_wait, -				   &init_wait, -				   init_complete, -				   ARRAY_SIZE(init_complete), -				   iwl_wait_init_complete, -				   NULL); - -	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); - -	/* Will also start the device */ -	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); -	if (ret) { -		IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); -		goto error; -	} -	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, -			       NULL); - -	/* Send init config command to mark that we are sending NVM access -	 * commands -	 */ -	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, -						INIT_EXTENDED_CFG_CMD), -				   CMD_SEND_IN_RFKILL, -				   sizeof(init_cfg), &init_cfg); -	if (ret) { -		IWL_ERR(mvm, "Failed to run init config command: %d\n", -			ret); -		goto error; -	} - -	/* Load NVM to NIC if needed */ -	if (mvm->nvm_file_name) { -		ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name, -					    mvm->nvm_sections); -		if (ret) -			goto error; -		ret = iwl_mvm_load_nvm_to_nic(mvm); -		if (ret) -			goto error; -	} - -	if (IWL_MVM_PARSE_NVM && !mvm->nvm_data) { -		ret = iwl_nvm_init(mvm); -		if (ret) { -			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); -			goto error; -		} -	} - -	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, -						NVM_ACCESS_COMPLETE), -				   CMD_SEND_IN_RFKILL, -				   sizeof(nvm_complete), &nvm_complete); -	if (ret) { -		IWL_ERR(mvm, "Failed to run complete NVM access: %d\n", -			ret); -		goto error; -	} - -	/* We wait for the INIT complete notification */ -	ret = iwl_wait_notification(&mvm->notif_wait, &init_wait, -				    MVM_UCODE_ALIVE_TIMEOUT); -	if (ret) -		return ret; - -	/* Read the NVM only at driver load time, no need to do this twice */ -	if (!IWL_MVM_PARSE_NVM && !mvm->nvm_data) { -		mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw); -		if (IS_ERR(mvm->nvm_data)) { -			ret = PTR_ERR(mvm->nvm_data); -			mvm->nvm_data = NULL; -			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); -			return ret; -		} -	} - -	mvm->rfkill_safe_init_done = true; - -	return 0; - -error: -	iwl_remove_notification(&mvm->notif_wait, &init_wait); -	return ret; -} -  #ifdef CONFIG_ACPI  static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,  				    struct iwl_phy_specific_cfg *phy_filters) @@ -700,6 +603,118 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)  	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &phy_cfg_cmd);  } +static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) +{ +	struct iwl_notification_wait init_wait; +	struct iwl_nvm_access_complete_cmd nvm_complete = {}; +	struct iwl_init_extended_cfg_cmd init_cfg = { +		.init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)), +	}; +	static const u16 init_complete[] = { +		INIT_COMPLETE_NOTIF, +	}; +	int ret; + +	if (mvm->trans->cfg->tx_with_siso_diversity) +		init_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY)); + +	lockdep_assert_held(&mvm->mutex); + +	mvm->rfkill_safe_init_done = false; + +	iwl_init_notification_wait(&mvm->notif_wait, +				   &init_wait, +				   init_complete, +				   ARRAY_SIZE(init_complete), +				   iwl_wait_init_complete, +				   NULL); + +	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); + +	/* Will also start the device */ +	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); +	if (ret) { +		IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); +		goto error; +	} +	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, +			       NULL); + +	/* Send init config command to mark that we are sending NVM access +	 * commands +	 */ +	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, +						INIT_EXTENDED_CFG_CMD), +				   CMD_SEND_IN_RFKILL, +				   sizeof(init_cfg), &init_cfg); +	if (ret) { +		IWL_ERR(mvm, "Failed to run init config command: %d\n", +			ret); +		goto error; +	} + +	/* Load NVM to NIC if needed */ +	if (mvm->nvm_file_name) { +		ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name, +					    mvm->nvm_sections); +		if (ret) +			goto error; +		ret = iwl_mvm_load_nvm_to_nic(mvm); +		if (ret) +			goto error; +	} + +	if (IWL_MVM_PARSE_NVM && !mvm->nvm_data) { +		ret = iwl_nvm_init(mvm); +		if (ret) { +			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); +			goto error; +		} +	} + +	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, +						NVM_ACCESS_COMPLETE), +				   CMD_SEND_IN_RFKILL, +				   sizeof(nvm_complete), &nvm_complete); +	if (ret) { +		IWL_ERR(mvm, "Failed to run complete NVM access: %d\n", +			ret); +		goto error; +	} + +	ret = iwl_send_phy_cfg_cmd(mvm); +	if (ret) { +		IWL_ERR(mvm, "Failed to run PHY configuration: %d\n", +			ret); +		goto error; +	} + +	/* We wait for the INIT complete notification */ +	ret = iwl_wait_notification(&mvm->notif_wait, &init_wait, +				    MVM_UCODE_ALIVE_TIMEOUT); +	if (ret) +		return ret; + +	/* Read the NVM only at driver load time, no need to do this twice */ +	if (!IWL_MVM_PARSE_NVM && !mvm->nvm_data) { +		mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw); +		if (IS_ERR(mvm->nvm_data)) { +			ret = PTR_ERR(mvm->nvm_data); +			mvm->nvm_data = NULL; +			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); +			return ret; +		} +	} + +	mvm->rfkill_safe_init_done = true; + +	return 0; + +error: +	iwl_remove_notification(&mvm->notif_wait, &init_wait); +	return ret; +} +  int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm)  {  	struct iwl_notification_wait calib_wait; @@ -1040,7 +1055,7 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)  	ret = iwl_read_ppag_table(&mvm->fwrt, &cmd, &cmd_size);  	/* Not supporting PPAG table is a valid scenario */ -	if(ret < 0) +	if (ret < 0)  		return 0;  	IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); @@ -1076,7 +1091,7 @@ static const struct dmi_system_id dmi_tas_approved_list[] = {  	},  		{ .ident = "LENOVO",  	  .matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "Lenovo"), +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),  		},  	},  	{ .ident = "DELL", @@ -1084,11 +1099,21 @@ static const struct dmi_system_id dmi_tas_approved_list[] = {  			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),  		},  	}, +	{ .ident = "MSFT", +	  .matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), +		}, +	},  	/* keep last */  	{}  }; +bool iwl_mvm_is_vendor_in_approved_list(void) +{ +	return dmi_check_system(dmi_tas_approved_list); +} +  static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc)  {  	int i; @@ -1368,6 +1393,11 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm)  {  } +bool iwl_mvm_is_vendor_in_approved_list(void) +{ +	return false; +} +  static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm)  {  	return DSM_VALUE_RFI_DISABLE; @@ -1525,12 +1555,11 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  		ret = iwl_send_phy_db_data(mvm->phy_db);  		if (ret)  			goto error; +		ret = iwl_send_phy_cfg_cmd(mvm); +		if (ret) +			goto error;  	} -	ret = iwl_send_phy_cfg_cmd(mvm); -	if (ret) -		goto error; -  	ret = iwl_mvm_send_bt_init_conf(mvm);  	if (ret)  		goto error; @@ -1557,8 +1586,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  	}  	/* init the fw <-> mac80211 STA mapping */ -	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) +	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {  		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); +		RCU_INIT_POINTER(mvm->fw_id_to_link_sta[i], NULL); +	} + +	memset(&mvm->fw_link_ids_map, 0, sizeof(mvm->fw_link_ids_map));  	mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; @@ -1664,8 +1697,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  			goto error;  	} -	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {  		iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB); +		iwl_mvm_time_sync_config(mvm, mvm->time_sync.peer_addr, +					 IWL_TIME_SYNC_PROTOCOL_TM | +					 IWL_TIME_SYNC_PROTOCOL_FTM); +	} + +	if (!mvm->ptp_data.ptp_clock) +		iwl_mvm_ptp_init(mvm);  	if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid))  		IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n"); @@ -1687,10 +1727,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  	iwl_mvm_tas_init(mvm);  	iwl_mvm_leds_sync(mvm); -	iwl_mvm_ftm_initiator_smooth_config(mvm); - -	if (fw_has_capa(&mvm->fw->ucode_capa, -			IWL_UCODE_TLV_CAPA_RFIM_SUPPORT)) { +	if (iwl_rfi_supported(mvm)) {  		if (iwl_mvm_eval_dsm_rfi(mvm) == DSM_VALUE_RFI_ENABLE)  			iwl_rfi_send_config_cmd(mvm, NULL);  	} @@ -1735,8 +1772,10 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)  		goto error;  	/* init the fw <-> mac80211 STA mapping */ -	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) +	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {  		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); +		RCU_INIT_POINTER(mvm->fw_id_to_link_sta[i], NULL); +	}  	if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) < 12) {  		/* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c new file mode 100644 index 000000000000..3814915cb1a6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 - 2023 Intel Corporation + */ +#include "mvm.h" +#include "time-event.h" + +static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm, +				       struct iwl_mvm_vif *mvm_vif) +{ +	u32 link_id; + +	lockdep_assert_held(&mvm->mutex); + +	link_id = ffz(mvm->fw_link_ids_map); + +	/* this case can happen if there're deactivated but not removed links */ +	if (link_id > IWL_MVM_FW_MAX_LINK_ID) +		return IWL_MVM_FW_LINK_ID_INVALID; + +	mvm->fw_link_ids_map |= BIT(link_id); +	return link_id; +} + +static void iwl_mvm_release_fw_link_id(struct iwl_mvm *mvm, u32 link_id) +{ +	lockdep_assert_held(&mvm->mutex); + +	if (!WARN_ON(link_id > IWL_MVM_FW_MAX_LINK_ID)) +		mvm->fw_link_ids_map &= ~BIT(link_id); +} + +static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm, +				 struct iwl_link_config_cmd *cmd, +				 enum iwl_ctxt_action action) +{ +	int ret; + +	cmd->action = cpu_to_le32(action); +	ret = iwl_mvm_send_cmd_pdu(mvm, +				   WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0, +				   sizeof(*cmd), cmd); +	if (ret) +		IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n", +			action, ret); +	return ret; +} + +int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id = link_conf->link_id; +	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; +	struct iwl_link_config_cmd cmd = {}; +	struct iwl_mvm_phy_ctxt *phyctxt; + +	if (WARN_ON_ONCE(!link_info)) +		return -EINVAL; + +	if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) { +		link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm, +								    mvmvif); +		if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) +			return -EINVAL; +	} + +	/* Update SF - Disable if needed. if this fails, SF might still be on +	 * while many macs are bound, which is forbidden - so fail the binding. +	 */ +	if (iwl_mvm_sf_update(mvm, vif, false)) +		return -EINVAL; + +	cmd.link_id = cpu_to_le32(link_info->fw_link_id); +	cmd.mac_id = cpu_to_le32(mvmvif->id); +	/* P2P-Device already has a valid PHY context during add */ +	phyctxt = link_info->phy_ctxt; +	if (phyctxt) +		cmd.phy_id = cpu_to_le32(phyctxt->id); +	else +		cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); + +	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN); + +	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) +		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); + +	return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD); +} + +int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 struct ieee80211_bss_conf *link_conf, +			 u32 changes, bool active) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id = link_conf->link_id; +	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; +	struct iwl_mvm_phy_ctxt *phyctxt; +	struct iwl_link_config_cmd cmd = {}; +	u32 ht_flag, flags = 0, flags_mask = 0; +	int ret; + +	if (WARN_ON_ONCE(!link_info || +			 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) +		return -EINVAL; + +	if (changes & LINK_CONTEXT_MODIFY_ACTIVE) { +		/* When activating a link, phy context should be valid; +		 * when deactivating a link, it also should be valid since +		 * the link was active before. So, do nothing in this case. +		 * Since a link is added first with FW_CTXT_INVALID, then we +		 * can get here in case it's removed before it was activated. +		 */ +		if (!link_info->phy_ctxt) +			return 0; + +		/* check there aren't too many active links */ +		if (!link_info->active && active) { +			int i, count = 0; + +			/* link with phy_ctxt is active in FW */ +			for_each_mvm_vif_valid_link(mvmvif, i) +				if (mvmvif->link[i]->phy_ctxt) +					count++; + +			if (vif->type == NL80211_IFTYPE_AP) { +				if (count > mvm->fw->ucode_capa.num_beacons) +					return -EOPNOTSUPP; +			/* this should be per HW or such */ +			} else if (count >= IWL_MVM_FW_MAX_ACTIVE_LINKS_NUM) { +				return -EOPNOTSUPP; +			} +		} + +		/* Catch early if driver tries to activate or deactivate a link +		 * twice. +		 */ +		WARN_ON_ONCE(active == link_info->active); + +		/* When deactivating a link session protection should +		 * be stopped +		 */ +		if (!active && vif->type == NL80211_IFTYPE_STATION) +			iwl_mvm_stop_session_protection(mvm, vif); +	} + +	cmd.link_id = cpu_to_le32(link_info->fw_link_id); + +	/* The phy_id, link address and listen_lmac can be modified only until +	 * the link becomes active, otherwise they will be ignored. +	 */ +	phyctxt = link_info->phy_ctxt; +	if (phyctxt) +		cmd.phy_id = cpu_to_le32(phyctxt->id); +	else +		cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); +	cmd.mac_id = cpu_to_le32(mvmvif->id); + +	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN); + +	cmd.active = cpu_to_le32(active); + +	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) +		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN); + +	/* TODO: set a value to cmd.listen_lmac when system requiremens +	 * will define it +	 */ + +	iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf, +				   &cmd.cck_rates, &cmd.ofdm_rates); + +	cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble); +	cmd.short_slot = cpu_to_le32(link_conf->use_short_slot); + +	/* The fw does not distinguish between ht and fat */ +	ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT; +	iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf, +					&cmd.protection_flags, +					ht_flag, LINK_PROT_FLG_TGG_PROTECT); + +	iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, &cmd.ac[0], +				  &cmd.qos_flags); + + +	cmd.bi = cpu_to_le32(link_conf->beacon_int); +	cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int * +					link_conf->dtim_period); + +	if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax || +	    (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) { +		changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS; +		goto send_cmd; +	} + +	cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext; + +	if (link_conf->uora_exists) { +		cmd.rand_alloc_ecwmin = +			link_conf->uora_ocw_range & 0x7; +		cmd.rand_alloc_ecwmax = +			(link_conf->uora_ocw_range >> 3) & 0x7; +	} + +	/* TODO  how to set ndp_fdbk_buff_th_exp? */ + +	if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif, +					  &cmd.trig_based_txf[0])) { +		flags |= LINK_FLG_MU_EDCA_CW; +		flags_mask |= LINK_FLG_MU_EDCA_CW; +	} + +	if (link_conf->eht_puncturing && !iwlwifi_mod_params.disable_11be) +		cmd.puncture_mask = cpu_to_le16(link_conf->eht_puncturing); +	else +		/* This flag can be set only if the MAC has eht support */ +		changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + +	cmd.bss_color = link_conf->he_bss_color.color; + +	if (!link_conf->he_bss_color.enabled) { +		flags |= LINK_FLG_BSS_COLOR_DIS; +		flags_mask |= LINK_FLG_BSS_COLOR_DIS; +	} + +	cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th); + +	/* Block 26-tone RU OFDMA transmissions */ +	if (link_info->he_ru_2mhz_block) { +		flags |= LINK_FLG_RU_2MHZ_BLOCK; +		flags_mask |= LINK_FLG_RU_2MHZ_BLOCK; +	} + +	if (link_conf->nontransmitted) { +		ether_addr_copy(cmd.ref_bssid_addr, +				link_conf->transmitter_bssid); +		cmd.bssid_index = link_conf->bssid_index; +	} + +send_cmd: +	cmd.modify_mask = cpu_to_le32(changes); +	cmd.flags = cpu_to_le32(flags); +	cmd.flags_mask = cpu_to_le32(flags_mask); + +	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY); +	if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE)) +		link_info->active = active; + +	return ret; +} + +int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id = link_conf->link_id; +	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; +	struct iwl_link_config_cmd cmd = {}; +	int ret; + +	if (WARN_ON(!link_info || +		    link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) +		return -EINVAL; + +	cmd.link_id = cpu_to_le32(link_info->fw_link_id); +	iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id); +	link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; + +	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE); + +	if (!ret) +		if (iwl_mvm_sf_update(mvm, vif, true)) +			IWL_ERR(mvm, "Failed to update SF state\n"); + +	return ret; +} + +/* link should be deactivated before removal, so in most cases we need to + * perform these two operations together + */ +int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 struct ieee80211_bss_conf *link_conf) +{ +	int ret; + +	ret = iwl_mvm_link_changed(mvm, vif, link_conf, +				   LINK_CONTEXT_MODIFY_ACTIVE, false); +	if (ret) +		return ret; + +	ret = iwl_mvm_remove_link(mvm, vif, link_conf); +	if (ret) +		return ret; + +	return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index aa791dbc3066..cc90f2884cff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -225,16 +225,20 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	 * that we should share it with another interface.  	 */ -	/* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ -	switch (vif->type) { -	case NL80211_IFTYPE_ADHOC: -		break; -	case NL80211_IFTYPE_STATION: -		if (!vif->p2p) +	/* MAC ID 0 should be used only for the managed/IBSS vif with non-MLO +	 * FW API +	 */ +	if (!mvm->mld_api_is_used) { +		switch (vif->type) { +		case NL80211_IFTYPE_ADHOC:  			break; -		fallthrough; -	default: -		__clear_bit(0, data.available_mac_ids); +		case NL80211_IFTYPE_STATION: +			if (!vif->p2p) +				break; +			fallthrough; +		default: +			__clear_bit(0, data.available_mac_ids); +		}  	}  	ieee80211_iterate_active_interfaces_atomic( @@ -293,15 +297,15 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  		 * For TVQM this will be overwritten later with the FW assigned  		 * queue value (when queue is enabled).  		 */ -		mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; +		mvmvif->deflink.cab_queue = IWL_MVM_DQA_GCAST_QUEUE;  	} -	mvmvif->bcast_sta.sta_id = IWL_MVM_INVALID_STA; -	mvmvif->mcast_sta.sta_id = IWL_MVM_INVALID_STA; -	mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; +	mvmvif->deflink.bcast_sta.sta_id = IWL_MVM_INVALID_STA; +	mvmvif->deflink.mcast_sta.sta_id = IWL_MVM_INVALID_STA; +	mvmvif->deflink.ap_sta_id = IWL_MVM_INVALID_STA;  	for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) -		mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; +		mvmvif->deflink.smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;  	return 0; @@ -396,15 +400,46 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm,  	*ofdm_rates = ofdm;  } -static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, -					 struct ieee80211_vif *vif, -					 struct iwl_mac_ctx_cmd *cmd) +void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *link_conf, +				__le32 *cck_rates, __le32 *ofdm_rates) +{ +	struct ieee80211_chanctx_conf *chanctx; +	u8 cck_ack_rates = 0, ofdm_ack_rates = 0; + +	rcu_read_lock(); +	chanctx = rcu_dereference(link_conf->chanctx_conf); +	iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band +					    : NL80211_BAND_2GHZ, +			  &cck_ack_rates, &ofdm_ack_rates); + +	rcu_read_unlock(); + +	*cck_rates = cpu_to_le32((u32)cck_ack_rates); +	*ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); +} + +void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, +				     struct ieee80211_vif *vif, +				     struct ieee80211_bss_conf *link_conf, +				     __le32 *protection_flags, u32 ht_flag, +				     u32 tgg_flag)  {  	/* for both sta and ap, ht_operation_mode hold the protection_mode */ -	u8 protection_mode = vif->bss_conf.ht_operation_mode & +	u8 protection_mode = link_conf->ht_operation_mode &  				 IEEE80211_HT_OP_MODE_PROTECTION; -	/* The fw does not distinguish between ht and fat */ -	u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; +	bool ht_enabled = !!(link_conf->ht_operation_mode & +			     IEEE80211_HT_OP_MODE_PROTECTION); + +	if (link_conf->use_cts_prot) +		*protection_flags |= cpu_to_le32(tgg_flag); + +	IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", +		       link_conf->use_cts_prot, +		       link_conf->ht_operation_mode); + +	if (!ht_enabled) +		return;  	IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode);  	/* @@ -416,12 +451,12 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm,  		break;  	case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:  	case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: -		cmd->protection_flags |= cpu_to_le32(ht_flag); +		*protection_flags |= cpu_to_le32(ht_flag);  		break;  	case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:  		/* Protect when channel wider than 20MHz */ -		if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) -			cmd->protection_flags |= cpu_to_le32(ht_flag); +		if (link_conf->chandef.width > NL80211_CHAN_WIDTH_20) +			*protection_flags |= cpu_to_le32(ht_flag);  		break;  	default:  		IWL_ERR(mvm, "Illegal protection mode %d\n", @@ -430,46 +465,77 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm,  	}  } -static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, -					struct ieee80211_vif *vif, -					struct iwl_mac_ctx_cmd *cmd, -					const u8 *bssid_override, -					u32 action) +void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			       struct ieee80211_bss_conf *link_conf, +			       struct iwl_ac_qos *ac, __le32 *qos_flags)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct ieee80211_chanctx_conf *chanctx; -	bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & -			     IEEE80211_HT_OP_MODE_PROTECTION); -	u8 cck_ack_rates, ofdm_ack_rates; -	const u8 *bssid = bssid_override ?: vif->bss_conf.bssid;  	int i; -	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, -							    mvmvif->color)); -	cmd->action = cpu_to_le32(action); +	for (i = 0; i < IEEE80211_NUM_ACS; i++) { +		u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); +		u8 ucode_ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); + +		ac[ucode_ac].cw_min = +			cpu_to_le16(mvmvif->deflink.queue_params[i].cw_min); +		ac[ucode_ac].cw_max = +			cpu_to_le16(mvmvif->deflink.queue_params[i].cw_max); +		ac[ucode_ac].edca_txop = +			cpu_to_le16(mvmvif->deflink.queue_params[i].txop * 32); +		ac[ucode_ac].aifsn = mvmvif->deflink.queue_params[i].aifs; +		ac[ucode_ac].fifos_mask = BIT(txf); +	} + +	if (link_conf->qos) +		*qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + +	if (link_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT) +		*qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); +} + +int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) +{ +	u32 mac_type = FW_MAC_TYPE_BSS_STA;  	switch (vif->type) {  	case NL80211_IFTYPE_STATION:  		if (vif->p2p) -			cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA); +			mac_type = FW_MAC_TYPE_P2P_STA;  		else -			cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA); +			mac_type = FW_MAC_TYPE_BSS_STA;  		break;  	case NL80211_IFTYPE_AP: -		cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO); +		mac_type = FW_MAC_TYPE_GO;  		break;  	case NL80211_IFTYPE_MONITOR: -		cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER); +		mac_type = FW_MAC_TYPE_LISTENER;  		break;  	case NL80211_IFTYPE_P2P_DEVICE: -		cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE); +		mac_type = FW_MAC_TYPE_P2P_DEVICE;  		break;  	case NL80211_IFTYPE_ADHOC: -		cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS); +		mac_type = FW_MAC_TYPE_IBSS;  		break;  	default:  		WARN_ON_ONCE(1);  	} +	return mac_type; +} + +static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					struct iwl_mac_ctx_cmd *cmd, +					const u8 *bssid_override, +					u32 action) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; +	u32 ht_flag; + +	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, +							    mvmvif->color)); +	cmd->action = cpu_to_le32(action); +	cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif));  	cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); @@ -480,15 +546,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,  	else  		eth_broadcast_addr(cmd->bssid_addr); -	rcu_read_lock(); -	chanctx = rcu_dereference(vif->bss_conf.chanctx_conf); -	iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band -					    : NL80211_BAND_2GHZ, -			  &cck_ack_rates, &ofdm_ack_rates); -	rcu_read_unlock(); - -	cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates); -	cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); +	iwl_mvm_set_fw_basic_rates(mvm, vif, &vif->bss_conf, &cmd->cck_rates, +				   &cmd->ofdm_rates);  	cmd->cck_short_preamble =  		cpu_to_le32(vif->bss_conf.use_short_preamble ? @@ -499,33 +558,14 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,  	cmd->filter_flags = 0; -	for (i = 0; i < IEEE80211_NUM_ACS; i++) { -		u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); -		u8 ucode_ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); +	iwl_mvm_set_fw_qos_params(mvm, vif, &vif->bss_conf, &cmd->ac[0], +				  &cmd->qos_flags); -		cmd->ac[ucode_ac].cw_min = -			cpu_to_le16(mvmvif->queue_params[i].cw_min); -		cmd->ac[ucode_ac].cw_max = -			cpu_to_le16(mvmvif->queue_params[i].cw_max); -		cmd->ac[ucode_ac].edca_txop = -			cpu_to_le16(mvmvif->queue_params[i].txop * 32); -		cmd->ac[ucode_ac].aifsn = mvmvif->queue_params[i].aifs; -		cmd->ac[ucode_ac].fifos_mask = BIT(txf); -	} - -	if (vif->bss_conf.qos) -		cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - -	if (vif->bss_conf.use_cts_prot) -		cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - -	IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", -		       vif->bss_conf.use_cts_prot, -		       vif->bss_conf.ht_operation_mode); -	if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) -		cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); -	if (ht_enabled) -		iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); +	/* The fw does not distinguish between ht and fat */ +	ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; +	iwl_mvm_set_fw_protection_flags(mvm, vif, &vif->bss_conf, +					&cmd->protection_flags, +					ht_flag, MAC_PROT_FLG_TGG_PROTECT);  }  static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, @@ -534,11 +574,76 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,  	int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,  				       sizeof(*cmd), cmd);  	if (ret) -		IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", +		IWL_ERR(mvm, "Failed to send MAC_CONTEXT_CMD (action:%d): %d\n",  			le32_to_cpu(cmd->action), ret);  	return ret;  } +void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf, +			      __le64 *dtim_tsf, __le32 *dtim_time, +			      __le32 *assoc_beacon_arrive_time) +{ +	u32 dtim_offs; + +	/* +	 * The DTIM count counts down, so when it is N that means N +	 * more beacon intervals happen until the DTIM TBTT. Therefore +	 * add this to the current time. If that ends up being in the +	 * future, the firmware will handle it. +	 * +	 * Also note that the system_timestamp (which we get here as +	 * "sync_device_ts") and TSF timestamp aren't at exactly the +	 * same offset in the frame -- the TSF is at the first symbol +	 * of the TSF, the system timestamp is at signal acquisition +	 * time. This means there's an offset between them of at most +	 * a few hundred microseconds (24 * 8 bits + PLCP time gives +	 * 384us in the longest case), this is currently not relevant +	 * as the firmware wakes up around 2ms before the TBTT. +	 */ +	dtim_offs = link_conf->sync_dtim_count * +			link_conf->beacon_int; +	/* convert TU to usecs */ +	dtim_offs *= 1024; + +	*dtim_tsf = +		cpu_to_le64(link_conf->sync_tsf + dtim_offs); +	*dtim_time = +		cpu_to_le32(link_conf->sync_device_ts + dtim_offs); +	*assoc_beacon_arrive_time = +		cpu_to_le32(link_conf->sync_device_ts); + +	IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", +		       le64_to_cpu(*dtim_tsf), +		       le32_to_cpu(*dtim_time), +		       dtim_offs); +} + +__le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, +						    struct ieee80211_vif *vif) +{ +	struct ieee80211_p2p_noa_attr *noa = +		&vif->bss_conf.p2p_noa_attr; + +	return cpu_to_le32(noa->oppps_ctwindow & +			IEEE80211_P2P_OPPPS_CTWINDOW_MASK); +} + +__le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, +					       struct ieee80211_vif *vif) +{ +	__le32 twt_policy = cpu_to_le32(0); + +	if (vif->bss_conf.twt_requester && IWL_MVM_USE_TWT) +		twt_policy |= cpu_to_le32(TWT_SUPPORTED); +	if (vif->bss_conf.twt_protected) +		twt_policy |= cpu_to_le32(PROTECTED_TWT_SUPPORTED); +	if (vif->bss_conf.twt_broadcast) +		twt_policy |= cpu_to_le32(BROADCAST_TWT_SUPPORTED); + +	return twt_policy; +} +  static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,  				    struct ieee80211_vif *vif,  				    u32 action, bool force_assoc_off, @@ -559,11 +664,9 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,  	cmd.filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP);  	if (vif->p2p) { -		struct ieee80211_p2p_noa_attr *noa = -			&vif->bss_conf.p2p_noa_attr; +		cmd.p2p_sta.ctwin = +			iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); -		cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & -					IEEE80211_P2P_OPPPS_CTWINDOW_MASK);  		ctxt_sta = &cmd.p2p_sta.sta;  	} else {  		ctxt_sta = &cmd.sta; @@ -573,39 +676,11 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,  	if (vif->cfg.assoc && vif->bss_conf.dtim_period &&  	    !force_assoc_off) {  		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -		u32 dtim_offs; -		/* -		 * The DTIM count counts down, so when it is N that means N -		 * more beacon intervals happen until the DTIM TBTT. Therefore -		 * add this to the current time. If that ends up being in the -		 * future, the firmware will handle it. -		 * -		 * Also note that the system_timestamp (which we get here as -		 * "sync_device_ts") and TSF timestamp aren't at exactly the -		 * same offset in the frame -- the TSF is at the first symbol -		 * of the TSF, the system timestamp is at signal acquisition -		 * time. This means there's an offset between them of at most -		 * a few hundred microseconds (24 * 8 bits + PLCP time gives -		 * 384us in the longest case), this is currently not relevant -		 * as the firmware wakes up around 2ms before the TBTT. -		 */ -		dtim_offs = vif->bss_conf.sync_dtim_count * -				vif->bss_conf.beacon_int; -		/* convert TU to usecs */ -		dtim_offs *= 1024; - -		ctxt_sta->dtim_tsf = -			cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); -		ctxt_sta->dtim_time = -			cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); -		ctxt_sta->assoc_beacon_arrive_time = -			cpu_to_le32(vif->bss_conf.sync_device_ts); - -		IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", -			       le64_to_cpu(ctxt_sta->dtim_tsf), -			       le32_to_cpu(ctxt_sta->dtim_time), -			       dtim_offs); +		iwl_mvm_set_fw_dtim_tbtt(mvm, vif, &vif->bss_conf, +					 &ctxt_sta->dtim_tsf, +					 &ctxt_sta->dtim_time, +					 &ctxt_sta->assoc_beacon_arrive_time);  		ctxt_sta->is_assoc = cpu_to_le32(1); @@ -635,14 +710,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,  	if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) {  		cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); -		if (vif->bss_conf.twt_requester && IWL_MVM_USE_TWT) -			ctxt_sta->data_policy |= cpu_to_le32(TWT_SUPPORTED); -		if (vif->bss_conf.twt_protected) -			ctxt_sta->data_policy |= -				cpu_to_le32(PROTECTED_TWT_SUPPORTED); -		if (vif->bss_conf.twt_broadcast) -			ctxt_sta->data_policy |= -				cpu_to_le32(BROADCAST_TWT_SUPPORTED); +		ctxt_sta->data_policy |= +			iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif);  	} @@ -654,7 +723,7 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,  					 u32 action)  {  	struct iwl_mac_ctx_cmd cmd = {}; -	u32 tfd_queue_msk = BIT(mvm->snif_queue); +	u32 tfd_queue_msk = 0;  	int ret;  	WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); @@ -669,6 +738,14 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,  				       MAC_FILTER_ACCEPT_GRP);  	ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); +	/* +	 * the queue mask is only relevant for old TX API, and +	 * mvm->snif_queue isn't set here (it's still set to +	 * IWL_MVM_INVALID_QUEUE so the BIT() of it is UB) +	 */ +	if (!iwl_mvm_has_new_tx_api(mvm)) +		tfd_queue_msk = BIT(mvm->snif_queue); +  	/* Allocate sniffer station */  	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk,  				       vif->type, IWL_STA_GENERAL_PURPOSE); @@ -716,20 +793,11 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)  		data->go_active = true;  } -static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, -					   struct ieee80211_vif *vif, -					   u32 action) +__le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, +					      struct ieee80211_vif *vif)  { -	struct iwl_mac_ctx_cmd cmd = {};  	struct iwl_mvm_go_iterator_data data = {}; -	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); - -	iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - -	/* Override the filter flags to accept only probe requests */ -	cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); -  	/*  	 * This flag should be set to true when the P2P Device is  	 * discoverable and there is at least another active P2P GO. Settings @@ -742,7 +810,25 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,  		mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,  		iwl_mvm_go_iterator, &data); -	cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0); +	return cpu_to_le32(data.go_active ? 1 : 0); +} + +static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, +					   struct ieee80211_vif *vif, +					   u32 action) +{ +	struct iwl_mac_ctx_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); + +	iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + +	cmd.p2p_dev.is_disc_extended = +		iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); + +	/* Override the filter flags to accept only probe requests */ +	cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); +  	return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);  } @@ -788,17 +874,44 @@ static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size)  	return ie - beacon;  } -static u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, -					   struct ieee80211_tx_info *info, -					   struct ieee80211_vif *vif) +u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, +				    struct ieee80211_tx_info *info, +				    struct ieee80211_vif *vif)  { +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct ieee80211_supported_band *sband;  	unsigned long basic = vif->bss_conf.basic_rates;  	u16 lowest_cck = IWL_RATE_COUNT, lowest_ofdm = IWL_RATE_COUNT; +	u32 link_id = u32_get_bits(info->control.flags, +				   IEEE80211_TX_CTRL_MLO_LINK); +	u8 band = info->band;  	u8 rate;  	u32 i; -	sband = mvm->hw->wiphy->bands[info->band]; +	if (link_id == IEEE80211_LINK_UNSPECIFIED && vif->valid_links) { +		for (i = 0; i < ARRAY_SIZE(mvmvif->link); i++) { +			if (!mvmvif->link[i]) +				continue; +			/* shouldn't do this when >1 link is active */ +			WARN_ON_ONCE(link_id != IEEE80211_LINK_UNSPECIFIED); +			link_id = i; +		} +	} + +	if (link_id < IEEE80211_LINK_UNSPECIFIED) { +		struct ieee80211_bss_conf *link_conf; + +		rcu_read_lock(); +		link_conf = rcu_dereference(vif->link_conf[link_id]); +		if (link_conf) { +			basic = link_conf->basic_rates; +			if (link_conf->chandef.chan) +				band = link_conf->chandef.chan->band; +		} +		rcu_read_unlock(); +	} + +	sband = mvm->hw->wiphy->bands[band];  	for_each_set_bit(i, &basic, BITS_PER_LONG) {  		u16 hw = sband->bitrates[i].hw_value; @@ -810,7 +923,9 @@ static u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm,  		}  	} -	if (info->band == NL80211_BAND_2GHZ && !vif->p2p) { +	if (band == NL80211_BAND_2GHZ && !vif->p2p && +	    vif->type != NL80211_IFTYPE_P2P_DEVICE && +	    !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) {  		if (lowest_cck != IWL_RATE_COUNT)  			rate = lowest_cck;  		else if (lowest_ofdm != IWL_RATE_COUNT) @@ -870,7 +985,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,  	/* Set up TX command fields */  	tx->len = cpu_to_le16((u16)beacon->len); -	tx->sta_id = mvmvif->bcast_sta.sta_id; +	tx->sta_id = mvmvif->deflink.bcast_sta.sta_id;  	tx->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);  	tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;  	tx_flags |= @@ -965,7 +1080,8 @@ static int iwl_mvm_mac_ctxt_send_beacon_v7(struct iwl_mvm *mvm,  static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm,  					   struct ieee80211_vif *vif, -					   struct sk_buff *beacon) +					   struct sk_buff *beacon, +					   struct ieee80211_bss_conf *link_conf)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); @@ -978,7 +1094,7 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm,  	/* Enable FILS on PSC channels only */  	rcu_read_lock(); -	ctx = rcu_dereference(vif->bss_conf.chanctx_conf); +	ctx = rcu_dereference(link_conf->chanctx_conf);  	channel = ieee80211_frequency_to_channel(ctx->def.chan->center_freq);  	WARN_ON(channel == 0);  	if (cfg80211_channel_is_psc(ctx->def.chan) && @@ -995,7 +1111,11 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm,  	beacon_cmd.flags = cpu_to_le16(flags);  	beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); -	beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); +	if (iwl_fw_lookup_cmd_ver(mvm->fw, BEACON_TEMPLATE_CMD, 0) > 12) +		beacon_cmd.link_id = +			cpu_to_le32(mvmvif->link[link_conf->link_id]->fw_link_id); +	else +		beacon_cmd.link_id = cpu_to_le32((u32)mvmvif->id);  	if (vif->type == NL80211_IFTYPE_AP)  		iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx, @@ -1015,9 +1135,10 @@ static int iwl_mvm_mac_ctxt_send_beacon_v9(struct iwl_mvm *mvm,  						sizeof(beacon_cmd));  } -int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, -				 struct ieee80211_vif *vif, -				 struct sk_buff *beacon) +static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					struct sk_buff *beacon, +					struct ieee80211_bss_conf *link_conf)  {  	if (WARN_ON(!beacon))  		return -EINVAL; @@ -1031,14 +1152,16 @@ int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,  	if (fw_has_api(&mvm->fw->ucode_capa,  		       IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE)) -		return iwl_mvm_mac_ctxt_send_beacon_v9(mvm, vif, beacon); +		return iwl_mvm_mac_ctxt_send_beacon_v9(mvm, vif, beacon, +						       link_conf);  	return iwl_mvm_mac_ctxt_send_beacon_v7(mvm, vif, beacon);  }  /* The beacon template for the AP/GO/IBSS has changed and needs update */  int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, -				    struct ieee80211_vif *vif) +				    struct ieee80211_vif *vif, +				    struct ieee80211_bss_conf *link_conf)  {  	struct sk_buff *beacon;  	int ret; @@ -1046,7 +1169,8 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,  	WARN_ON(vif->type != NL80211_IFTYPE_AP &&  		vif->type != NL80211_IFTYPE_ADHOC); -	beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL, 0); +	beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL, +					       link_conf->link_id);  	if (!beacon)  		return -ENOMEM; @@ -1057,7 +1181,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,  	}  #endif -	ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon); +	ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon, link_conf);  	dev_kfree_skb(beacon);  	return ret;  } @@ -1087,6 +1211,30 @@ static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac,  }  /* + * Fill the filter flags for mac context of type AP or P2P GO. + */ +void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, +					      struct iwl_mvm_vif *mvmvif, +					      __le32 *filter_flags, +					      int accept_probe_req_flag, +					      int accept_beacon_flag) +{ +	/* +	 * in AP mode, pass probe requests and beacons from other APs +	 * (needed for ht protection); when there're no any associated +	 * station don't ask FW to pass beacons to prevent unnecessary +	 * wake-ups. +	 */ +	*filter_flags |= cpu_to_le32(accept_probe_req_flag); +	if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) { +		*filter_flags |= cpu_to_le32(accept_beacon_flag); +		IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); +	} else { +		IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); +	} +} + +/*   * Fill the specific data for mac context of type AP of P2P GO   */  static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, @@ -1105,19 +1253,10 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,  	/* in AP mode, the MCAST FIFO takes the EDCA params from VO */  	cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); -	/* -	 * in AP mode, pass probe requests and beacons from other APs -	 * (needed for ht protection); when there're no any associated -	 * station don't ask FW to pass beacons to prevent unnecessary -	 * wake-ups. -	 */ -	cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); -	if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) { -		cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); -		IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); -	} else { -		IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); -	} +	iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, +						 &cmd->filter_flags, +						 MAC_FILTER_IN_PROBE_REQUEST, +						 MAC_FILTER_IN_BEACON);  	ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);  	ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * @@ -1125,7 +1264,7 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,  	if (!fw_has_api(&mvm->fw->ucode_capa,  			IWL_UCODE_TLV_API_STA_TYPE)) -		ctxt_ap->mcast_qid = cpu_to_le32(mvmvif->cab_queue); +		ctxt_ap->mcast_qid = cpu_to_le32(mvmvif->deflink.cab_queue);  	/*  	 * Only set the beacon time when the MAC is being added, when we @@ -1279,12 +1418,9 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  							   mvmvif->color));  	cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); -	ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, -				   sizeof(cmd), &cmd); -	if (ret) { -		IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); +	ret = iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +	if (ret)  		return ret; -	}  	mvmvif->uploaded = false; @@ -1312,7 +1448,8 @@ static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,  	if (!ieee80211_beacon_cntdwn_is_complete(csa_vif)) {  		int c = ieee80211_beacon_update_cntdwn(csa_vif); -		iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); +		iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif, +						&csa_vif->bss_conf);  		if (csa_vif->p2p &&  		    !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&  		    tx_success) { @@ -1420,6 +1557,7 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,  	struct ieee80211_vif *vif;  	u32 id = le32_to_cpu(mb->mac_id);  	union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; +	u32 mac_type;  	IWL_DEBUG_INFO(mvm,  		       "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", @@ -1435,6 +1573,14 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,  	if (!vif)  		goto out; +	mac_type = iwl_mvm_get_mac_type(vif); + +	IWL_DEBUG_INFO(mvm, "missed beacon mac_type=%u,\n", mac_type); + +	mvm->trans->dbg.dump_file_name_ext_valid = true; +	snprintf(mvm->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, +		 "MacId_%d_MacType_%d", id, mac_type); +  	rx_missed_bcon = le32_to_cpu(mb->consec_missed_beacons);  	rx_missed_bcon_since_rx =  		le32_to_cpu(mb->consec_missed_beacons_since_last_rx); @@ -1568,9 +1714,9 @@ void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm,  	    sizeof(struct ieee80211_p2p_noa_desc) + 2)  		new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc); -	old_data = rcu_dereference_protected(mvmvif->probe_resp_data, -					lockdep_is_held(&mvmvif->mvm->mutex)); -	rcu_assign_pointer(mvmvif->probe_resp_data, new_data); +	old_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data, +					     lockdep_is_held(&mvmvif->mvm->mutex)); +	rcu_assign_pointer(mvmvif->deflink.probe_resp_data, new_data);  	if (old_data)  		kfree_rcu(old_data, rcu_head); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 565522466eba..17f788a5ff6b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023 Intel Corporation   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH   * Copyright (C) 2016-2017 Intel Deutschland GmbH   */ @@ -28,6 +28,7 @@  #include "fw/error-dump.h"  #include "iwl-prph.h"  #include "iwl-nvm-parse.h" +#include "time-sync.h"  static const struct ieee80211_iface_limit iwl_mvm_limits[] = {  	{ @@ -222,23 +223,46 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)  	return ret;  } +/* Each capability added here should also be add to tm_if_types_ext_capa_sta */  static const u8 he_if_types_ext_capa_sta[] = {  	 [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,  	 [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, -	 [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +	 [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF | +	       WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB, +	 [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,  }; -static const struct wiphy_iftype_ext_capab he_iftypes_ext_capa[] = { +static const u8 tm_if_types_ext_capa_sta[] = { +	 [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, +	 [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT | +	       WLAN_EXT_CAPA3_TIMING_MEASUREMENT_SUPPORT, +	 [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF | +	       WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB, +	 [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB, +	 [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, +}; + +/* Additional interface types for which extended capabilities are + * specified separately + */ +static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = {  	{  		.iftype = NL80211_IFTYPE_STATION,  		.extended_capabilities = he_if_types_ext_capa_sta,  		.extended_capabilities_mask = he_if_types_ext_capa_sta,  		.extended_capabilities_len = sizeof(he_if_types_ext_capa_sta),  	}, +	{ +		.iftype = NL80211_IFTYPE_STATION, +		.extended_capabilities = tm_if_types_ext_capa_sta, +		.extended_capabilities_mask = tm_if_types_ext_capa_sta, +		.extended_capabilities_len = sizeof(tm_if_types_ext_capa_sta), +		/* relevant only if EHT is supported */ +		.eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP, +	},  }; -static int -iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	*tx_ant = iwl_mvm_get_valid_tx_ant(mvm); @@ -260,6 +284,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	bool unified = fw_has_capa(&mvm->fw->ucode_capa,  				   IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);  #endif +	u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); +	u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);  	/* Tell mac80211 our characteristics */  	ieee80211_hw_set(hw, SIGNAL_DBM); @@ -269,17 +295,33 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	ieee80211_hw_set(hw, SUPPORTS_PS);  	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);  	ieee80211_hw_set(hw, AMPDU_AGGREGATION); -	ieee80211_hw_set(hw, TIMING_BEACON_ONLY);  	ieee80211_hw_set(hw, CONNECTION_MONITOR);  	ieee80211_hw_set(hw, CHANCTX_STA_CSA);  	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);  	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);  	ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);  	ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); -	ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);  	ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);  	ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);  	ieee80211_hw_set(hw, STA_MMPDU_TXQ); + +	/* Set this early since we need to have it for the check below */ +	if (mvm->mld_api_is_used && +	    mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) +		hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + +	/* With MLD FW API, it tracks timing by itself, +	 * no need for any timing from the host +	 */ +	if (!mvm->mld_api_is_used) +		ieee80211_hw_set(hw, TIMING_BEACON_ONLY); + +	/* We should probably have this, but mac80211 +	 * currently doesn't support it for MLO. +	 */ +	if (!(hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) +		ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP); +  	/*  	 * On older devices, enabling TX A-MSDU occasionally leads to  	 * something getting messed up, the command read from the FIFO @@ -376,6 +418,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  	wiphy_ext_feature_set(hw->wiphy,  			      NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); +	wiphy_ext_feature_set(hw->wiphy, +			      NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);  	if (fw_has_capa(&mvm->fw->ucode_capa,  			IWL_UCODE_TLV_CAPA_FTM_CALIBRATED)) { @@ -384,11 +428,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  		hw->wiphy->pmsr_capa = &iwl_mvm_pmsr_capa;  	} -	if (fw_has_capa(&mvm->fw->ucode_capa, -			IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT)) +	if (sec_key_ver && +	    fw_has_capa(&mvm->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_BIGTK_TX_SUPPORT)) +		wiphy_ext_feature_set(hw->wiphy, +				      NL80211_EXT_FEATURE_BEACON_PROTECTION); +	else if (fw_has_capa(&mvm->fw->ucode_capa, +			     IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT))  		wiphy_ext_feature_set(hw->wiphy,  				      NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT); +	if (fw_has_capa(&mvm->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM)) +		hw->wiphy->hw_timestamp_max_peers = 1; +  	ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);  	hw->wiphy->features |=  		NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | @@ -562,16 +615,34 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)  					      NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);  	} +	hw->wiphy->iftype_ext_capab = NULL; +	hw->wiphy->num_iftype_ext_capab = 0; +  	if (mvm->nvm_data->sku_cap_11ax_enable &&  	    !iwlwifi_mod_params.disable_11ax) { -		hw->wiphy->iftype_ext_capab = he_iftypes_ext_capa; +		hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa;  		hw->wiphy->num_iftype_ext_capab = -			ARRAY_SIZE(he_iftypes_ext_capa); +			ARRAY_SIZE(add_iftypes_ext_capa) - 1;  		ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);  		ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);  	} +	if (iwl_fw_lookup_cmd_ver(mvm->fw, +				  WIDE_ID(DATA_PATH_GROUP, +					  WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), +				  IWL_FW_CMD_VER_UNKNOWN) >= 1) { +		IWL_DEBUG_INFO(mvm->trans, "Timing measurement supported\n"); + +		if (!hw->wiphy->iftype_ext_capab) { +			hw->wiphy->num_iftype_ext_capab = 1; +			hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa + +				ARRAY_SIZE(add_iftypes_ext_capa) - 1; +		} else { +			hw->wiphy->iftype_ext_capab = add_iftypes_ext_capa + 1; +		} +	} +  	mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;  #ifdef CONFIG_PM_SLEEP @@ -653,9 +724,8 @@ static void iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,  	ieee80211_free_txskb(mvm->hw, skb);  } -static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, -			   struct ieee80211_tx_control *control, -			   struct sk_buff *skb) +void iwl_mvm_mac_tx(struct ieee80211_hw *hw, +		    struct ieee80211_tx_control *control, struct sk_buff *skb)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct ieee80211_sta *sta = control->sta; @@ -663,6 +733,9 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  	struct ieee80211_hdr *hdr = (void *)skb->data;  	bool offchannel = IEEE80211_SKB_CB(skb)->flags &  		IEEE80211_TX_CTL_TX_OFFCHAN; +	u32 link_id = u32_get_bits(info->control.flags, +				   IEEE80211_TX_CTRL_MLO_LINK); +	struct ieee80211_sta *tmp_sta = sta;  	if (iwl_mvm_is_radio_killed(mvm)) {  		IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); @@ -686,7 +759,7 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  	    !offchannel) {  		struct iwl_mvm_vif *mvmvif =  			iwl_mvm_vif_from_mac80211(info->control.vif); -		u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id); +		u8 ap_sta_id = READ_ONCE(mvmvif->deflink.ap_sta_id);  		if (ap_sta_id < mvm->fw->ucode_capa.num_stations) {  			/* mac80211 holds rcu read lock */ @@ -696,6 +769,25 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,  		}  	} +	if (tmp_sta && !sta && link_id != IEEE80211_LINK_UNSPECIFIED && +	    !ieee80211_is_probe_resp(hdr->frame_control)) { +		/* translate MLD addresses to LINK addresses */ +		struct ieee80211_link_sta *link_sta = +			rcu_dereference(tmp_sta->link[link_id]); +		struct ieee80211_bss_conf *link_conf = +			rcu_dereference(info->control.vif->link_conf[link_id]); +		struct ieee80211_mgmt *mgmt; + +		if (WARN_ON(!link_sta || !link_conf)) +			goto drop; + +		/* if sta is NULL, the frame is a management frame */ +		mgmt = (void *)hdr; +		memcpy(mgmt->da, link_sta->addr, ETH_ALEN); +		memcpy(mgmt->sa, link_conf->addr, ETH_ALEN); +		memcpy(mgmt->bssid, link_conf->bssid, ETH_ALEN); +	} +  	iwl_mvm_tx_skb(mvm, skb, sta);  	return;   drop: @@ -732,7 +824,10 @@ void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)  	rcu_read_lock();  	do { -		while (likely(!mvmtxq->stopped && +		while (likely(!test_bit(IWL_MVM_TXQ_STATE_STOP_FULL, +					&mvmtxq->state) && +			      !test_bit(IWL_MVM_TXQ_STATE_STOP_REDIRECT, +					&mvmtxq->state) &&  			      !test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))) {  			skb = ieee80211_tx_dequeue(hw, txq); @@ -751,48 +846,31 @@ void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)  	rcu_read_unlock();  } -static void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw, -				      struct ieee80211_txq *txq) +void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw, +			       struct ieee80211_txq *txq)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq); -	/* -	 * Please note that racing is handled very carefully here: -	 * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is -	 * deleted afterwards. -	 * This means that if: -	 * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list): -	 *	queue is allocated and we can TX. -	 * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list): -	 *	a race, should defer the frame. -	 * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list): -	 *	need to allocate the queue and defer the frame. -	 * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list): -	 *	queue is already scheduled for allocation, no need to allocate, -	 *	should defer the frame. -	 */ - -	/* If the queue is allocated TX and return. */ -	if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) { -		/* -		 * Check that list is empty to avoid a race where txq_id is -		 * already updated, but the queue allocation work wasn't -		 * finished -		 */ -		if (unlikely(txq->sta && !list_empty(&mvmtxq->list))) -			return; - +	if (likely(test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) || +	    !txq->sta) {  		iwl_mvm_mac_itxq_xmit(hw, txq);  		return;  	} -	/* The list is being deleted only after the queue is fully allocated. */ -	if (!list_empty(&mvmtxq->list)) -		return; +	/* iwl_mvm_mac_itxq_xmit() will later be called by the worker +	 * to handle any packets we leave on the txq now +	 */ -	list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs); -	schedule_work(&mvm->add_stream_wk); +	spin_lock_bh(&mvm->add_stream_lock); +	/* The list is being deleted only after the queue is fully allocated. */ +	if (list_empty(&mvmtxq->list) && +	    /* recheck under lock */ +	    !test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) { +		list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs); +		schedule_work(&mvm->add_stream_wk); +	} +	spin_unlock_bh(&mvm->add_stream_lock);  }  #define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...)		\ @@ -847,9 +925,9 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	}  } -static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, -				    struct ieee80211_vif *vif, -				    struct ieee80211_ampdu_params *params) +int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     struct ieee80211_ampdu_params *params)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -871,8 +949,8 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,  	switch (action) {  	case IEEE80211_AMPDU_RX_START: -		if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id == -				iwl_mvm_sta_from_mac80211(sta)->sta_id) { +		if (iwl_mvm_vif_from_mac80211(vif)->deflink.ap_sta_id == +		    iwl_mvm_sta_from_mac80211(sta)->deflink.sta_id) {  			struct iwl_mvm_vif *mvmvif;  			u16 macid = iwl_mvm_vif_from_mac80211(vif)->id;  			struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid]; @@ -935,17 +1013,29 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,  {  	struct iwl_mvm *mvm = data;  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_probe_resp_data *probe_data; +	unsigned int link_id;  	mvmvif->uploaded = false; -	mvmvif->ap_sta_id = IWL_MVM_INVALID_STA;  	spin_lock_bh(&mvm->time_event_lock);  	iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);  	spin_unlock_bh(&mvm->time_event_lock); -	mvmvif->phy_ctxt = NULL;  	memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); -	memset(&mvmvif->probe_resp_data, 0, sizeof(mvmvif->probe_resp_data)); + +	for_each_mvm_vif_valid_link(mvmvif, link_id) { +		mvmvif->link[link_id]->ap_sta_id = IWL_MVM_INVALID_STA; +		mvmvif->link[link_id]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; +		mvmvif->link[link_id]->phy_ctxt = NULL; +		mvmvif->link[link_id]->active = 0; +	} + +	probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data, +					       lockdep_is_held(&mvm->mutex)); +	if (probe_data) +		kfree_rcu(probe_data, rcu_head); +	RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL);  }  static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) @@ -982,6 +1072,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)  	mvm->rx_ba_sessions = 0;  	mvm->fwrt.dump.conf = FW_DBG_INVALID;  	mvm->monitor_on = false; +#ifdef CONFIG_IWLWIFI_DEBUGFS +	mvm->beacon_inject_active = false; +#endif  	/* keep statistics ticking */  	iwl_mvm_accu_radio_stats(mvm); @@ -1044,7 +1137,7 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)  	return ret;  } -static int iwl_mvm_mac_start(struct ieee80211_hw *hw) +int iwl_mvm_mac_start(struct ieee80211_hw *hw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -1113,9 +1206,8 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)  	mutex_unlock(&mvm->mutex);  } -static void -iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, -			      enum ieee80211_reconfig_type reconfig_type) +void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, +				   enum ieee80211_reconfig_type reconfig_type)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1177,7 +1269,7 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)  	}  } -static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) +void iwl_mvm_mac_stop(struct ieee80211_hw *hw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1216,7 +1308,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)  	cancel_work_sync(&mvm->async_handlers_wk);  } -static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)  {  	u16 i; @@ -1230,8 +1322,8 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)  	return NULL;  } -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, -				s16 tx_power) +int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 s16 tx_power)  {  	u32 cmd_id = REDUCE_TX_POWER_CMD;  	int len; @@ -1266,8 +1358,8 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);  } -static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif) +int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1280,7 +1372,7 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,  		mvmvif->csa_bcn_pending = false;  		mvmsta = iwl_mvm_sta_from_staid_protected(mvm, -							  mvmvif->ap_sta_id); +							  mvmvif->deflink.ap_sta_id);  		if (WARN_ON(!mvmsta)) {  			ret = -EIO; @@ -1288,8 +1380,10 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,  		}  		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); - -		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); +		if (mvm->mld_api_is_used) +			iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); +		else +			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);  		if (!fw_has_capa(&mvm->fw->ucode_capa,  				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) { @@ -1313,8 +1407,8 @@ out_unlock:  	return ret;  } -static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw, -					 struct ieee80211_vif *vif) +void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1350,7 +1444,7 @@ static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,  	iwl_mvm_post_channel_switch(hw, vif);  } -static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk) +void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)  {  	struct iwl_mvm_vif *mvmvif;  	struct ieee80211_vif *vif; @@ -1362,15 +1456,47 @@ static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)  	ieee80211_chswitch_done(vif, false);  } -static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, -				     struct ieee80211_vif *vif) +static u8 +iwl_mvm_chandef_get_primary_80(struct cfg80211_chan_def *chandef) +{ +	int data_start; +	int control_start; +	int bw; + +	if (chandef->width == NL80211_CHAN_WIDTH_320) +		bw = 320; +	else if (chandef->width == NL80211_CHAN_WIDTH_160) +		bw = 160; +	else +		return 0; + +	/* data is bw wide so the start is half the width */ +	data_start = chandef->center_freq1 - bw / 2; +	/* control is 20Mhz width */ +	control_start = chandef->chan->center_freq - 10; + +	return (control_start - data_start) / 80; +} + +/* + * Returns true if addding the interface is done + * (either with success or failure) + * + * FIXME: remove this again and merge it in + */ +static bool iwl_mvm_mac_add_interface_common(struct iwl_mvm *mvm, +					     struct ieee80211_hw *hw, +					     struct ieee80211_vif *vif, +					     int *ret)  { -	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	int ret; + +	lockdep_assert_held(&mvm->mutex);  	mvmvif->mvm = mvm; -	RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL); + +	/* the first link always points to the default one */ +	mvmvif->link[0] = &mvmvif->deflink;  	/*  	 * Not much to do here. The stack will not allow interface @@ -1378,17 +1504,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	 * don't really have to check the types.  	 */ -	mutex_lock(&mvm->mutex); -  	/* make sure that beacon statistics don't go backwards with FW reset */  	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) -		mvmvif->beacon_stats.accu_num_beacons += -			mvmvif->beacon_stats.num_beacons; +		mvmvif->deflink.beacon_stats.accu_num_beacons += +			mvmvif->deflink.beacon_stats.num_beacons;  	/* Allocate resources for the MAC context, and add it to the fw  */ -	ret = iwl_mvm_mac_ctxt_init(mvm, vif); -	if (ret) -		goto out_unlock; +	*ret = iwl_mvm_mac_ctxt_init(mvm, vif); +	if (*ret) +		return true;  	rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); @@ -1405,28 +1529,51 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	 */  	if (vif->type == NL80211_IFTYPE_AP ||  	    vif->type == NL80211_IFTYPE_ADHOC) { -		ret = iwl_mvm_alloc_bcast_sta(mvm, vif); -		if (ret) { -			IWL_ERR(mvm, "Failed to allocate bcast sta\n"); -			goto out_unlock; -		} - -		/* -		 * Only queue for this station is the mcast queue, -		 * which shouldn't be in TFD mask anyway -		 */ -		ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, -					       0, vif->type, -					       IWL_STA_MULTICAST); -		if (ret) -			goto out_unlock; -  		iwl_mvm_vif_dbgfs_register(mvm, vif); -		goto out_unlock; +		return true;  	}  	mvmvif->features |= hw->netdev_features; +	return false; +} + +static int iwl_mvm_alloc_bcast_mcast_sta(struct iwl_mvm *mvm, +					 struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	ret = iwl_mvm_alloc_bcast_sta(mvm, vif); +	if (ret) { +		IWL_ERR(mvm, "Failed to allocate bcast sta\n"); +		return ret; +	} + +	/* +	 * Only queue for this station is the mcast queue, +	 * which shouldn't be in TFD mask anyway +	 */ +	return iwl_mvm_allocate_int_sta(mvm, &mvmvif->deflink.mcast_sta, 0, +					vif->type, +					IWL_STA_MULTICAST); +} + +static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	mutex_lock(&mvm->mutex); + +	/* Common for MLD and non-MLD API */ +	if (iwl_mvm_mac_add_interface_common(mvm, hw, vif, &ret)) +		goto out; +  	ret = iwl_mvm_mac_ctxt_add(mvm, vif);  	if (ret)  		goto out_unlock; @@ -1454,13 +1601,13 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	 */  	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { -		mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); -		if (!mvmvif->phy_ctxt) { +		mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); +		if (!mvmvif->deflink.phy_ctxt) {  			ret = -ENOSPC;  			goto out_free_bf;  		} -		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); +		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt);  		ret = iwl_mvm_binding_add_vif(mvm, vif);  		if (ret)  			goto out_unref_phy; @@ -1478,8 +1625,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	INIT_DELAYED_WORK(&mvmvif->csa_work,  			  iwl_mvm_channel_switch_disconnect_wk); -	if (vif->type == NL80211_IFTYPE_MONITOR) +	if (vif->type == NL80211_IFTYPE_MONITOR) {  		mvm->monitor_on = true; +		mvm->monitor_p80 = +			iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chandef); +	}  	iwl_mvm_vif_dbgfs_register(mvm, vif); @@ -1491,12 +1641,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  		mvm->csme_vif = vif;  	} +out: +	if (!ret && (vif->type == NL80211_IFTYPE_AP || +		     vif->type == NL80211_IFTYPE_ADHOC)) +		ret = iwl_mvm_alloc_bcast_mcast_sta(mvm, vif); +  	goto out_unlock;   out_unbind:  	iwl_mvm_binding_remove_vif(mvm, vif);   out_unref_phy: -	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); +	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt);   out_free_bf:  	if (mvm->bf_allowed_vif == mvmvif) {  		mvm->bf_allowed_vif = NULL; @@ -1504,7 +1659,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  				       IEEE80211_VIF_SUPPORTS_CQM_RSSI);  	}   out_remove_mac: -	mvmvif->phy_ctxt = NULL; +	mvmvif->deflink.phy_ctxt = NULL;  	iwl_mvm_mac_ctxt_remove(mvm, vif);   out_unlock:  	mutex_unlock(&mvm->mutex); @@ -1512,8 +1667,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,  	return ret;  } -static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, -					struct ieee80211_vif *vif) +void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif)  {  	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {  		/* @@ -1525,8 +1680,12 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,  	}  } -static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, -					 struct ieee80211_vif *vif) +/* This function is doing the common part of removing the interface for + * both - MLD and non-MLD modes. Returns true if removing the interface + * is done + */ +static bool iwl_mvm_mac_remove_interface_common(struct ieee80211_hw *hw, +						struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1545,9 +1704,9 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,  		mvm->csme_vif = NULL;  	} -	probe_data = rcu_dereference_protected(mvmvif->probe_resp_data, +	probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data,  					       lockdep_is_held(&mvm->mutex)); -	RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL); +	RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL);  	if (probe_data)  		kfree_rcu(probe_data, rcu_head); @@ -1574,20 +1733,30 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,  			mvm->noa_duration = 0;  		}  #endif -		iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta); -		iwl_mvm_dealloc_bcast_sta(mvm, vif); -		goto out_release; +		return true;  	} +	iwl_mvm_power_update_mac(mvm); +	return false; +} + +static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, +					 struct ieee80211_vif *vif) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + +	if (iwl_mvm_mac_remove_interface_common(hw, vif)) +		goto out; +  	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {  		mvm->p2p_device_vif = NULL;  		iwl_mvm_rm_p2p_bcast_sta(mvm, vif);  		iwl_mvm_binding_remove_vif(mvm, vif); -		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); -		mvmvif->phy_ctxt = NULL; +		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); +		mvmvif->deflink.phy_ctxt = NULL;  	} -	iwl_mvm_power_update_mac(mvm);  	iwl_mvm_mac_ctxt_remove(mvm, vif);  	RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); @@ -1595,13 +1764,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,  	if (vif->type == NL80211_IFTYPE_MONITOR)  		mvm->monitor_on = false; -out_release: -	mutex_unlock(&mvm->mutex); -} +out: +	if (vif->type == NL80211_IFTYPE_AP || +	    vif->type == NL80211_IFTYPE_ADHOC) { +		iwl_mvm_dealloc_int_sta(mvm, &mvmvif->deflink.mcast_sta); +		iwl_mvm_dealloc_bcast_sta(mvm, vif); +	} -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) -{ -	return 0; +	mutex_unlock(&mvm->mutex);  }  struct iwl_mvm_mc_iter_data { @@ -1675,8 +1845,8 @@ static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)  		IWL_ERR(mvm, "Failed to synchronize multicast groups update\n");  } -static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, -				     struct netdev_hw_addr_list *mc_list) +u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, +			      struct netdev_hw_addr_list *mc_list)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mcast_filter_cmd *cmd; @@ -1712,10 +1882,9 @@ static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,  	return (u64)(unsigned long)cmd;  } -static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, -				     unsigned int changed_flags, -				     unsigned int *total_flags, -				     u64 multicast) +void iwl_mvm_configure_filter(struct ieee80211_hw *hw, +			      unsigned int changed_flags, +			      unsigned int *total_flags, u64 multicast)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; @@ -1762,8 +1931,7 @@ static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, -				    struct ieee80211_vif *vif) +int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mu_group_mgmt_cmd cmd = {}; @@ -1888,12 +2056,13 @@ set_thresholds:  }  static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, -					    struct ieee80211_sta *sta, +					    struct ieee80211_link_sta *link_sta,  					    struct iwl_he_pkt_ext_v2 *pkt_ext,  					    bool inheritance)  { -	u8 nss = (sta->deflink.he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1; -	u8 *ppe = &sta->deflink.he_cap.ppe_thres[0]; +	u8 nss = (link_sta->he_cap.ppe_thres[0] & +		  IEEE80211_PPE_THRES_NSS_MASK) + 1; +	u8 *ppe = &link_sta->he_cap.ppe_thres[0];  	u8 ru_index_bitmap =  		u8_get_bits(*ppe,  			    IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); @@ -1904,9 +2073,9 @@ static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm,  			  inheritance);  } -static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, -						     u8 nominal_padding, -						     u32 *flags) +static int +iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, +					 u8 nominal_padding)  {  	int low_th = -1;  	int high_th = -1; @@ -1929,21 +2098,22 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p  		break;  	} +	if (low_th < 0 || high_th < 0) +		return -EINVAL; +  	/* Set the PPE thresholds accordingly */ -	if (low_th >= 0 && high_th >= 0) { -		for (i = 0; i < MAX_HE_SUPP_NSS; i++) { -			u8 bw; +	for (i = 0; i < MAX_HE_SUPP_NSS; i++) { +		u8 bw; -			for (bw = 0; -			     bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); -			     bw++) { -				pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; -				pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; -			} +		for (bw = 0; +			bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); +			bw++) { +			pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; +			pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th;  		} - -		*flags |= STA_CTXT_HE_PACKET_EXT;  	} + +	return 0;  }  static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, @@ -1971,6 +2141,190 @@ static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,  	}  } +/* Set the pkt_ext field according to PPE Thresholds element */ +int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm, +			    struct ieee80211_link_sta *link_sta, +			    struct iwl_he_pkt_ext_v2 *pkt_ext) +{ +	u8 nominal_padding; +	int i, ret = 0; + +	if (WARN_ON(!link_sta)) +		return -EINVAL; + +	/* Initialize the PPE thresholds to "None" (7), as described in Table +	 * 9-262ac of 80211.ax/D3.0. +	 */ +	memset(pkt_ext, IWL_HE_PKT_EXT_NONE, +	       sizeof(struct iwl_he_pkt_ext_v2)); + +	if (link_sta->eht_cap.has_eht) { +		nominal_padding = +			u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5], +				    IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); + +		/* If PPE Thresholds exists, parse them into a FW-familiar +		 * format. +		 */ +		if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & +		    IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { +			u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] & +				IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; +			u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0]; +			u8 ru_index_bitmap = +				u16_get_bits(*ppe, +					     IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); +			 /* Starting after PPE header */ +			u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + +			iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, +					  ppe, ppe_pos_bit, true); +		/* EHT PPE Thresholds doesn't exist - set the API according to +		 * HE PPE Tresholds +		 */ +		} else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & +			   IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { +			/* Even though HE Capabilities IE doesn't contain PPE +			 * Thresholds for BW 320Mhz, thresholds for this BW will +			 * be filled in with the same values as 160Mhz, due to +			 * the inheritance, as required. +			 */ +			iwl_mvm_set_pkt_ext_from_he_ppe(mvm, link_sta, pkt_ext, +							true); + +			/* According to the requirements, for MCSs 12-13 the +			 * maximum value between HE PPE Threshold and Common +			 * Nominal Packet Padding needs to be taken +			 */ +			iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding); + +		/* if PPE Thresholds doesn't present in both EHT IE and HE IE - +		 * take the Thresholds from Common Nominal Packet Padding field +		 */ +		} else { +			ret = iwl_mvm_set_pkt_ext_from_nominal_padding(pkt_ext, +								       nominal_padding); +		} +	} else if (link_sta->he_cap.has_he) { +		/* If PPE Thresholds exist, parse them into a FW-familiar format. */ +		if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & +			IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { +			iwl_mvm_set_pkt_ext_from_he_ppe(mvm, link_sta, pkt_ext, +							false); +		/* PPE Thresholds doesn't exist - set the API PPE values +		 * according to Common Nominal Packet Padding field. +		 */ +		} else { +			nominal_padding = +				u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9], +					    IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); +			if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) +				ret = iwl_mvm_set_pkt_ext_from_nominal_padding(pkt_ext, +									       nominal_padding); +		} +	} + +	for (i = 0; i < MAX_HE_SUPP_NSS; i++) { +		int bw; + +		for (bw = 0; +		     bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]); +		     bw++) { +			u8 *qam_th = +				&pkt_ext->pkt_ext_qam_th[i][bw][0]; + +			IWL_DEBUG_HT(mvm, +				     "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", +				     i, bw, qam_th[0], qam_th[1]); +		} +	} +	return ret; +} + +/* + * This function sets the MU EDCA parameters ans returns whether MU EDCA + * is enabled or not + */ +bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, +				   struct iwl_mvm_vif *mvmvif, +				   struct iwl_he_backoff_conf *trig_based_txf) +{ +	int i; +	/* Mark MU EDCA as enabled, unless none detected on some AC */ +	bool mu_edca_enabled = true; + +	for (i = 0; i < IEEE80211_NUM_ACS; i++) { +		struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = +			&mvmvif->deflink.queue_params[i].mu_edca_param_rec; +		u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); + +		if (!mvmvif->deflink.queue_params[i].mu_edca) { +			mu_edca_enabled = false; +			break; +		} + +		trig_based_txf[ac].cwmin = +			cpu_to_le16(mu_edca->ecw_min_max & 0xf); +		trig_based_txf[ac].cwmax = +			cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); +		trig_based_txf[ac].aifsn = +			cpu_to_le16(mu_edca->aifsn & 0xf); +		trig_based_txf[ac].mu_time = +			cpu_to_le16(mu_edca->mu_edca_timer); +	} + +	return mu_edca_enabled; +} + +bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +	const struct ieee80211_supported_band *sband; +	const struct ieee80211_sta_he_cap *own_he_cap = NULL; + +	/* This capability is the same for all bands, +	 * so take it from one of them. +	 */ +	sband = mvm->hw->wiphy->bands[NL80211_BAND_2GHZ]; +	own_he_cap = ieee80211_get_he_iftype_cap(sband, +						 ieee80211_vif_type_p2p(vif)); + +	return (own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] & +			       IEEE80211_HE_MAC_CAP2_ACK_EN)); +} + +__le32 iwl_mvm_get_sta_htc_flags(struct ieee80211_sta *sta, +				 struct ieee80211_link_sta *link_sta) +{ +	u8 *mac_cap_info = +		&link_sta->he_cap.he_cap_elem.mac_cap_info[0]; +	__le32 htc_flags = 0; + +	if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) +		htc_flags |= cpu_to_le32(IWL_HE_HTC_SUPPORT); +	if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) || +	    (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) { +		u8 link_adap = +			((mac_cap_info[2] & +			  IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) + +			 (mac_cap_info[1] & +			  IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION); + +		if (link_adap == 2) +			htc_flags |= +				cpu_to_le32(IWL_HE_HTC_LINK_ADAP_UNSOLICITED); +		else if (link_adap == 3) +			htc_flags |= cpu_to_le32(IWL_HE_HTC_LINK_ADAP_BOTH); +	} +	if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) +		htc_flags |= cpu_to_le32(IWL_HE_HTC_BSR_SUPP); +	if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) +		htc_flags |= cpu_to_le32(IWL_HE_HTC_OMI_SUPP); +	if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) +		htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP); + +	return htc_flags; +} +  static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  			       struct ieee80211_vif *vif, u8 sta_id)  { @@ -1990,11 +2344,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  	struct ieee80211_sta *sta;  	u32 flags;  	int i; -	const struct ieee80211_sta_he_cap *own_he_cap = NULL; -	struct ieee80211_chanctx_conf *chanctx_conf; -	const struct ieee80211_supported_band *sband;  	void *cmd; -	u8 nominal_padding;  	if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE))  		ver = 1; @@ -2020,16 +2370,6 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  	rcu_read_lock(); -	chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); -	if (WARN_ON(!chanctx_conf)) { -		rcu_read_unlock(); -		return; -	} - -	sband = mvm->hw->wiphy->bands[chanctx_conf->def.chan->band]; -	own_he_cap = ieee80211_get_he_iftype_cap(sband, -						 ieee80211_vif_type_p2p(vif)); -  	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_ctxt_cmd.sta_id]);  	if (IS_ERR_OR_NULL(sta)) {  		rcu_read_unlock(); @@ -2045,136 +2385,15 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  	flags = 0;  	/* Block 26-tone RU OFDMA transmissions */ -	if (mvmvif->he_ru_2mhz_block) +	if (mvmvif->deflink.he_ru_2mhz_block)  		flags |= STA_CTXT_HE_RU_2MHZ_BLOCK;  	/* HTC flags */ -	if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[0] & -	    IEEE80211_HE_MAC_CAP0_HTC_HE) -		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_SUPPORT); -	if ((sta->deflink.he_cap.he_cap_elem.mac_cap_info[1] & -	      IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) || -	    (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & -	      IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) { -		u8 link_adap = -			((sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & -			  IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) + -			 (sta->deflink.he_cap.he_cap_elem.mac_cap_info[1] & -			  IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION); - -		if (link_adap == 2) -			sta_ctxt_cmd.htc_flags |= -				cpu_to_le32(IWL_HE_HTC_LINK_ADAP_UNSOLICITED); -		else if (link_adap == 3) -			sta_ctxt_cmd.htc_flags |= -				cpu_to_le32(IWL_HE_HTC_LINK_ADAP_BOTH); -	} -	if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) -		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BSR_SUPP); -	if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[3] & -	    IEEE80211_HE_MAC_CAP3_OMI_CONTROL) -		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_OMI_SUPP); -	if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) -		sta_ctxt_cmd.htc_flags |= cpu_to_le32(IWL_HE_HTC_BQR_SUPP); - -	/* -	 * Initialize the PPE thresholds to "None" (7), as described in Table -	 * 9-262ac of 80211.ax/D3.0. -	 */ -	memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE, -	       sizeof(sta_ctxt_cmd.pkt_ext)); - -	if (sta->deflink.eht_cap.has_eht) { -		nominal_padding = -			u8_get_bits(sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5], -				    IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); - -		/* If PPE Thresholds exists, parse them into a FW-familiar format. */ -		if (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5] & -		    IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { -			u8 nss = (sta->deflink.eht_cap.eht_ppe_thres[0] & -				IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; -			u8 *ppe = &sta->deflink.eht_cap.eht_ppe_thres[0]; -			u8 ru_index_bitmap = -				u16_get_bits(*ppe, -					     IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); -			 /* Starting after PPE header */ -			u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; - -			iwl_mvm_parse_ppe(mvm, -					  &sta_ctxt_cmd.pkt_ext, -					  nss, ru_index_bitmap, ppe, -					  ppe_pos_bit, true); -			flags |= STA_CTXT_HE_PACKET_EXT; -		/* EHT PPE Thresholds doesn't exist - set the API according to HE PPE Tresholds*/ -		} else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & -			   IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { -			struct iwl_he_pkt_ext_v2 *pkt_ext = -				&sta_ctxt_cmd.pkt_ext; - -			/* -			* Even though HE Capabilities IE doesn't contain PPE -			* Thresholds for BW 320Mhz, thresholds for this BW will -			* be filled in with the same values as 160Mhz, due to -			* the inheritance, as required. -			*/ -			iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, pkt_ext, -							true); - -			/* -			* According to the requirements, for MCSs 12-13 the maximum value between -			* HE PPE Threshold and Common Nominal Packet Padding needs to be taken -			*/ -			iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding); - -			flags |= STA_CTXT_HE_PACKET_EXT; - -		/* -		* if PPE Thresholds doesn't present in both EHT IE and HE IE - -		* take the Thresholds from Common Nominal Packet Padding field -		*/ -		} else { -			iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, -								 nominal_padding, -								 &flags); -		} -	} else if (sta->deflink.he_cap.has_he) { -		/* If PPE Thresholds exist, parse them into a FW-familiar format. */ -		if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & -			IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { -			iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, -							&sta_ctxt_cmd.pkt_ext, -							false); -			flags |= STA_CTXT_HE_PACKET_EXT; -		/* -		* PPE Thresholds doesn't exist - set the API PPE values -		* according to Common Nominal Packet Padding field. -		*/ -		} else { -			nominal_padding = -				u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9], -					    IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); -			if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) -				iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, -									 nominal_padding, -									 &flags); -		} -	} +	sta_ctxt_cmd.htc_flags = iwl_mvm_get_sta_htc_flags(sta, &sta->deflink); -	for (i = 0; i < MAX_HE_SUPP_NSS; i++) { -		int bw; - -		for (bw = 0; -		     bw < ARRAY_SIZE(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i]); -		     bw++) { -			u8 *qam_th = -				&sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0]; - -			IWL_DEBUG_HT(mvm, -				     "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", -				     i, bw, qam_th[0], qam_th[1]); -		} -	} +	/* PPE Thresholds */ +	if (!iwl_mvm_set_sta_pkt_ext(mvm, &sta->deflink, &sta_ctxt_cmd.pkt_ext)) +		flags |= STA_CTXT_HE_PACKET_EXT;  	if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] &  	    IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP) @@ -2186,28 +2405,9 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  	rcu_read_unlock(); -	/* Mark MU EDCA as enabled, unless none detected on some AC */ -	flags |= STA_CTXT_HE_MU_EDCA_CW; -	for (i = 0; i < IEEE80211_NUM_ACS; i++) { -		struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = -			&mvmvif->queue_params[i].mu_edca_param_rec; -		u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); - -		if (!mvmvif->queue_params[i].mu_edca) { -			flags &= ~STA_CTXT_HE_MU_EDCA_CW; -			break; -		} - -		sta_ctxt_cmd.trig_based_txf[ac].cwmin = -			cpu_to_le16(mu_edca->ecw_min_max & 0xf); -		sta_ctxt_cmd.trig_based_txf[ac].cwmax = -			cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); -		sta_ctxt_cmd.trig_based_txf[ac].aifsn = -			cpu_to_le16(mu_edca->aifsn); -		sta_ctxt_cmd.trig_based_txf[ac].mu_time = -			cpu_to_le16(mu_edca->mu_edca_timer); -	} - +	if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif, +					  &sta_ctxt_cmd.trig_based_txf[0])) +		flags |= STA_CTXT_HE_MU_EDCA_CW;  	if (vif->bss_conf.uora_exists) {  		flags |= STA_CTXT_HE_TRIG_RND_ALLOC; @@ -2218,8 +2418,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  			(vif->bss_conf.uora_ocw_range >> 3) & 0x7;  	} -	if (own_he_cap && !(own_he_cap->he_cap_elem.mac_cap_info[2] & -			    IEEE80211_HE_MAC_CAP2_ACK_EN)) +	if (!iwl_mvm_is_nic_ack_enabled(mvm, vif))  		flags |= STA_CTXT_HE_NIC_NOT_ACK_ENABLED;  	if (vif->bss_conf.nontransmitted) { @@ -2279,9 +2478,8 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,  		IWL_ERR(mvm, "Failed to config FW to work HE!\n");  } -static void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, -				  struct ieee80211_vif *vif, -				  u32 duration_override) +void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			   u32 duration_override)  {  	u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;  	u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS; @@ -2307,6 +2505,87 @@ static void iwl_mvm_protect_assoc(struct iwl_mvm *mvm,  					min_duration, 500, false);  } +/* Handle association common part to MLD and non-MLD modes */ +void iwl_mvm_bss_info_changed_station_assoc(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif, +					    u64 changes) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	/* The firmware tracks the MU-MIMO group on its own. +	 * However, on HW restart we should restore this data. +	 */ +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && +	    (changes & BSS_CHANGED_MU_GROUPS) && vif->bss_conf.mu_mimo_owner) { +		ret = iwl_mvm_update_mu_groups(mvm, vif); +		if (ret) +			IWL_ERR(mvm, +				"failed to update VHT MU_MIMO groups\n"); +	} + +	iwl_mvm_recalc_multicast(mvm); + +	/* reset rssi values */ +	mvmvif->bf_data.ave_beacon_signal = 0; + +	iwl_mvm_bt_coex_vif_change(mvm); +	iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_TT, +					    IEEE80211_SMPS_AUTOMATIC); +	if (fw_has_capa(&mvm->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_UMAC_SCAN)) +		iwl_mvm_config_scan(mvm); +} + +/* Execute the common part for MLD and non-MLD modes */ +void +iwl_mvm_bss_info_changed_station_common(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					struct ieee80211_bss_conf *link_conf, +					u64 changes) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	if (changes & BSS_CHANGED_BEACON_INFO) { +		/* We received a beacon from the associated AP so +		 * remove the session protection. +		 */ +		iwl_mvm_stop_session_protection(mvm, vif); + +		iwl_mvm_sf_update(mvm, vif, false); +		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); +	} + +	if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS | +		       /* Send power command on every beacon change, +			* because we may have not enabled beacon abort yet. +			*/ +		       BSS_CHANGED_BEACON_INFO)) { +		ret = iwl_mvm_power_update_mac(mvm); +		if (ret) +			IWL_ERR(mvm, "failed to update power mode\n"); +	} + +	if (changes & BSS_CHANGED_CQM) { +		IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); +		/* reset cqm events tracking */ +		mvmvif->bf_data.last_cqm_event = 0; +		if (mvmvif->bf_data.bf_enabled) { +			/* FIXME: need to update per link when FW API will +			 * support it +			 */ +			ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); +			if (ret) +				IWL_ERR(mvm, +					"failed to update CQM thresholds\n"); +		} +	} + +	if (changes & BSS_CHANGED_BANDWIDTH) +		iwl_mvm_update_link_smps(vif, link_conf); +} +  static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  					     struct ieee80211_vif *vif,  					     struct ieee80211_bss_conf *bss_conf, @@ -2325,7 +2604,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  		     !iwlwifi_mod_params.disable_11ax) ||  		    (vif->bss_conf.eht_support &&  		     !iwlwifi_mod_params.disable_11be)) -			iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); +			iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id);  		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);  	} @@ -2337,7 +2616,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  	      !iwlwifi_mod_params.disable_11ax) ||  	     (vif->bss_conf.eht_support &&  	      !iwlwifi_mod_params.disable_11be))) -		iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); +		iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->deflink.ap_sta_id);  	/*  	 * If we're not associated yet, take the (new) BSSID before associating @@ -2346,22 +2625,22 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  	 * branch for disassociation below.  	 */  	if (changes & BSS_CHANGED_BSSID && !mvmvif->associated) -		memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); +		memcpy(mvmvif->deflink.bssid, bss_conf->bssid, ETH_ALEN); -	ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid); +	ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->deflink.bssid);  	if (ret)  		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);  	/* after sending it once, adopt mac80211 data */ -	memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); +	memcpy(mvmvif->deflink.bssid, bss_conf->bssid, ETH_ALEN);  	mvmvif->associated = vif->cfg.assoc;  	if (changes & BSS_CHANGED_ASSOC) {  		if (vif->cfg.assoc) {  			/* clear statistics to get clean beacon counter */  			iwl_mvm_request_statistics(mvm, true); -			memset(&mvmvif->beacon_stats, 0, -			       sizeof(mvmvif->beacon_stats)); +			memset(&mvmvif->deflink.beacon_stats, 0, +			       sizeof(mvmvif->deflink.beacon_stats));  			/* add quota for this interface */  			ret = iwl_mvm_update_quotas(mvm, true, NULL); @@ -2416,9 +2695,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  			if (vif->p2p) {  				iwl_mvm_update_smps(mvm, vif,  						    IWL_MVM_SMPS_REQ_PROT, -						    IEEE80211_SMPS_DYNAMIC); +						    IEEE80211_SMPS_DYNAMIC, 0);  			} -		} else if (mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { +		} else if (mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) {  			iwl_mvm_mei_host_disassociated(mvm);  			/*  			 * If update fails - SF might be running in associated @@ -2441,19 +2720,20 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  			if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,  				      &mvm->status)) {  				/* first remove remaining keys */ -				iwl_mvm_sec_key_remove_ap(mvm, vif); +				iwl_mvm_sec_key_remove_ap(mvm, vif, +							  &mvmvif->deflink, 0);  				/*  				 * Remove AP station now that  				 * the MAC is unassoc  				 */  				ret = iwl_mvm_rm_sta_id(mvm, vif, -							mvmvif->ap_sta_id); +							mvmvif->deflink.ap_sta_id);  				if (ret)  					IWL_ERR(mvm,  						"failed to remove AP station\n"); -				mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; +				mvmvif->deflink.ap_sta_id = IWL_MVM_INVALID_STA;  			}  			/* remove quota for this interface */ @@ -2469,67 +2749,52 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,  					vif->addr);  		} -		/* -		 * The firmware tracks the MU-MIMO group on its own. -		 * However, on HW restart we should restore this data. -		 */ -		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && -		    (changes & BSS_CHANGED_MU_GROUPS) && vif->bss_conf.mu_mimo_owner) { -			ret = iwl_mvm_update_mu_groups(mvm, vif); -			if (ret) -				IWL_ERR(mvm, -					"failed to update VHT MU_MIMO groups\n"); -		} +		iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); +	} -		iwl_mvm_recalc_multicast(mvm); +	iwl_mvm_bss_info_changed_station_common(mvm, vif, &vif->bss_conf, +						changes); +} -		/* reset rssi values */ -		mvmvif->bf_data.ave_beacon_signal = 0; +bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, +				  int *ret) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int i; -		iwl_mvm_bt_coex_vif_change(mvm); -		iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, -				    IEEE80211_SMPS_AUTOMATIC); -		if (fw_has_capa(&mvm->fw->ucode_capa, -				IWL_UCODE_TLV_CAPA_UMAC_SCAN)) -			iwl_mvm_config_scan(mvm); -	} +	lockdep_assert_held(&mvm->mutex); -	if (changes & BSS_CHANGED_BEACON_INFO) { -		/* -		 * We received a beacon from the associated AP so -		 * remove the session protection. -		 */ -		iwl_mvm_stop_session_protection(mvm, vif); +	mvmvif->ap_assoc_sta_count = 0; -		iwl_mvm_sf_update(mvm, vif, false); -		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); -	} +	/* must be set before quota calculations */ +	mvmvif->ap_ibss_active = true; -	if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS | -		       /* -			* Send power command on every beacon change, -			* because we may have not enabled beacon abort yet. -			*/ -		       BSS_CHANGED_BEACON_INFO)) { -		ret = iwl_mvm_power_update_mac(mvm); -		if (ret) -			IWL_ERR(mvm, "failed to update power mode\n"); +	/* send all the early keys to the device now */ +	for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) { +		struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i]; + +		if (!key) +			continue; + +		mvmvif->ap_early_keys[i] = NULL; + +		*ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key); +		if (*ret) +			return true;  	} -	if (changes & BSS_CHANGED_CQM) { -		IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); -		/* reset cqm events tracking */ -		mvmvif->bf_data.last_cqm_event = 0; -		if (mvmvif->bf_data.bf_enabled) { -			ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); -			if (ret) -				IWL_ERR(mvm, -					"failed to update CQM thresholds\n"); -		} +	if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) { +		iwl_mvm_vif_set_low_latency(mvmvif, true, +					    LOW_LATENCY_VIF_TYPE); +		iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);  	} -	if (changes & BSS_CHANGED_BANDWIDTH) -		iwl_mvm_apply_fw_smps_request(vif); +	/* power updated needs to be done before quotas */ +	iwl_mvm_power_update_mac(mvm); + +	return false;  }  static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, @@ -2538,15 +2803,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	int ret, i; +	int ret;  	mutex_lock(&mvm->mutex); -	/* Send the beacon template */ -	ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); -	if (ret) -		goto out_unlock; -  	/*  	 * Re-calculate the tsf id, as the leader-follower relations depend on  	 * the beacon interval, which was not known when the AP interface @@ -2555,12 +2815,31 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,  	if (vif->type == NL80211_IFTYPE_AP)  		iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); -	mvmvif->ap_assoc_sta_count = 0; +	/* For older devices need to send beacon template before adding mac +	 * context. For the newer, the beacon is a resource that belongs to a +	 * MAC, so need to send beacon template after adding the mac. +	 */ +	if (mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_22000) { +		/* Add the mac context */ +		ret = iwl_mvm_mac_ctxt_add(mvm, vif); +		if (ret) +			goto out_unlock; -	/* Add the mac context */ -	ret = iwl_mvm_mac_ctxt_add(mvm, vif); -	if (ret) -		goto out_unlock; +		/* Send the beacon template */ +		ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); +		if (ret) +			goto out_unlock; +	} else { +		/* Send the beacon template */ +		ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); +		if (ret) +			goto out_unlock; + +		/* Add the mac context */ +		ret = iwl_mvm_mac_ctxt_add(mvm, vif); +		if (ret) +			goto out_unlock; +	}  	/* Perform the binding */  	ret = iwl_mvm_binding_add_vif(mvm, vif); @@ -2602,35 +2881,12 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,  		}  	} -	/* must be set before quota calculations */ -	mvmvif->ap_ibss_active = true; - -	/* send all the early keys to the device now */ -	for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) { -		struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i]; - -		if (!key) -			continue; - -		mvmvif->ap_early_keys[i] = NULL; - -		ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key); -		if (ret) -			goto out_quota_failed; -	} - -	if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) { -		iwl_mvm_vif_set_low_latency(mvmvif, true, -					    LOW_LATENCY_VIF_TYPE); -		iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id); -	} - -	/* power updated needs to be done before quotas */ -	iwl_mvm_power_update_mac(mvm); +	if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret)) +		goto out_failed;  	ret = iwl_mvm_update_quotas(mvm, false, NULL);  	if (ret) -		goto out_quota_failed; +		goto out_failed;  	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */  	if (vif->p2p && mvm->p2p_device_vif) @@ -2646,7 +2902,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,  	goto out_unlock; -out_quota_failed: +out_failed:  	iwl_mvm_power_update_mac(mvm);  	mvmvif->ap_ibss_active = false;  	iwl_mvm_send_rm_bcast_sta(mvm, vif); @@ -2673,16 +2929,15 @@ static int iwl_mvm_start_ibss(struct ieee80211_hw *hw,  	return iwl_mvm_start_ap_ibss(hw, vif, &vif->bss_conf);  } -static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, -				 struct ieee80211_vif *vif, -				 struct ieee80211_bss_conf *link_conf) +/* Common part for MLD and non-MLD ops */ +void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif)  { -	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	iwl_mvm_prepare_mac_removal(mvm, vif); +	lockdep_assert_held(&mvm->mutex); -	mutex_lock(&mvm->mutex); +	iwl_mvm_prepare_mac_removal(mvm, vif);  	/* Handle AP stop while in CSA */  	if (rcu_access_pointer(mvm->csa_vif) == vif) { @@ -2707,6 +2962,17 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,  	}  	iwl_mvm_bt_coex_vif_change(mvm); +} + +static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); + +	iwl_mvm_stop_ap_ibss_common(mvm, vif);  	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */  	if (vif->p2p && mvm->p2p_device_vif) @@ -2770,7 +3036,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,  	/* Need to send a new beacon template to the FW */  	if (changes & BSS_CHANGED_BEACON && -	    iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) +	    iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, &vif->bss_conf))  		IWL_WARN(mvm, "Failed updating beacon data\n");  	if (changes & BSS_CHANGED_FTM_RESPONDER) { @@ -2788,6 +3054,22 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,  				     struct ieee80211_bss_conf *bss_conf,  				     u64 changes)  { +	struct iwl_mvm_bss_info_changed_ops callbacks = { +		.bss_info_changed_sta = iwl_mvm_bss_info_changed_station, +		.bss_info_changed_ap_ibss = iwl_mvm_bss_info_changed_ap_ibss, +	}; + +	iwl_mvm_bss_info_changed_common(hw, vif, bss_conf, &callbacks, +					changes); +} + +void +iwl_mvm_bss_info_changed_common(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *bss_conf, +				struct iwl_mvm_bss_info_changed_ops *callbacks, +				u64 changes) +{  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	mutex_lock(&mvm->mutex); @@ -2797,11 +3079,12 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,  	switch (vif->type) {  	case NL80211_IFTYPE_STATION: -		iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); +		callbacks->bss_info_changed_sta(mvm, vif, bss_conf, changes);  		break;  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_ADHOC: -		iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); +		callbacks->bss_info_changed_ap_ibss(mvm, vif, bss_conf, +						    changes);  		break;  	case NL80211_IFTYPE_MONITOR:  		if (changes & BSS_CHANGED_MU_GROUPS) @@ -2821,9 +3104,8 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, -			       struct ieee80211_vif *vif, -			       struct ieee80211_scan_request *hw_req) +int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			struct ieee80211_scan_request *hw_req)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -2839,8 +3121,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,  	return ret;  } -static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif) +void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -2859,7 +3141,7 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static void +void  iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,  				  struct ieee80211_sta *sta, u16 tids,  				  int num_frames, @@ -2874,7 +3156,7 @@ iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,  					  tids, more_data, false);  } -static void +void  iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,  				    struct ieee80211_sta *sta, u16 tids,  				    int num_frames, @@ -2935,7 +3217,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,  		 */  		break;  	case STA_NOTIFY_AWAKE: -		if (WARN_ON(mvmsta->sta_id == IWL_MVM_INVALID_STA)) +		if (WARN_ON(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA))  			break;  		if (txqs) @@ -2948,10 +3230,8 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,  	spin_unlock_bh(&mvmsta->lock);  } -static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, -				   struct ieee80211_vif *vif, -				   enum sta_notify_cmd cmd, -				   struct ieee80211_sta *sta) +void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			    enum sta_notify_cmd cmd, struct ieee80211_sta *sta)  {  	__iwl_mvm_mac_sta_notify(hw, cmd, sta);  } @@ -3009,12 +3289,13 @@ void iwl_mvm_sta_pm_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)  	rcu_read_unlock();  } -static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif, -				       struct ieee80211_sta *sta) +void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	unsigned int link_id;  	/*  	 * This is called before mac80211 does RCU synchronisation, @@ -3023,12 +3304,26 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,  	 * be able to find the station this way, and we don't rely  	 * on further RCU synchronisation after the sta_state()  	 * callback deleted the station. +	 * Since there's mvm->mutex here, no need to have RCU lock for +	 * mvm_sta->link access.  	 */  	mutex_lock(&mvm->mutex); -	if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) -		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], -				   ERR_PTR(-ENOENT)); +	for (link_id = 0; link_id < ARRAY_SIZE(mvm_sta->link); link_id++) { +		struct iwl_mvm_link_sta *link_sta; +		u32 sta_id; + +		if (!mvm_sta->link[link_id]) +			continue; +		link_sta = rcu_dereference_protected(mvm_sta->link[link_id], +						     lockdep_is_held(&mvm->mutex)); +		sta_id = link_sta->sta_id; +		if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) { +			RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], +					 ERR_PTR(-ENOENT)); +			RCU_INIT_POINTER(mvm->fw_id_to_link_sta[sta_id], NULL); +		} +	}  	mutex_unlock(&mvm->mutex);  } @@ -3121,20 +3416,27 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,  	rcu_read_unlock();  } -static void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, -					       struct ieee80211_vif *vif) +static void +iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, +				   struct ieee80211_vif *vif, +				   unsigned int link_id, +				   struct ieee80211_bss_conf *link_conf)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_he_obss_narrow_bw_ru_data iter_data = {  		.tolerated = true,  	}; -	if (!(vif->bss_conf.chandef.chan->flags & IEEE80211_CHAN_RADAR)) { -		mvmvif->he_ru_2mhz_block = false; +	if (WARN_ON_ONCE(!link_conf->chandef.chan || +			 !mvmvif->link[link_id])) +		return; + +	if (!(link_conf->chandef.chan->flags & IEEE80211_CHAN_RADAR)) { +		mvmvif->link[link_id]->he_ru_2mhz_block = false;  		return;  	} -	cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chandef, +	cfg80211_bss_iter(hw->wiphy, &link_conf->chandef,  			  iwl_mvm_check_he_obss_narrow_bw_ru_iter,  			  &iter_data); @@ -3142,7 +3444,7 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,  	 * If there is at least one AP on radar channel that cannot  	 * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU.  	 */ -	mvmvif->he_ru_2mhz_block = !iter_data.tolerated; +	mvmvif->link[link_id]->he_ru_2mhz_block = !iter_data.tolerated;  }  static void iwl_mvm_reset_cca_40mhz_workaround(struct iwl_mvm *mvm, @@ -3186,7 +3488,6 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mei_conn_info conn_info = {  		.ssid_len = vif->cfg.ssid_len, -		.channel = vif->bss_conf.chandef.chan->hw_value,  	};  	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) @@ -3195,6 +3496,12 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,  	if (!mvm->mei_registered)  		return; +	/* FIXME: MEI needs to be updated for MLO */ +	if (!vif->bss_conf.chandef.chan) +		return; + +	conn_info.channel = vif->bss_conf.chandef.chan->hw_value; +  	switch (mvm_sta->pairwise_cipher) {  	case WLAN_CIPHER_SUITE_TKIP:  		conn_info.pairwise_cipher = IWL_MEI_CIPHER_TKIP; @@ -3244,24 +3551,309 @@ static void iwl_mvm_mei_host_associated(struct iwl_mvm *mvm,  #endif  } +static int iwl_mvm_mac_ctxt_changed_wrapper(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif, +					    bool force_assoc_off) +{ +	return iwl_mvm_mac_ctxt_changed(mvm, vif, force_assoc_off, NULL); +} +  static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  				 struct ieee80211_vif *vif,  				 struct ieee80211_sta *sta,  				 enum ieee80211_sta_state old_state,  				 enum ieee80211_sta_state new_state)  { +	struct iwl_mvm_sta_state_ops callbacks = { +		.add_sta = iwl_mvm_add_sta, +		.update_sta = iwl_mvm_update_sta, +		.rm_sta = iwl_mvm_rm_sta, +		.mac_ctxt_changed = iwl_mvm_mac_ctxt_changed_wrapper, +	}; + +	return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state, +					    &callbacks); +} + +/* FIXME: temporary making two assumptions in all sta handling functions: + *	(1) when setting sta state, the link exists and protected + *	(2) if a link is valid in sta then it's valid in vif (can + *	use same index in the link array) + */ +static void iwl_mvm_rs_rate_init_all_links(struct iwl_mvm *mvm, +					   struct ieee80211_vif *vif, +					   struct ieee80211_sta *sta) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id; + +	for_each_mvm_vif_valid_link(mvmvif, link_id) { +		struct ieee80211_bss_conf *conf = +			link_conf_dereference_check(vif, link_id); +		struct ieee80211_link_sta *link_sta = +			link_sta_dereference_check(sta, link_id); + +		if (!conf || !link_sta || !mvmvif->link[link_id]->phy_ctxt) +			continue; + +		iwl_mvm_rs_rate_init(mvm, vif, sta, conf, link_sta, +				     mvmvif->link[link_id]->phy_ctxt->channel->band); +	} +} + +#define IWL_MVM_MIN_BEACON_INTERVAL_TU 16 + +static bool iwl_mvm_vif_conf_from_sta(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_sta *sta) +{ +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; + +	/* Beacon interval check - firmware will crash if the beacon +	 * interval is less than 16. We can't avoid connecting at all, +	 * so refuse the station state change, this will cause mac80211 +	 * to abandon attempts to connect to this AP, and eventually +	 * wpa_s will blocklist the AP... +	 */ + +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); + +		if (!link_conf) +			continue; + +		if (link_conf->beacon_int < IWL_MVM_MIN_BEACON_INTERVAL_TU) { +			IWL_ERR(mvm, +				"Beacon interval %d for AP %pM is too small\n", +				link_conf->beacon_int, link_sta->addr); +			return false; +		} + +		link_conf->he_support = link_sta->he_cap.has_he; +	} + +	return true; +} + +static void iwl_mvm_vif_set_he_support(struct ieee80211_hw *hw, +				       struct ieee80211_vif *vif, +				       struct ieee80211_sta *sta, +				       bool is_sta) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; + +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); + +		if (!link_conf || !mvmvif->link[link_id]) +			continue; + +		link_conf->he_support = link_sta->he_cap.has_he; + +		if (is_sta) { +			mvmvif->link[link_id]->he_ru_2mhz_block = false; +			if (link_sta->he_cap.has_he) +				iwl_mvm_check_he_obss_narrow_bw_ru(hw, vif, +								   link_id, +								   link_conf); +		} +	} +} + +static int +iwl_mvm_sta_state_notexist_to_none(struct iwl_mvm *mvm, +				   struct ieee80211_vif *vif, +				   struct ieee80211_sta *sta, +				   struct iwl_mvm_sta_state_ops *callbacks) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_link_sta *link_sta; +	unsigned int i; +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	if (vif->type == NL80211_IFTYPE_STATION && +	    !iwl_mvm_vif_conf_from_sta(mvm, vif, sta)) +		return -EINVAL; + +	if (sta->tdls && +	    (vif->p2p || +	     iwl_mvm_tdls_sta_count(mvm, NULL) == IWL_MVM_TDLS_STA_COUNT || +	     iwl_mvm_phy_ctx_count(mvm) > 1)) { +		IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); +		return -EBUSY; +	} + +	ret = callbacks->add_sta(mvm, vif, sta); +	if (sta->tdls && ret == 0) { +		iwl_mvm_recalc_tdls_state(mvm, vif, true); +		iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, +					   NL80211_TDLS_SETUP); +	} + +	for_each_sta_active_link(vif, sta, link_sta, i) +		link_sta->agg.max_rc_amsdu_len = 1; + +	ieee80211_sta_recalc_aggregates(sta); + +	if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) +		mvmvif->ap_sta = sta; + +	return 0; +} + +static int +iwl_mvm_sta_state_auth_to_assoc(struct ieee80211_hw *hw, +				struct iwl_mvm *mvm, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				struct iwl_mvm_sta_state_ops *callbacks) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; + +	lockdep_assert_held(&mvm->mutex); + +	if (vif->type == NL80211_IFTYPE_AP) { +		iwl_mvm_vif_set_he_support(hw, vif, sta, false); +		mvmvif->ap_assoc_sta_count++; +		callbacks->mac_ctxt_changed(mvm, vif, false); + +		/* since the below is not for MLD API, it's ok to use +		 * the default bss_conf +		 */ +		if (!mvm->mld_api_is_used && +		    ((vif->bss_conf.he_support && +		      !iwlwifi_mod_params.disable_11ax) || +		    (vif->bss_conf.eht_support && +		     !iwlwifi_mod_params.disable_11be))) +			iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->deflink.sta_id); +	} else if (vif->type == NL80211_IFTYPE_STATION) { +		iwl_mvm_vif_set_he_support(hw, vif, sta, true); + +		callbacks->mac_ctxt_changed(mvm, vif, false); + +		if (!mvm->mld_api_is_used) +			goto out; + +		for_each_sta_active_link(vif, sta, link_sta, link_id) { +			struct ieee80211_bss_conf *link_conf = +				link_conf_dereference_protected(vif, link_id); + +			if (WARN_ON(!link_conf)) +				return -EINVAL; +			if (!mvmvif->link[link_id]) +				continue; + +			iwl_mvm_link_changed(mvm, vif, link_conf, +					     LINK_CONTEXT_MODIFY_ALL & +					     ~LINK_CONTEXT_MODIFY_ACTIVE, +					     true); +		} +	} + +out: +	iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); + +	return callbacks->update_sta(mvm, vif, sta); +} + +static int +iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_sta *sta, +				      struct iwl_mvm_sta_state_ops *callbacks) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + +	lockdep_assert_held(&mvm->mutex); + +	/* we don't support TDLS during DCM */ +	if (iwl_mvm_phy_ctx_count(mvm) > 1) +		iwl_mvm_teardown_tdls_peers(mvm); + +	if (sta->tdls) { +		iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, +					   NL80211_TDLS_ENABLE_LINK); +	} else { +		/* enable beacon filtering */ +		WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + +		mvmvif->authorized = 1; + +		callbacks->mac_ctxt_changed(mvm, vif, false); +		iwl_mvm_mei_host_associated(mvm, vif, mvm_sta); +	} + +	mvm_sta->authorized = true; + +	iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); + +	return 0; +} + +static int +iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_sta *sta, +				      struct iwl_mvm_sta_state_ops *callbacks) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	mvmsta->authorized = false; + +	/* once we move into assoc state, need to update rate scale to +	 * disable using wide bandwidth +	 */ +	iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); + +	if (!sta->tdls) { +		/* Set this but don't call iwl_mvm_mac_ctxt_changed() +		 * yet to avoid sending high prio again for a little +		 * time. +		 */ +		mvmvif->authorized = 0; + +		/* disable beacon filtering */ +		ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); +		WARN_ON(ret && +			!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, +				  &mvm->status)); +	} + +	return 0; +} + +/* Common part for MLD and non-MLD modes */ +int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_sta *sta, +				 enum ieee80211_sta_state old_state, +				 enum ieee80211_sta_state new_state, +				 struct iwl_mvm_sta_state_ops *callbacks) +{  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	unsigned int link_id;  	int ret;  	IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",  			   sta->addr, old_state, new_state); -	/* this would be a mac80211 bug ... but don't crash */ -	if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) -		return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) ? 0 : -EINVAL; -  	/*  	 * If we are in a STA removal flow and in DQA mode:  	 * @@ -3289,51 +3881,31 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  		 * from the AP now.  		 */  		iwl_mvm_reset_cca_40mhz_workaround(mvm, vif); + +		/* Also free dup data just in case any assertions below fail */ +		kfree(mvm_sta->dup_data);  	}  	mutex_lock(&mvm->mutex); + +	/* this would be a mac80211 bug ... but don't crash */ +	for_each_mvm_vif_valid_link(mvmvif, link_id) { +		if (WARN_ON_ONCE(!mvmvif->link[link_id]->phy_ctxt)) { +			mutex_unlock(&mvm->mutex); +			return test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, +					&mvm->status) ? 0 : -EINVAL; +		} +	} +  	/* track whether or not the station is associated */  	mvm_sta->sta_state = new_state;  	if (old_state == IEEE80211_STA_NOTEXIST &&  	    new_state == IEEE80211_STA_NONE) { -		/* -		 * Firmware bug - it'll crash if the beacon interval is less -		 * than 16. We can't avoid connecting at all, so refuse the -		 * station state change, this will cause mac80211 to abandon -		 * attempts to connect to this AP, and eventually wpa_s will -		 * blocklist the AP... -		 */ -		if (vif->type == NL80211_IFTYPE_STATION && -		    vif->bss_conf.beacon_int < 16) { -			IWL_ERR(mvm, -				"AP %pM beacon interval is %d, refusing due to firmware bug!\n", -				sta->addr, vif->bss_conf.beacon_int); -			ret = -EINVAL; +		ret = iwl_mvm_sta_state_notexist_to_none(mvm, vif, sta, +							 callbacks); +		if (ret < 0)  			goto out_unlock; -		} - -		if (vif->type == NL80211_IFTYPE_STATION) -			vif->bss_conf.he_support = sta->deflink.he_cap.has_he; - -		if (sta->tdls && -		    (vif->p2p || -		     iwl_mvm_tdls_sta_count(mvm, NULL) == -						IWL_MVM_TDLS_STA_COUNT || -		     iwl_mvm_phy_ctx_count(mvm) > 1)) { -			IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); -			ret = -EBUSY; -			goto out_unlock; -		} - -		ret = iwl_mvm_add_sta(mvm, vif, sta); -		if (sta->tdls && ret == 0) { -			iwl_mvm_recalc_tdls_state(mvm, vif, true); -			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, -						   NL80211_TDLS_SETUP); -		} - -		sta->deflink.agg.max_rc_amsdu_len = 1;  	} else if (old_state == IEEE80211_STA_NONE &&  		   new_state == IEEE80211_STA_AUTH) {  		/* @@ -3345,85 +3917,21 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  		ret = 0;  	} else if (old_state == IEEE80211_STA_AUTH &&  		   new_state == IEEE80211_STA_ASSOC) { -		if (vif->type == NL80211_IFTYPE_AP) { -			vif->bss_conf.he_support = sta->deflink.he_cap.has_he; -			mvmvif->ap_assoc_sta_count++; -			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); -			if ((vif->bss_conf.he_support && -			     !iwlwifi_mod_params.disable_11ax) || -			    (vif->bss_conf.eht_support && -			     !iwlwifi_mod_params.disable_11be)) -				iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id); -		} else if (vif->type == NL80211_IFTYPE_STATION) { -			vif->bss_conf.he_support = sta->deflink.he_cap.has_he; - -			mvmvif->he_ru_2mhz_block = false; -			if (sta->deflink.he_cap.has_he) -				iwl_mvm_check_he_obss_narrow_bw_ru(hw, vif); - -			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); -		} - -		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, -				     false); -		ret = iwl_mvm_update_sta(mvm, vif, sta); +		ret = iwl_mvm_sta_state_auth_to_assoc(hw, mvm, vif, sta, +						      callbacks);  	} else if (old_state == IEEE80211_STA_ASSOC &&  		   new_state == IEEE80211_STA_AUTHORIZED) { -		ret = 0; - -		/* we don't support TDLS during DCM */ -		if (iwl_mvm_phy_ctx_count(mvm) > 1) -			iwl_mvm_teardown_tdls_peers(mvm); - -		if (sta->tdls) { -			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, -						   NL80211_TDLS_ENABLE_LINK); -		} else { -			/* enable beacon filtering */ -			WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - -			mvmvif->authorized = 1; - -			/* -			 * Now that the station is authorized, i.e., keys were already -			 * installed, need to indicate to the FW that -			 * multicast data frames can be forwarded to the driver -			 */ -			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); -			iwl_mvm_mei_host_associated(mvm, vif, mvm_sta); -		} - -		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, -				     true); +		ret = iwl_mvm_sta_state_assoc_to_authorized(mvm, vif, sta, +							    callbacks);  	} else if (old_state == IEEE80211_STA_AUTHORIZED &&  		   new_state == IEEE80211_STA_ASSOC) { -		/* once we move into assoc state, need to update rate scale to -		 * disable using wide bandwidth -		 */ -		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, -				     false); -		if (!sta->tdls) { -			/* Multicast data frames are no longer allowed */ -			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - -			/* -			 * Set this after the above iwl_mvm_mac_ctxt_changed() -			 * to avoid sending high prio again for a little time. -			 */ -			mvmvif->authorized = 0; - -			/* disable beacon filtering */ -			ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); -			WARN_ON(ret && -				!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, -					  &mvm->status)); -		} -		ret = 0; +		ret = iwl_mvm_sta_state_authorized_to_assoc(mvm, vif, sta, +							    callbacks);  	} else if (old_state == IEEE80211_STA_ASSOC &&  		   new_state == IEEE80211_STA_AUTH) {  		if (vif->type == NL80211_IFTYPE_AP) {  			mvmvif->ap_assoc_sta_count--; -			iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); +			callbacks->mac_ctxt_changed(mvm, vif, false);  		} else if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)  			iwl_mvm_stop_session_protection(mvm, vif);  		ret = 0; @@ -3432,9 +3940,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  		ret = 0;  	} else if (old_state == IEEE80211_STA_NONE &&  		   new_state == IEEE80211_STA_NOTEXIST) { -		if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) +		if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {  			iwl_mvm_stop_session_protection(mvm, vif); -		ret = iwl_mvm_rm_sta(mvm, vif, sta); +			mvmvif->ap_sta = NULL; +		} +		ret = callbacks->rm_sta(mvm, vif, sta);  		if (sta->tdls) {  			iwl_mvm_recalc_tdls_state(mvm, vif, false);  			iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, @@ -3463,7 +3973,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,  	return ret;  } -static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -3472,18 +3982,15 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)  	return 0;  } -static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, -				  struct ieee80211_vif *vif, -				  struct ieee80211_sta *sta, u32 changed) +void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			   struct ieee80211_sta *sta, u32 changed)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); -	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	if (changed & (IEEE80211_RC_BW_CHANGED |  		       IEEE80211_RC_SUPP_RATES_CHANGED |  		       IEEE80211_RC_NSS_CHANGED)) -		iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, -				     true); +		iwl_mvm_rs_rate_init_all_links(mvm, vif, sta);  	if (vif->type == NL80211_IFTYPE_STATION &&  	    changed & IEEE80211_RC_NSS_CHANGED) @@ -3498,7 +4005,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	mvmvif->queue_params[ac] = *params; +	mvmvif->deflink.queue_params[ac] = *params;  	/*  	 * No need to update right away, we'll get BSS_CHANGED_QOS @@ -3515,9 +4022,9 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,  	return 0;  } -static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif, -				       struct ieee80211_prep_tx_info *info) +void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_prep_tx_info *info)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -3526,9 +4033,9 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw, -					struct ieee80211_vif *vif, -					struct ieee80211_prep_tx_info *info) +void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_prep_tx_info *info)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -3541,10 +4048,10 @@ static void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, -					struct ieee80211_vif *vif, -					struct cfg80211_sched_scan_request *req, -					struct ieee80211_scan_ies *ies) +int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct cfg80211_sched_scan_request *req, +				 struct ieee80211_scan_ies *ies)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -3564,8 +4071,8 @@ out:  	return ret;  } -static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif) +int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -3601,7 +4108,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_sta *mvmsta = NULL; -	struct iwl_mvm_key_pn *ptk_pn; +	struct iwl_mvm_key_pn *ptk_pn = NULL;  	int keyidx = key->keyidx;  	u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);  	u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); @@ -3648,7 +4155,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  	switch (cmd) {  	case SET_KEY: -		if (keyidx == 6 || keyidx == 7) +		if (vif->type == NL80211_IFTYPE_STATION && +		    (keyidx == 6 || keyidx == 7))  			rcu_assign_pointer(mvmvif->bcn_prot.keys[keyidx - 6],  					   key); @@ -3659,10 +4167,14 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  			 * on IBSS they're per-station and because we're lazy  			 * we don't support them for RX, so do the same.  			 * CMAC/GMAC in AP/IBSS modes must be done in software. +			 * +			 * Except, of course, beacon protection - it must be +			 * offloaded since we just set a beacon template.  			 */ -			if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || -			    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || -			    key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) { +			if (keyidx < 6 && +			    (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || +			     key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || +			     key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)) {  				ret = -EOPNOTSUPP;  				break;  			} @@ -3743,7 +4255,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  		if (mvmsta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE)  			mvmsta->pairwise_cipher = key->cipher; -		IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); +		IWL_DEBUG_MAC80211(mvm, "set hwcrypto key (sta:%pM, id:%d)\n", +				   sta ? sta->addr : NULL, key->keyidx);  		if (sec_key_ver)  			ret = iwl_mvm_sec_key_add(mvm, vif, sta, key); @@ -3753,6 +4266,10 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  		if (ret) {  			IWL_WARN(mvm, "set key failed\n");  			key->hw_key_idx = STA_KEY_IDX_INVALID; +			if (ptk_pn) { +				RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL); +				kfree(ptk_pn); +			}  			/*  			 * can't add key for RX, but we don't need it  			 * in the device for TX so still return 0, @@ -3767,7 +4284,8 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  		break;  	case DISABLE_KEY: -		if (keyidx == 6 || keyidx == 7) +		if (vif->type == NL80211_IFTYPE_STATION && +		    (keyidx == 6 || keyidx == 7))  			RCU_INIT_POINTER(mvmvif->bcn_prot.keys[keyidx - 6],  					 NULL); @@ -3814,11 +4332,9 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  	return ret;  } -static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, -			       enum set_key_cmd cmd, -			       struct ieee80211_vif *vif, -			       struct ieee80211_sta *sta, -			       struct ieee80211_key_conf *key) +int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, +			struct ieee80211_vif *vif, struct ieee80211_sta *sta, +			struct ieee80211_key_conf *key)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -3830,11 +4346,11 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,  	return ret;  } -static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, -					struct ieee80211_vif *vif, -					struct ieee80211_key_conf *keyconf, -					struct ieee80211_sta *sta, -					u32 iv32, u16 *phase1key) +void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_key_conf *keyconf, +				 struct ieee80211_sta *sta, +				 u32 iv32, u16 *phase1key)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -4005,18 +4521,79 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,  	return res;  } +static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) +{ +	int ret = 0; + +	lockdep_assert_held(&mvm->mutex); + +	if (!fw_has_capa(&mvm->fw->ucode_capa, +			 IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { +		IWL_ERR(mvm, "hotspot not supported\n"); +		return -EINVAL; +	} + +	if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12) { +		ret = iwl_mvm_add_aux_sta(mvm, lmac_id); +		WARN(ret, "Failed to allocate aux station"); +	} + +	return ret; +} + +static int iwl_mvm_roc_switch_binding(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct iwl_mvm_phy_ctxt *new_phy_ctxt) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret = 0; + +	lockdep_assert_held(&mvm->mutex); + +	/* Unbind the P2P_DEVICE from the current PHY context, +	 * and if the PHY context is not used remove it. +	 */ +	ret = iwl_mvm_binding_remove_vif(mvm, vif); +	if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) +		return ret; + +	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + +	/* Bind the P2P_DEVICE to the current PHY Context */ +	mvmvif->deflink.phy_ctxt = new_phy_ctxt; + +	ret = iwl_mvm_binding_add_vif(mvm, vif); +	WARN(ret, "Failed binding P2P_DEVICE\n"); +	return ret; +} +  static int iwl_mvm_roc(struct ieee80211_hw *hw,  		       struct ieee80211_vif *vif,  		       struct ieee80211_channel *channel,  		       int duration,  		       enum ieee80211_roc_type type)  { +	struct iwl_mvm_roc_ops ops = { +		.add_aux_sta_for_hs20 = iwl_mvm_add_aux_sta_for_hs20, +		.switch_phy_ctxt = iwl_mvm_roc_switch_binding, +	}; + +	return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); +} + +/* Execute the common part for MLD and non-MLD modes */ +int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       struct ieee80211_channel *channel, int duration, +		       enum ieee80211_roc_type type, +		       struct iwl_mvm_roc_ops *ops) +{  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct cfg80211_chan_def chandef;  	struct iwl_mvm_phy_ctxt *phy_ctxt;  	bool band_change_removal;  	int ret, i; +	u32 lmac_id;  	IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,  			   duration, type); @@ -4031,25 +4608,13 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,  	switch (vif->type) {  	case NL80211_IFTYPE_STATION: -		if (fw_has_capa(&mvm->fw->ucode_capa, -				IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { -			/* Use aux roc framework (HS20) */ -			if (iwl_fw_lookup_cmd_ver(mvm->fw, ADD_STA, 0) >= 12) { -				u32 lmac_id; - -				lmac_id = iwl_mvm_get_lmac_id(mvm->fw, -							      channel->band); -				ret = iwl_mvm_add_aux_sta(mvm, lmac_id); -				if (WARN(ret, -					 "Failed to allocate aux station")) -					goto out_unlock; -			} +		lmac_id = iwl_mvm_get_lmac_id(mvm->fw, channel->band); + +		/* Use aux roc framework (HS20) */ +		ret = ops->add_aux_sta_for_hs20(mvm, lmac_id); +		if (!ret)  			ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,  						       vif, duration); -			goto out_unlock; -		} -		IWL_ERR(mvm, "hotspot not supported\n"); -		ret = -EINVAL;  		goto out_unlock;  	case NL80211_IFTYPE_P2P_DEVICE:  		/* handle below */ @@ -4062,34 +4627,21 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,  	for (i = 0; i < NUM_PHY_CTX; i++) {  		phy_ctxt = &mvm->phy_ctxts[i]; -		if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) +		if (phy_ctxt->ref == 0 || mvmvif->deflink.phy_ctxt == phy_ctxt)  			continue;  		if (phy_ctxt->ref && channel == phy_ctxt->channel) { -			/* -			 * Unbind the P2P_DEVICE from the current PHY context, -			 * and if the PHY context is not used remove it. -			 */ -			ret = iwl_mvm_binding_remove_vif(mvm, vif); -			if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) -				goto out_unlock; - -			iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - -			/* Bind the P2P_DEVICE to the current PHY Context */ -			mvmvif->phy_ctxt = phy_ctxt; - -			ret = iwl_mvm_binding_add_vif(mvm, vif); -			if (WARN(ret, "Failed binding P2P_DEVICE\n")) +			ret = ops->switch_phy_ctxt(mvm, vif, phy_ctxt); +			if (ret)  				goto out_unlock; -			iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); +			iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt);  			goto schedule_time_event;  		}  	}  	/* Need to update the PHY context only if the ROC channel changed */ -	if (channel == mvmvif->phy_ctxt->channel) +	if (channel == mvmvif->deflink.phy_ctxt->channel)  		goto schedule_time_event;  	cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); @@ -4103,14 +4655,14 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,  	band_change_removal =  		fw_has_capa(&mvm->fw->ucode_capa,  			    IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) && -		mvmvif->phy_ctxt->channel->band != chandef.chan->band; +		mvmvif->deflink.phy_ctxt->channel->band != chandef.chan->band; -	if (mvmvif->phy_ctxt->ref == 1 && !band_change_removal) { +	if (mvmvif->deflink.phy_ctxt->ref == 1 && !band_change_removal) {  		/*  		 * Change the PHY context configuration as it is currently  		 * referenced only by the P2P Device MAC (and we can modify it)  		 */ -		ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, +		ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->deflink.phy_ctxt,  					       &chandef, 1, 1);  		if (ret)  			goto out_unlock; @@ -4133,21 +4685,11 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,  			goto out_unlock;  		} -		/* Unbind the P2P_DEVICE from the current PHY context */ -		ret = iwl_mvm_binding_remove_vif(mvm, vif); -		if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) -			goto out_unlock; - -		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - -		/* Bind the P2P_DEVICE to the new allocated PHY context */ -		mvmvif->phy_ctxt = phy_ctxt; - -		ret = iwl_mvm_binding_add_vif(mvm, vif); -		if (WARN(ret, "Failed binding P2P_DEVICE\n")) +		ret = ops->switch_phy_ctxt(mvm, vif, phy_ctxt); +		if (ret)  			goto out_unlock; -		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); +		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt);  	}  schedule_time_event: @@ -4160,8 +4702,8 @@ out_unlock:  	return ret;  } -static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw, -			      struct ieee80211_vif *vif) +int iwl_mvm_cancel_roc(struct ieee80211_hw *hw, +		       struct ieee80211_vif *vif)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -4238,8 +4780,8 @@ out:  	return ret;  } -static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, -			       struct ieee80211_chanctx_conf *ctx) +int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, +			struct ieee80211_chanctx_conf *ctx)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -4262,8 +4804,8 @@ static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,  	iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);  } -static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, -				   struct ieee80211_chanctx_conf *ctx) +void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, +			    struct ieee80211_chanctx_conf *ctx)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -4272,9 +4814,8 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,  	mutex_unlock(&mvm->mutex);  } -static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, -				   struct ieee80211_chanctx_conf *ctx, -				   u32 changed) +void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, +			    struct ieee80211_chanctx_conf *ctx, u32 changed)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; @@ -4313,19 +4854,25 @@ out_unlock:  	mutex_unlock(&mvm->mutex);  } -static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, -					struct ieee80211_vif *vif, -					struct ieee80211_chanctx_conf *ctx, -					bool switching_chanctx) +/* + * This function executes the common part for MLD and non-MLD modes. + * + * Returns true if we're done assigning the chanctx + * (either on failure or success) + */ +static bool +__iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, +				    struct ieee80211_vif *vif, +				    struct ieee80211_chanctx_conf *ctx, +				    bool switching_chanctx, int *ret)  {  	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;  	struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	int ret;  	lockdep_assert_held(&mvm->mutex); -	mvmvif->phy_ctxt = phy_ctxt; +	mvmvif->deflink.phy_ctxt = phy_ctxt;  	switch (vif->type) {  	case NL80211_IFTYPE_AP: @@ -4340,19 +4887,36 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,  		 * The AP binding flow is handled as part of the start_ap flow  		 * (in bss_info_changed), similarly for IBSS.  		 */ -		ret = 0; -		goto out; +		*ret = 0; +		return true;  	case NL80211_IFTYPE_STATION: -		mvmvif->csa_bcn_pending = false;  		break;  	case NL80211_IFTYPE_MONITOR:  		/* always disable PS when a monitor interface is active */  		mvmvif->ps_disabled = true;  		break;  	default: -		ret = -EINVAL; -		goto out; +		*ret = -EINVAL; +		return true;  	} +	return false; +} + +static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					struct ieee80211_bss_conf *link_conf, +					struct ieee80211_chanctx_conf *ctx, +					bool switching_chanctx) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	if (WARN_ON(!link_conf)) +		return -EINVAL; + +	if (__iwl_mvm_assign_vif_chanctx_common(mvm, vif, ctx, +						switching_chanctx, &ret)) +		goto out;  	ret = iwl_mvm_binding_add_vif(mvm, vif);  	if (ret) @@ -4386,7 +4950,12 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,  		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);  	} -	if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { +	if (vif->type == NL80211_IFTYPE_STATION) { +		if (!switching_chanctx) { +			mvmvif->csa_bcn_pending = false; +			goto out; +		} +  		mvmvif->csa_bcn_pending = true;  		if (!fw_has_capa(&mvm->fw->ucode_capa, @@ -4411,9 +4980,10 @@ out_remove_binding:  	iwl_mvm_power_update_mac(mvm);  out:  	if (ret) -		mvmvif->phy_ctxt = NULL; +		mvmvif->deflink.phy_ctxt = NULL;  	return ret;  } +  static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,  				      struct ieee80211_vif *vif,  				      struct ieee80211_bss_conf *link_conf, @@ -4423,35 +4993,39 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,  	int ret;  	mutex_lock(&mvm->mutex); -	ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false); +	ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);  	mutex_unlock(&mvm->mutex);  	return ret;  } -static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, -					   struct ieee80211_vif *vif, -					   struct ieee80211_chanctx_conf *ctx, -					   bool switching_chanctx) +/* + * This function executes the common part for MLD and non-MLD modes. + * + * Returns if chanctx unassign chanctx is done + * (either on failure or success) + */ +static bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm, +						  struct ieee80211_vif *vif, +						  bool switching_chanctx)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct ieee80211_vif *disabled_vif = NULL;  	lockdep_assert_held(&mvm->mutex); -	iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); +	iwl_mvm_remove_time_event(mvm, mvmvif, +				  &mvmvif->time_event_data);  	switch (vif->type) {  	case NL80211_IFTYPE_ADHOC: -		goto out; +		return true;  	case NL80211_IFTYPE_MONITOR:  		mvmvif->monitor_active = false;  		mvmvif->ps_disabled = false; -		iwl_mvm_rm_snif_sta(mvm, vif);  		break;  	case NL80211_IFTYPE_AP:  		/* This part is triggered only during CSA */  		if (!switching_chanctx || !mvmvif->ap_ibss_active) -			goto out; +			return true;  		mvmvif->csa_countdown = false; @@ -4463,18 +5037,33 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,  		mvmvif->ap_ibss_active = false;  		break; -	case NL80211_IFTYPE_STATION: -		if (!switching_chanctx) -			break; +	default: +		break; +	} +	return false; +} -		disabled_vif = vif; +static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, +					   struct ieee80211_vif *vif, +					   struct ieee80211_bss_conf *link_conf, +					   struct ieee80211_chanctx_conf *ctx, +					   bool switching_chanctx) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_vif *disabled_vif = NULL; +	if (__iwl_mvm_unassign_vif_chanctx_common(mvm, vif, switching_chanctx)) +		goto out; + +	if (vif->type == NL80211_IFTYPE_MONITOR) +		iwl_mvm_rm_snif_sta(mvm, vif); + + +	if (vif->type == NL80211_IFTYPE_STATION && switching_chanctx) { +		disabled_vif = vif;  		if (!fw_has_capa(&mvm->fw->ucode_capa,  				 IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD))  			iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); -		break; -	default: -		break;  	}  	iwl_mvm_update_quotas(mvm, false, disabled_vif); @@ -4484,7 +5073,7 @@ out:  	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD) &&  	    switching_chanctx)  		return; -	mvmvif->phy_ctxt = NULL; +	mvmvif->deflink.phy_ctxt = NULL;  	iwl_mvm_power_update_mac(mvm);  } @@ -4496,18 +5085,20 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	mutex_lock(&mvm->mutex); -	__iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false); +	__iwl_mvm_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false);  	mutex_unlock(&mvm->mutex);  }  static int  iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm, -				struct ieee80211_vif_chanctx_switch *vifs) +				struct ieee80211_vif_chanctx_switch *vifs, +				struct iwl_mvm_switch_vif_chanctx_ops *ops)  {  	int ret;  	mutex_lock(&mvm->mutex); -	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); +	ops->__unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +				    vifs[0].old_ctx, true);  	__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);  	ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); @@ -4516,8 +5107,8 @@ iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,  		goto out_reassign;  	} -	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, -					   true); +	ret = ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +					vifs[0].new_ctx, true);  	if (ret) {  		IWL_ERR(mvm,  			"failed to assign new_ctx during channel switch\n"); @@ -4539,8 +5130,8 @@ out_reassign:  		goto out_restart;  	} -	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, -					 true)) { +	if (ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +				      vifs[0].old_ctx, true)) {  		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");  		goto out_restart;  	} @@ -4559,15 +5150,17 @@ out:  static int  iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm, -				    struct ieee80211_vif_chanctx_switch *vifs) +				    struct ieee80211_vif_chanctx_switch *vifs, +				    struct iwl_mvm_switch_vif_chanctx_ops *ops)  {  	int ret;  	mutex_lock(&mvm->mutex); -	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); +	ops->__unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +				    vifs[0].old_ctx, true); -	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, -					   true); +	ret = ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +					vifs[0].new_ctx, true);  	if (ret) {  		IWL_ERR(mvm,  			"failed to assign new_ctx during channel switch\n"); @@ -4577,8 +5170,8 @@ iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,  	goto out;  out_reassign: -	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, -					 true)) { +	if (ops->__assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].link_conf, +				      vifs[0].old_ctx, true)) {  		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");  		goto out_restart;  	} @@ -4595,10 +5188,13 @@ out:  	return ret;  } -static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, -				      struct ieee80211_vif_chanctx_switch *vifs, -				      int n_vifs, -				      enum ieee80211_chanctx_switch_mode mode) +/* Execute the common part for both MLD and non-MLD modes */ +int +iwl_mvm_switch_vif_chanctx_common(struct ieee80211_hw *hw, +				  struct ieee80211_vif_chanctx_switch *vifs, +				  int n_vifs, +				  enum ieee80211_chanctx_switch_mode mode, +				  struct iwl_mvm_switch_vif_chanctx_ops *ops)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -4609,10 +5205,10 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,  	switch (mode) {  	case CHANCTX_SWMODE_SWAP_CONTEXTS: -		ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs); +		ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs, ops);  		break;  	case CHANCTX_SWMODE_REASSIGN_VIF: -		ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs); +		ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs, ops);  		break;  	default:  		ret = -EOPNOTSUPP; @@ -4622,16 +5218,28 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,  	return ret;  } -static int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw) +static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, +				      struct ieee80211_vif_chanctx_switch *vifs, +				      int n_vifs, +				      enum ieee80211_chanctx_switch_mode mode) +{ +	struct iwl_mvm_switch_vif_chanctx_ops ops = { +		.__assign_vif_chanctx = __iwl_mvm_assign_vif_chanctx, +		.__unassign_vif_chanctx = __iwl_mvm_unassign_vif_chanctx, +	}; + +	return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops); +} + +int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	return mvm->ibss_manager;  } -static int iwl_mvm_set_tim(struct ieee80211_hw *hw, -			   struct ieee80211_sta *sta, -			   bool set) +int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, +		    bool set)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); @@ -4641,7 +5249,8 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,  		return -EINVAL;  	} -	return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); +	return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif, +					       &mvm_sta->vif->bss_conf);  }  #ifdef CONFIG_NL80211_TESTMODE @@ -4697,9 +5306,9 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,  	return -EOPNOTSUPP;  } -static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, -				    struct ieee80211_vif *vif, -				    void *data, int len) +int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     void *data, int len)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int err; @@ -4712,9 +5321,8 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,  }  #endif -static void iwl_mvm_channel_switch(struct ieee80211_hw *hw, -				   struct ieee80211_vif *vif, -				   struct ieee80211_channel_switch *chsw) +void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			    struct ieee80211_channel_switch *chsw)  {  	/* By implementing this operation, we prevent mac80211 from  	 * starting its own channel switch timer, so that we can call @@ -4789,9 +5397,9 @@ static int iwl_mvm_old_pre_chan_sw_sta(struct iwl_mvm *mvm,  }  #define IWL_MAX_CSA_BLOCK_TX 1500 -static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, -				      struct ieee80211_vif *vif, -				      struct ieee80211_channel_switch *chsw) +int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, +			       struct ieee80211_vif *vif, +			       struct ieee80211_channel_switch *chsw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct ieee80211_vif *csa_vif; @@ -4904,9 +5512,9 @@ out_unlock:  	return ret;  } -static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw, -					     struct ieee80211_vif *vif, -					     struct ieee80211_channel_switch *chsw) +void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw, +				      struct ieee80211_vif *vif, +				      struct ieee80211_channel_switch *chsw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -4991,13 +5599,14 @@ static void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)  	mutex_unlock(&mvm->mutex);  } -static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, -			      struct ieee80211_vif *vif, u32 queues, bool drop) +void iwl_mvm_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       u32 queues, bool drop)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif;  	struct iwl_mvm_sta *mvmsta;  	struct ieee80211_sta *sta; +	bool ap_sta_done = false;  	int i;  	u32 msk = 0; @@ -5026,16 +5635,23 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,  		if (mvmsta->vif != vif)  			continue; +		if (sta == mvmvif->ap_sta) { +			if (ap_sta_done) +				continue; +			ap_sta_done = true; +		} +  		/* make sure only TDLS peers or the AP are flushed */ -		WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls); +		WARN_ON_ONCE(sta != mvmvif->ap_sta && !sta->tdls);  		if (drop) {  			if (iwl_mvm_flush_sta(mvm, mvmsta, false))  				IWL_ERR(mvm, "flush request fail\n");  		} else { -			msk |= mvmsta->tfd_queue_msk;  			if (iwl_mvm_has_new_tx_api(mvm))  				iwl_mvm_wait_sta_queues_empty(mvm, mvmsta); +			else /* only used for !iwl_mvm_has_new_tx_api() below */ +				msk |= mvmsta->tfd_queue_msk;  		}  	} @@ -5048,8 +5664,8 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,  		iwl_trans_wait_tx_queues_empty(mvm->trans, msk);  } -static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, -				  struct survey_info *survey) +int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, +			   struct survey_info *survey)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -5233,22 +5849,22 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo)  	}  } -static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif, -				       struct ieee80211_sta *sta, -				       struct station_info *sinfo) +void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				struct station_info *sinfo)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	if (mvmsta->avg_energy) { -		sinfo->signal_avg = -(s8)mvmsta->avg_energy; +	if (mvmsta->deflink.avg_energy) { +		sinfo->signal_avg = -(s8)mvmsta->deflink.avg_energy;  		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);  	}  	if (iwl_mvm_has_tlc_offload(mvm)) { -		struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; +		struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->deflink.lq_sta.rs_fw;  		iwl_mvm_set_sta_rate(lq_sta->last_rate_n_flags, &sinfo->txrate);  		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); @@ -5263,18 +5879,19 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,  	mutex_lock(&mvm->mutex); -	if (mvmvif->ap_sta_id != mvmsta->sta_id) +	if (mvmvif->deflink.ap_sta_id != mvmsta->deflink.sta_id)  		goto unlock;  	if (iwl_mvm_request_statistics(mvm, false))  		goto unlock; -	sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons + -			   mvmvif->beacon_stats.accu_num_beacons; +	sinfo->rx_beacon = mvmvif->deflink.beacon_stats.num_beacons + +			   mvmvif->deflink.beacon_stats.accu_num_beacons;  	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX); -	if (mvmvif->beacon_stats.avg_signal) { +	if (mvmvif->deflink.beacon_stats.avg_signal) {  		/* firmware only reports a value after RXing a few beacons */ -		sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal; +		sinfo->rx_beacon_signal_avg = +			mvmvif->deflink.beacon_stats.avg_signal;  		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG);  	}   unlock: @@ -5376,9 +5993,9 @@ static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,  				event->u.ba.ssn);  } -static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, -				       struct ieee80211_vif *vif, -				       const struct ieee80211_event *event) +void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				const struct ieee80211_event *event)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -5460,7 +6077,7 @@ out:  	}  } -static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw) +void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -5469,7 +6086,7 @@ static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)  	mutex_unlock(&mvm->mutex);  } -static int +int  iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,  				    struct ieee80211_vif *vif,  				    struct cfg80211_ftm_responder_stats *stats) @@ -5498,9 +6115,8 @@ iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw,  	return 0;  } -static int iwl_mvm_start_pmsr(struct ieee80211_hw *hw, -			      struct ieee80211_vif *vif, -			      struct cfg80211_pmsr_request *request) +int iwl_mvm_start_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       struct cfg80211_pmsr_request *request)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);  	int ret; @@ -5512,9 +6128,8 @@ static int iwl_mvm_start_pmsr(struct ieee80211_hw *hw,  	return ret;  } -static void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw, -			       struct ieee80211_vif *vif, -			       struct cfg80211_pmsr_request *request) +void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			struct cfg80211_pmsr_request *request)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -5539,7 +6154,7 @@ static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); -	if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) +	if (iwl_mvm_has_new_tx_csum(mvm))  		return iwl_mvm_tx_csum_bz(mvm, head, true) ==  		       iwl_mvm_tx_csum_bz(mvm, skb, true); @@ -5553,6 +6168,29 @@ static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,  	return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head);  } +int iwl_mvm_set_hw_timestamp(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     struct cfg80211_set_hw_timestamp *hwts) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	u32 protocols = 0; +	int ret; + +	/* HW timestamping is only supported for a specific station */ +	if (!hwts->macaddr) +		return -EOPNOTSUPP; + +	if (hwts->enable) +		protocols = +			IWL_TIME_SYNC_PROTOCOL_TM | IWL_TIME_SYNC_PROTOCOL_FTM; + +	mutex_lock(&mvm->mutex); +	ret = iwl_mvm_time_sync_config(mvm, hwts->macaddr, protocols); +	mutex_unlock(&mvm->mutex); + +	return ret; +} +  const struct ieee80211_ops iwl_mvm_hw_ops = {  	.tx = iwl_mvm_mac_tx,  	.wake_tx_queue = iwl_mvm_mac_wake_tx_queue, @@ -5641,4 +6279,5 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {  #ifdef CONFIG_IWLWIFI_DEBUGFS  	.sta_add_debugfs = iwl_mvm_sta_add_debugfs,  #endif +	.set_hw_timestamp = iwl_mvm_set_hw_timestamp,  }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index e27c893502f7..8853821b3716 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -14,23 +14,41 @@ static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm,  				    struct ieee80211_key_conf *keyconf)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *link_info = &mvmvif->deflink; -	if (vif->type == NL80211_IFTYPE_AP && -	    !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) -		return BIT(mvmvif->mcast_sta.sta_id); - -	if (sta) { -		struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); +	lockdep_assert_held(&mvm->mutex); -		return BIT(mvmsta->sta_id); +	if (keyconf->link_id >= 0) { +		link_info = mvmvif->link[keyconf->link_id]; +		if (!link_info) +			return 0;  	} -	if (vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) -		return BIT(mvmvif->ap_sta_id); +	/* AP group keys are per link and should be on the mcast STA */ +	if (vif->type == NL80211_IFTYPE_AP && +	    !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) +		return BIT(link_info->mcast_sta.sta_id); + +	/* for client mode use the AP STA also for group keys */ +	if (!sta && vif->type == NL80211_IFTYPE_STATION) +		sta = mvmvif->ap_sta; + +	/* During remove the STA was removed and the group keys come later +	 * (which sounds like a bad sequence, but remember that to mac80211 the +	 * group keys have no sta pointer), so we don't have a STA now. +	 * Since this happens for group keys only, just use the link_info as +	 * the group keys are per link; make sure that is the case by checking +	 * we do have a link_id or are not doing MLO. +	 * Of course the same can be done during add as well, but we must do +	 * it during remove, since we don't have the mvmvif->ap_sta pointer. +	 */ +	if (!sta && (keyconf->link_id >= 0 || !vif->valid_links)) +		return BIT(link_info->ap_sta_id); + +	/* STA should be non-NULL now, but iwl_mvm_sta_fw_id_mask() checks */ -	/* invalid */ -	return 0; +	/* pass link_id to filter by it if not -1 (GTK on client) */ +	return iwl_mvm_sta_fw_id_mask(mvm, sta, keyconf->link_id);  }  static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, @@ -41,6 +59,8 @@ static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	u32 flags = 0; +	lockdep_assert_held(&mvm->mutex); +  	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))  		flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; @@ -68,22 +88,68 @@ static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,  		break;  	} -	rcu_read_lock(); -	if (!sta && vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { -		u8 sta_id = mvmvif->ap_sta_id; - -		sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], -					    lockdep_is_held(&mvm->mutex)); -	} +	if (!sta && vif->type == NL80211_IFTYPE_STATION) +		sta = mvmvif->ap_sta;  	if (!IS_ERR_OR_NULL(sta) && sta->mfp)  		flags |= IWL_SEC_KEY_FLAG_MFP; -	rcu_read_unlock();  	return flags;  } +struct iwl_mvm_sta_key_update_data { +	struct ieee80211_sta *sta; +	u32 old_sta_mask; +	u32 new_sta_mask; +	int err; +}; + +static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw, +				       struct ieee80211_vif *vif, +				       struct ieee80211_sta *sta, +				       struct ieee80211_key_conf *key, +				       void *_data) +{ +	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); +	struct iwl_mvm_sta_key_update_data *data = _data; +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_sec_key_cmd cmd = { +		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY), +		.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask), +		.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask), +		.u.modify.key_id = cpu_to_le32(key->keyidx), +		.u.modify.key_flags = +			cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)), +	}; +	int err; + +	/* only need to do this for pairwise keys (link_id == -1) */ +	if (sta != data->sta || key->link_id >= 0) +		return; + +	err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, sizeof(cmd), &cmd); + +	if (err) +		data->err = err; +} + +int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				u32 old_sta_mask, +				u32 new_sta_mask) +{ +	struct iwl_mvm_sta_key_update_data data = { +		.sta = sta, +		.old_sta_mask = old_sta_mask, +		.new_sta_mask = new_sta_mask, +	}; + +	ieee80211_iter_keys_rcu(mvm->hw, vif, iwl_mvm_mld_update_sta_key, +				&data); +	return data.err; +} +  static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,  				 u32 key_flags, u32 keyidx, u32 flags)  { @@ -118,6 +184,9 @@ int iwl_mvm_sec_key_add(struct iwl_mvm *mvm,  	if (WARN_ON(keyconf->keylen > sizeof(cmd.u.add.key)))  		return -EINVAL; +	if (WARN_ON(!sta_mask)) +		return -EINVAL; +  	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||  	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)  		memcpy(cmd.u.add.key + IWL_SEC_WEP_KEY_OFFSET, keyconf->key, @@ -164,6 +233,9 @@ static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm,  	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);  	int ret; +	if (WARN_ON(!sta_mask)) +		return -EINVAL; +  	ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,  				    flags);  	if (ret) @@ -195,6 +267,7 @@ static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw,  					   void *data)  {  	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	unsigned int link_id = (uintptr_t)data;  	if (key->hw_key_idx == STA_KEY_IDX_INVALID)  		return; @@ -202,19 +275,23 @@ static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw,  	if (sta)  		return; +	if (key->link_id >= 0 && key->link_id != link_id) +		return; +  	_iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC);  	key->hw_key_idx = STA_KEY_IDX_INVALID;  }  void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, -			       struct ieee80211_vif *vif) +			       struct ieee80211_vif *vif, +			       struct iwl_mvm_vif_link_info *link, +			       unsigned int link_id)  { -	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);  	u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); -	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || -		    mvmvif->ap_sta_id == IWL_MVM_INVALID_STA)) +	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION || +			 link->ap_sta_id == IWL_MVM_INVALID_STA))  		return;  	if (!sec_key_ver) @@ -222,5 +299,5 @@ void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,  	ieee80211_iter_keys_rcu(mvm->hw, vif,  				iwl_mvm_sec_key_remove_ap_iter, -				NULL); +				(void *)(uintptr_t)link_id);  } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c new file mode 100644 index 000000000000..1717fb52dc12 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, +				       struct ieee80211_vif *vif, +				       struct iwl_mac_config_cmd *cmd) +{ +	if (vif->type == NL80211_IFTYPE_AP) +		cmd->he_ap_support = cpu_to_le16(1); +	else +		cmd->he_support = cpu_to_le16(1); +} + +static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif, +					    struct iwl_mac_config_cmd *cmd, +					    u32 action) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_bss_conf *link_conf; +	unsigned int link_id; + +	cmd->id_and_color = cpu_to_le32(mvmvif->id); +	cmd->action = cpu_to_le32(action); + +	cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif)); + +	memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); + +	cmd->he_support = 0; +	cmd->eht_support = 0; + +	/* should be set by specific context type handler */ +	cmd->filter_flags = 0; + +	cmd->nic_not_ack_enabled = +		cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif)); + +	if (iwlwifi_mod_params.disable_11ax) +		return; + +	/* If we have MLO enabled, then the firmware needs to enable +	 * address translation for the station(s) we add. That depends +	 * on having EHT enabled in firmware, which in turn depends on +	 * mac80211 in the code below. +	 * However, mac80211 doesn't enable HE/EHT until it has parsed +	 * the association response successfully, so just skip all that +	 * and enable both when we have MLO. +	 */ +	if (vif->valid_links) { +		iwl_mvm_mld_set_he_support(mvm, vif, cmd); +		cmd->eht_support = cpu_to_le32(1); +		return; +	} + +	rcu_read_lock(); +	for (link_id = 0; link_id < ARRAY_SIZE((vif)->link_conf); link_id++) { +		link_conf = rcu_dereference(vif->link_conf[link_id]); +		if (!link_conf) +			continue; + +		if (link_conf->he_support) +			iwl_mvm_mld_set_he_support(mvm, vif, cmd); + +		/* it's not reasonable to have EHT without HE and FW API doesn't +		 * support it. Ignore EHT in this case. +		 */ +		if (!link_conf->he_support && link_conf->eht_support) +			continue; + +		if (link_conf->eht_support) { +			cmd->eht_support = cpu_to_le32(1); +			break; +		} +	} +	rcu_read_unlock(); +} + +static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, +					 struct iwl_mac_config_cmd *cmd) +{ +	int ret = iwl_mvm_send_cmd_pdu(mvm, +				       WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), +				       0, sizeof(*cmd), cmd); +	if (ret) +		IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n", +			le32_to_cpu(cmd->action), ret); +	return ret; +} + +static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					u32 action, bool force_assoc_off) +{ +	struct iwl_mac_config_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_STATION); + +	/* Fill the common data for all mac context types */ +	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + +	/* +	 * We always want to hear MCAST frames, if we're not authorized yet, +	 * we'll drop them. +	 */ +	cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); + +	if (vif->p2p) +		cmd.client.ctwin = +			iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); + +	if (vif->cfg.assoc && !force_assoc_off) { +		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + +		cmd.client.is_assoc = cpu_to_le32(1); + +		if (!mvmvif->authorized && +		    fw_has_capa(&mvm->fw->ucode_capa, +				IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO)) +			cmd.client.data_policy |= +				cpu_to_le32(COEX_HIGH_PRIORITY_ENABLE); + +	} else { +		cmd.client.is_assoc = cpu_to_le32(0); + +		/* Allow beacons to pass through as long as we are not +		 * associated, or we do not have dtim period information. +		 */ +		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); +	} + +	cmd.client.assoc_id = cpu_to_le32(vif->cfg.aid); + +	if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) +		cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + +	if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) +		cmd.client.data_policy |= +			iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif); + +	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, +					     struct ieee80211_vif *vif, +					     u32 action) +{ +	struct iwl_mac_config_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); + +	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + +	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | +				       MAC_FILTER_IN_CONTROL_AND_MGMT | +				       MAC_CFG_FILTER_ACCEPT_BEACON | +				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ | +				       MAC_CFG_FILTER_ACCEPT_GRP); + +	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, +					 struct ieee80211_vif *vif, +					 u32 action) +{ +	struct iwl_mac_config_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + +	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + +	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | +				       MAC_CFG_FILTER_ACCEPT_PROBE_REQ | +				       MAC_CFG_FILTER_ACCEPT_GRP); + +	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, +					       struct ieee80211_vif *vif, +					       u32 action) +{ +	struct iwl_mac_config_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); + +	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + +	cmd.p2p_dev.is_disc_extended = +		iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); + +	/* Override the filter flags to accept only probe requests */ +	cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + +	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, +					  struct ieee80211_vif *vif, +					  u32 action) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mac_config_cmd cmd = {}; + +	WARN_ON(vif->type != NL80211_IFTYPE_AP); + +	/* Fill the common data for all mac context types */ +	iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + +	iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, +						 &cmd.filter_flags, +						 MAC_CFG_FILTER_ACCEPT_PROBE_REQ, +						 MAC_CFG_FILTER_ACCEPT_BEACON); + +	return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, +				    struct ieee80211_vif *vif, +				    u32 action, bool force_assoc_off) +{ +	switch (vif->type) { +	case NL80211_IFTYPE_STATION: +		return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, +						    force_assoc_off); +	case NL80211_IFTYPE_AP: +		return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); +	case NL80211_IFTYPE_MONITOR: +		return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); +	case NL80211_IFTYPE_P2P_DEVICE: +		return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); +	case NL80211_IFTYPE_ADHOC: +		return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); +	default: +		break; +	} + +	return -EOPNOTSUPP; +} + +int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) +		return -EOPNOTSUPP; + +	if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", +		      vif->addr, ieee80211_vif_type_p2p(vif))) +		return -EIO; + +	ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, +				       true); +	if (ret) +		return ret; + +	/* will only do anything at resume from D3 time */ +	iwl_mvm_set_last_nonqos_seq(mvm, vif); + +	mvmvif->uploaded = true; +	return 0; +} + +int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif, +				 bool force_assoc_off) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + +	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) +		return -EOPNOTSUPP; + +	if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", +		      vif->addr, ieee80211_vif_type_p2p(vif))) +		return -EIO; + +	return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, +					force_assoc_off); +} + +int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mac_config_cmd cmd = { +		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), +		.id_and_color = cpu_to_le32(mvmvif->id), +	}; +	int ret; + +	if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) +		return -EOPNOTSUPP; + +	if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", +		      vif->addr, ieee80211_vif_type_p2p(vif))) +		return -EIO; + +	ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +	if (ret) +		return ret; + +	mvmvif->uploaded = false; + +	return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c new file mode 100644 index 000000000000..7fb66c570959 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, +					 struct ieee80211_vif *vif) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	mutex_lock(&mvm->mutex); + +	mvmvif->mvm = mvm; + +	/* Not much to do here. The stack will not allow interface +	 * types or combinations that we didn't advertise, so we +	 * don't really have to check the types. +	 */ + +	/* make sure that beacon statistics don't go backwards with FW reset */ +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) +		mvmvif->deflink.beacon_stats.accu_num_beacons += +			mvmvif->deflink.beacon_stats.num_beacons; + +	/* Allocate resources for the MAC context, and add it to the fw  */ +	ret = iwl_mvm_mac_ctxt_init(mvm, vif); +	if (ret) +		goto out_unlock; + +	rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); + +	mvmvif->features |= hw->netdev_features; + +	/* reset deflink MLO parameters */ +	mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; +	mvmvif->deflink.active = 0; +	/* the first link always points to the default one */ +	mvmvif->link[0] = &mvmvif->deflink; + +	ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); +	if (ret) +		goto out_unlock; + +	/* beacon filtering */ +	ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); +	if (ret) +		goto out_remove_mac; + +	if (!mvm->bf_allowed_vif && +	    vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { +		mvm->bf_allowed_vif = mvmvif; +		vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | +				     IEEE80211_VIF_SUPPORTS_CQM_RSSI; +	} + +	/* +	 * P2P_DEVICE interface does not have a channel context assigned to it, +	 * so a dedicated PHY context is allocated to it and the corresponding +	 * MAC context is bound to it at this stage. +	 */ +	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { +		mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); +		if (!mvmvif->deflink.phy_ctxt) { +			ret = -ENOSPC; +			goto out_free_bf; +		} + +		iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt); +		ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); +		if (ret) +			goto out_unref_phy; + +		ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, +					   LINK_CONTEXT_MODIFY_ACTIVE | +					   LINK_CONTEXT_MODIFY_RATES_INFO, +					   true); +		if (ret) +			goto out_remove_link; + +		ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, &vif->bss_conf); +		if (ret) +			goto out_remove_link; + +		/* Save a pointer to p2p device vif, so it can later be used to +		 * update the p2p device MAC when a GO is started/stopped +		 */ +		mvm->p2p_device_vif = vif; +	} else { +		ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); +		if (ret) +			goto out_free_bf; +	} + +	ret = iwl_mvm_power_update_mac(mvm); +	if (ret) +		goto out_free_bf; + +	iwl_mvm_tcm_add_vif(mvm, vif); +	INIT_DELAYED_WORK(&mvmvif->csa_work, +			  iwl_mvm_channel_switch_disconnect_wk); + +	if (vif->type == NL80211_IFTYPE_MONITOR) { +		mvm->monitor_on = true; +		ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); +	} + +	iwl_mvm_vif_dbgfs_register(mvm, vif); + +	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && +	    vif->type == NL80211_IFTYPE_STATION && !vif->p2p && +	    !mvm->csme_vif && mvm->mei_registered) { +		iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr); +		iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev); +		mvm->csme_vif = vif; +	} + +	goto out_unlock; + + out_remove_link: +	iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); + out_unref_phy: +	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + out_free_bf: +	if (mvm->bf_allowed_vif == mvmvif) { +		mvm->bf_allowed_vif = NULL; +		vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | +				       IEEE80211_VIF_SUPPORTS_CQM_RSSI); +	} + out_remove_mac: +	mvmvif->deflink.phy_ctxt = NULL; +	mvmvif->link[0] = NULL; +	iwl_mvm_mld_mac_ctxt_remove(mvm, vif); + out_unlock: +	mutex_unlock(&mvm->mutex); + +	return ret; +} + +static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, +					     struct ieee80211_vif *vif) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_probe_resp_data *probe_data; + +	iwl_mvm_prepare_mac_removal(mvm, vif); + +	if (!(vif->type == NL80211_IFTYPE_AP || +	      vif->type == NL80211_IFTYPE_ADHOC)) +		iwl_mvm_tcm_rm_vif(mvm, vif); + +	mutex_lock(&mvm->mutex); + +	if (vif == mvm->csme_vif) { +		iwl_mei_set_netdev(NULL); +		mvm->csme_vif = NULL; +	} + +	if (mvm->bf_allowed_vif == mvmvif) { +		mvm->bf_allowed_vif = NULL; +		vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | +				       IEEE80211_VIF_SUPPORTS_CQM_RSSI); +	} + +	if (vif->bss_conf.ftm_responder) +		memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats)); + +	iwl_mvm_vif_dbgfs_clean(mvm, vif); + +	/* For AP/GO interface, the tear down of the resources allocated to the +	 * interface is be handled as part of the stop_ap flow. +	 */ +	if (vif->type == NL80211_IFTYPE_AP || +	    vif->type == NL80211_IFTYPE_ADHOC) { +#ifdef CONFIG_NL80211_TESTMODE +		if (vif == mvm->noa_vif) { +			mvm->noa_vif = NULL; +			mvm->noa_duration = 0; +		} +#endif +	} + +	iwl_mvm_power_update_mac(mvm); + +	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { +		mvm->p2p_device_vif = NULL; + +		/* P2P device uses only one link */ +		iwl_mvm_mld_rm_bcast_sta(mvm, vif, &vif->bss_conf); +		iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); +		iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); +		mvmvif->deflink.phy_ctxt = NULL; +	} else { +		iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); +	} + +	iwl_mvm_mld_mac_ctxt_remove(mvm, vif); + +	RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); + +	probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data, +					       lockdep_is_held(&mvm->mutex)); +	RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); +	if (probe_data) +		kfree_rcu(probe_data, rcu_head); + +	if (vif->type == NL80211_IFTYPE_MONITOR) { +		mvm->monitor_on = false; +		__clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); +	} + +	mutex_unlock(&mvm->mutex); +} + +static int +__iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif, +				 struct ieee80211_bss_conf *link_conf, +				 struct ieee80211_chanctx_conf *ctx, +				 bool switching_chanctx) +{ +	u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; +	struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id = link_conf->link_id; +	int ret; + +	if (WARN_ON_ONCE(!mvmvif->link[link_id])) +		return -EINVAL; + +	/* mac parameters such as HE support can change at this stage +	 * For sta, need first to configure correct state from drv_sta_state +	 * and only after that update mac config. +	 */ +	if (vif->type == NL80211_IFTYPE_AP) { +		ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); +		if (ret) { +			IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); +			return -EINVAL; +		} +	} + +	mvmvif->link[link_id]->phy_ctxt = phy_ctxt; + +	if (switching_chanctx) { +		/* reactivate if we turned this off during channel switch */ +		if (vif->type == NL80211_IFTYPE_AP) +			mvmvif->ap_ibss_active = true; +	} + +	/* send it first with phy context ID */ +	ret = iwl_mvm_link_changed(mvm, vif, link_conf, 0, false); +	if (ret) +		goto out; + +	/* Initialize rate control for the AP station, since we might be +	 * doing a link switch here - we cannot initialize it before since +	 * this needs the phy context assigned (and in FW?), and we cannot +	 * do it later because it needs to be initialized as soon as we're +	 * able to TX on the link, i.e. when active. +	 * +	 * Firmware restart isn't quite correct yet for MLO, but we don't +	 * need to do it in that case anyway since it will happen from the +	 * normal station state callback. +	 */ +	if (mvmvif->ap_sta && +	    !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { +		struct ieee80211_link_sta *link_sta; + +		rcu_read_lock(); +		link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]); + +		if (!WARN_ON_ONCE(!link_sta)) +			iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta, +					     link_conf, link_sta, +					     phy_ctxt->channel->band); +		rcu_read_unlock(); +	} + +	/* then activate */ +	ret = iwl_mvm_link_changed(mvm, vif, link_conf, +				   LINK_CONTEXT_MODIFY_ACTIVE | +				   LINK_CONTEXT_MODIFY_RATES_INFO, +				   true); +	if (ret) +		goto out; + +	/* +	 * Power state must be updated before quotas, +	 * otherwise fw will complain. +	 */ +	iwl_mvm_power_update_mac(mvm); + +	if (vif->type == NL80211_IFTYPE_MONITOR) { +		ret = iwl_mvm_mld_add_snif_sta(mvm, vif, link_conf); +		if (ret) +			goto deactivate; +	} + +	return 0; + +deactivate: +	iwl_mvm_link_changed(mvm, vif, link_conf, LINK_CONTEXT_MODIFY_ACTIVE, +			     false); +out: +	mvmvif->link[link_id]->phy_ctxt = NULL; +	iwl_mvm_power_update_mac(mvm); +	return ret; +} + +static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, +					  struct ieee80211_vif *vif, +					  struct ieee80211_bss_conf *link_conf, +					  struct ieee80211_chanctx_conf *ctx) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	int ret; + +	mutex_lock(&mvm->mutex); +	ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false); +	mutex_unlock(&mvm->mutex); + +	return ret; +} + +static void +__iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, +				   struct ieee80211_vif *vif, +				   struct ieee80211_bss_conf *link_conf, +				   struct ieee80211_chanctx_conf *ctx, +				   bool switching_chanctx) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	unsigned int link_id = link_conf->link_id; + +	/* shouldn't happen, but verify link_id is valid before accessing */ +	if (WARN_ON_ONCE(!mvmvif->link[link_id])) +		return; + +	if (vif->type == NL80211_IFTYPE_AP && switching_chanctx) { +		mvmvif->csa_countdown = false; + +		/* Set CS bit on all the stations */ +		iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); + +		/* Save blocked iface, the timeout is set on the next beacon */ +		rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); + +		mvmvif->ap_ibss_active = false; +	} + +	if (vif->type == NL80211_IFTYPE_MONITOR) +		iwl_mvm_mld_rm_snif_sta(mvm, vif); + +	iwl_mvm_link_changed(mvm, vif, link_conf, +			     LINK_CONTEXT_MODIFY_ACTIVE, false); + +	if (switching_chanctx) +		return; +	mvmvif->link[link_id]->phy_ctxt = NULL; +	iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, +					     struct ieee80211_vif *vif, +					     struct ieee80211_bss_conf *link_conf, +					     struct ieee80211_chanctx_conf *ctx) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); +	__iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false); +	mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif, +				     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret; + +	mutex_lock(&mvm->mutex); +	/* Send the beacon template */ +	ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); +	if (ret) +		goto out_unlock; + +	/* the link should be already activated when assigning chan context */ +	ret = iwl_mvm_link_changed(mvm, vif, link_conf, +				   LINK_CONTEXT_MODIFY_ALL & +				   ~LINK_CONTEXT_MODIFY_ACTIVE, +				   true); +	if (ret) +		goto out_unlock; + +	ret = iwl_mvm_mld_add_mcast_sta(mvm, vif, link_conf); +	if (ret) +		goto out_unlock; + +	/* Send the bcast station. At this stage the TBTT and DTIM time +	 * events are added and applied to the scheduler +	 */ +	ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, link_conf); +	if (ret) +		goto out_rm_mcast; + +	if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret)) +		goto out_failed; + +	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ +	if (vif->p2p && mvm->p2p_device_vif) +		iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + +	iwl_mvm_bt_coex_vif_change(mvm); + +	/* we don't support TDLS during DCM */ +	if (iwl_mvm_phy_ctx_count(mvm) > 1) +		iwl_mvm_teardown_tdls_peers(mvm); + +	iwl_mvm_ftm_restart_responder(mvm, vif); + +	goto out_unlock; + +out_failed: +	iwl_mvm_power_update_mac(mvm); +	mvmvif->ap_ibss_active = false; +	iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); +out_rm_mcast: +	iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); +out_unlock: +	mutex_unlock(&mvm->mutex); +	return ret; +} + +static int iwl_mvm_mld_start_ap(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *link_conf) +{ +	return iwl_mvm_mld_start_ap_ibss(hw, vif, link_conf); +} + +static int iwl_mvm_mld_start_ibss(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif) +{ +	return iwl_mvm_mld_start_ap_ibss(hw, vif, &vif->bss_conf); +} + +static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif, +				     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); + +	iwl_mvm_stop_ap_ibss_common(mvm, vif); + +	/* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ +	if (vif->p2p && mvm->p2p_device_vif) +		iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + +	iwl_mvm_ftm_responder_clear(mvm, vif); + +	iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); +	iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); + +	iwl_mvm_power_update_mac(mvm); +	mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *link_conf) +{ +	iwl_mvm_mld_stop_ap_ibss(hw, vif, link_conf); +} + +static void iwl_mvm_mld_stop_ibss(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif) +{ +	iwl_mvm_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); +} + +static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif, +				     struct ieee80211_sta *sta, +				     enum ieee80211_sta_state old_state, +				     enum ieee80211_sta_state new_state) +{ +	struct iwl_mvm_sta_state_ops callbacks = { +		.add_sta = iwl_mvm_mld_add_sta, +		.update_sta = iwl_mvm_mld_update_sta, +		.rm_sta = iwl_mvm_mld_rm_sta, +		.mac_ctxt_changed = iwl_mvm_mld_mac_ctxt_changed, +	}; + +	return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state, +					    &callbacks); +} + +static void +iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_bss_conf *link_conf, +				      u64 changes) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	bool has_he, has_eht; +	u32 link_changes = 0; +	int ret; + +	if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id])) +		return; + +	has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; +	has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; + +	/* Update EDCA params */ +	if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos) +		link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + +	if (changes & BSS_CHANGED_ERP_SLOT) +		link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + +	if (vif->cfg.assoc && (has_he || has_eht)) { +		IWL_DEBUG_MAC80211(mvm, "Associated in HE mode\n"); +		link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; +	} + +	/* Update EHT Puncturing info */ +	if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc && has_eht) +		link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; + +	if (link_changes) { +		ret = iwl_mvm_link_changed(mvm, vif, link_conf, link_changes, +					   true); +		if (ret) +			IWL_ERR(mvm, "failed to update link\n"); +	} + +	ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); +	if (ret) +		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + +	memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, +	       ETH_ALEN); + +	iwl_mvm_bss_info_changed_station_common(mvm, vif, link_conf, changes); +} + +static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif) +{ +	int i; + +	for_each_mvm_vif_valid_link(mvmvif, i) { +		if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA) +			return true; +	} + +	return false; +} + +static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int i, ret; + +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) +		return; + +	for_each_mvm_vif_valid_link(mvmvif, i) { +		struct iwl_mvm_vif_link_info *link = mvmvif->link[i]; + +		if (!link) +			continue; + +		iwl_mvm_sec_key_remove_ap(mvm, vif, link, i); +		ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id); +		if (ret) +			IWL_ERR(mvm, "failed to remove AP station\n"); + +		link->ap_sta_id = IWL_MVM_INVALID_STA; +	} +} + +static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, +						struct ieee80211_vif *vif, +						u64 changes) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_bss_conf *link_conf; +	bool protect = false; +	unsigned int i; +	int ret; + +	/* This might get called without active links during the +	 * chanctx switch, but we don't care about it anyway. +	 */ +	if (changes == BSS_CHANGED_IDLE) +		return; + +	ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); +	if (ret) +		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + +	mvmvif->associated = vif->cfg.assoc; + +	if (!(changes & BSS_CHANGED_ASSOC)) +		return; + +	if (vif->cfg.assoc) { +		/* clear statistics to get clean beacon counter */ +		iwl_mvm_request_statistics(mvm, true); +		iwl_mvm_sf_update(mvm, vif, false); +		iwl_mvm_power_vif_assoc(mvm, vif); + +		for_each_mvm_vif_valid_link(mvmvif, i) { +			memset(&mvmvif->link[i]->beacon_stats, 0, +			       sizeof(mvmvif->link[i]->beacon_stats)); + +			if (vif->p2p) { +				iwl_mvm_update_smps(mvm, vif, +						    IWL_MVM_SMPS_REQ_PROT, +						    IEEE80211_SMPS_DYNAMIC, i); +			} + +			rcu_read_lock(); +			link_conf = rcu_dereference(vif->link_conf[i]); +			if (link_conf && !link_conf->dtim_period) +				protect = true; +			rcu_read_unlock(); +		} + +		if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && +		    protect) { +			/* If we're not restarting and still haven't +			 * heard a beacon (dtim period unknown) then +			 * make sure we still have enough minimum time +			 * remaining in the time event, since the auth +			 * might actually have taken quite a while +			 * (especially for SAE) and so the remaining +			 * time could be small without us having heard +			 * a beacon yet. +			 */ +			iwl_mvm_protect_assoc(mvm, vif, 0); +		} + +		iwl_mvm_sf_update(mvm, vif, false); + +		/* FIXME: need to decide about misbehaving AP handling */ +		iwl_mvm_power_vif_assoc(mvm, vif); +	} else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { +		iwl_mvm_mei_host_disassociated(mvm); + +		/* If update fails - SF might be running in associated +		 * mode while disassociated - which is forbidden. +		 */ +		ret = iwl_mvm_sf_update(mvm, vif, false); +		WARN_ONCE(ret && +			  !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, +				    &mvm->status), +			  "Failed to update SF upon disassociation\n"); + +		/* If we get an assert during the connection (after the +		 * station has been added, but before the vif is set +		 * to associated), mac80211 will re-add the station and +		 * then configure the vif. Since the vif is not +		 * associated, we would remove the station here and +		 * this would fail the recovery. +		 */ +		iwl_mvm_mld_vif_delete_all_stas(mvm, vif); +	} + +	iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); +} + +static void +iwl_mvm_mld_link_info_changed_ap_ibss(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_bss_conf *link_conf, +				      u64 changes) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	u32 link_changes = LINK_CONTEXT_MODIFY_PROTECT_FLAGS | +			   LINK_CONTEXT_MODIFY_QOS_PARAMS; + +	/* Changes will be applied when the AP/IBSS is started */ +	if (!mvmvif->ap_ibss_active) +		return; + +	if (link_conf->he_support) +		link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + +	if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | +		       BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS | +		       BSS_CHANGED_HE_BSS_COLOR) && +		       iwl_mvm_link_changed(mvm, vif, link_conf, +					    link_changes, true)) +		IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + +	/* Need to send a new beacon template to the FW */ +	if (changes & BSS_CHANGED_BEACON && +	    iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf)) +		IWL_WARN(mvm, "Failed updating beacon data\n"); + +	/* FIXME: need to decide if we need FTM responder per link */ +	if (changes & BSS_CHANGED_FTM_RESPONDER) { +		int ret = iwl_mvm_ftm_start_responder(mvm, vif); + +		if (ret) +			IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n", +				 ret); +	} +} + +static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, +					  struct ieee80211_vif *vif, +					  struct ieee80211_bss_conf *link_conf, +					  u64 changes) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); + +	switch (vif->type) { +	case NL80211_IFTYPE_STATION: +		iwl_mvm_mld_link_info_changed_station(mvm, vif, link_conf, +						      changes); +		break; +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_ADHOC: +		iwl_mvm_mld_link_info_changed_ap_ibss(mvm, vif, link_conf, +						      changes); +		break; +	case NL80211_IFTYPE_MONITOR: +		if (changes & BSS_CHANGED_MU_GROUPS) +			iwl_mvm_update_mu_groups(mvm, vif); +		break; +	default: +		/* shouldn't happen */ +		WARN_ON_ONCE(1); +	} + +	if (changes & BSS_CHANGED_TXPOWER) { +		IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", +				link_conf->txpower); +		iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower); +	} + +	mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_mld_vif_cfg_changed(struct ieee80211_hw *hw, +					struct ieee80211_vif *vif, +					u64 changes) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	mutex_lock(&mvm->mutex); + +	if (changes & BSS_CHANGED_IDLE && !vif->cfg.idle) +		iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); + +	if (vif->type == NL80211_IFTYPE_STATION) +		iwl_mvm_mld_vif_cfg_changed_station(mvm, vif, changes); + +	mutex_unlock(&mvm->mutex); +} + +static int +iwl_mvm_mld_switch_vif_chanctx(struct ieee80211_hw *hw, +			       struct ieee80211_vif_chanctx_switch *vifs, +			       int n_vifs, +			       enum ieee80211_chanctx_switch_mode mode) +{ +	struct iwl_mvm_switch_vif_chanctx_ops ops = { +		.__assign_vif_chanctx = __iwl_mvm_mld_assign_vif_chanctx, +		.__unassign_vif_chanctx = __iwl_mvm_mld_unassign_vif_chanctx, +	}; + +	return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops); +} + +static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw, +					    struct ieee80211_vif *vif, +					    unsigned int filter_flags, +					    unsigned int changed_flags) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + +	/* We support only filter for probe requests */ +	if (!(changed_flags & FIF_PROBE_REQ)) +		return; + +	/* Supported only for p2p client interfaces */ +	if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc || +	    !vif->p2p) +		return; + +	mutex_lock(&mvm->mutex); +	iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); +	mutex_unlock(&mvm->mutex); +} + +static int +iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, +			struct ieee80211_vif *vif, +			unsigned int link_id, u16 ac, +			const struct ieee80211_tx_queue_params *params) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + +	mvmvif->deflink.queue_params[ac] = *params; + +	/* No need to update right away, we'll get BSS_CHANGED_QOS +	 * The exception is P2P_DEVICE interface which needs immediate update. +	 */ +	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { +		int ret; + +		mutex_lock(&mvm->mutex); +		ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, +					   LINK_CONTEXT_MODIFY_QOS_PARAMS, +					   true); +		mutex_unlock(&mvm->mutex); +		return ret; +	} +	return 0; +} + +static int iwl_mvm_link_switch_phy_ctx(struct iwl_mvm *mvm, +				       struct ieee80211_vif *vif, +				       struct iwl_mvm_phy_ctxt *new_phy_ctxt) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int ret = 0; + +	lockdep_assert_held(&mvm->mutex); + +	/* Inorder to change the phy_ctx of a link, the link needs to be +	 * inactive. Therefore, first deactivate the link, then change its +	 * phy_ctx, and then activate it again. +	 */ +	ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, +				   LINK_CONTEXT_MODIFY_ACTIVE, false); +	if (WARN(ret, "Failed to deactivate link\n")) +		return ret; + +	iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + +	mvmvif->deflink.phy_ctxt = new_phy_ctxt; + +	ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false); +	if (WARN(ret, "Failed to deactivate link\n")) +		return ret; + +	ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, +				   LINK_CONTEXT_MODIFY_ACTIVE, true); +	WARN(ret, "Failed binding P2P_DEVICE\n"); +	return ret; +} + +static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			   struct ieee80211_channel *channel, int duration, +			   enum ieee80211_roc_type type) +{ +	struct iwl_mvm_roc_ops ops = { +		.add_aux_sta_for_hs20 = iwl_mvm_mld_add_aux_sta, +		.switch_phy_ctxt = iwl_mvm_link_switch_phy_ctx, +	}; + +	return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); +} + +static int +iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     u16 old_links, u16 new_links, +			     struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ +	struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	u16 removed = old_links & ~new_links; +	u16 added = new_links & ~old_links; +	int err, i; + +	if (hweight16(new_links) > 2) { +		return -EOPNOTSUPP; +	} else if (hweight16(new_links) > 1) { +		unsigned int n_active = 0; + +		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { +			struct ieee80211_bss_conf *link_conf; + +			link_conf = link_conf_dereference_protected(vif, i); +			if (link_conf && +			    rcu_access_pointer(link_conf->chanctx_conf)) +				n_active++; +		} + +		if (vif->type == NL80211_IFTYPE_AP) { +			if (n_active > mvm->fw->ucode_capa.num_beacons) +				return -EOPNOTSUPP; +		} else if (n_active > 1) { +			return -EOPNOTSUPP; +		} +	} + +	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { +		int r; + +		if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) +			break; + +		if (!(added & BIT(i))) +			continue; +		new_link[i] = kzalloc(sizeof(*new_link[i]), GFP_KERNEL); +		if (!new_link[i]) { +			err = -ENOMEM; +			goto free; +		} + +		new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA; +		new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA; +		new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA; +		new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; + +		for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) +			new_link[i]->smps_requests[r] = +				IEEE80211_SMPS_AUTOMATIC; +	} + +	mutex_lock(&mvm->mutex); + +	if (old_links == 0) { +		err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); +		if (err) +			goto out_err; +		mvmvif->link[0] = NULL; +	} + +	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { +		if (removed & BIT(i)) { +			struct ieee80211_bss_conf *link_conf = old[i]; + +			err = iwl_mvm_disable_link(mvm, vif, link_conf); +			if (err) +				goto out_err; +			kfree(mvmvif->link[i]); +			mvmvif->link[i] = NULL; +		} + +		if (added & BIT(i)) { +			struct ieee80211_bss_conf *link_conf; + +			link_conf = link_conf_dereference_protected(vif, i); +			if (WARN_ON(!link_conf)) +				continue; + +			if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, +				      &mvm->status)) +				mvmvif->link[i] = new_link[i]; +			new_link[i] = NULL; +			err = iwl_mvm_add_link(mvm, vif, link_conf); +			if (err) +				goto out_err; +		} +	} + +	err = 0; +	if (new_links == 0) { +		mvmvif->link[0] = &mvmvif->deflink; +		err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); +	} + +out_err: +	/* we really don't have a good way to roll back here ... */ +	mutex_unlock(&mvm->mutex); + +free: +	for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) +		kfree(new_link[i]); +	return err; +} + +static int +iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     struct ieee80211_sta *sta, +			     u16 old_links, u16 new_links) +{ +	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); +	int ret; + +	mutex_lock(&mvm->mutex); +	ret = iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); +	mutex_unlock(&mvm->mutex); + +	return ret; +} + +const struct ieee80211_ops iwl_mvm_mld_hw_ops = { +	.tx = iwl_mvm_mac_tx, +	.wake_tx_queue = iwl_mvm_mac_wake_tx_queue, +	.ampdu_action = iwl_mvm_mac_ampdu_action, +	.get_antenna = iwl_mvm_op_get_antenna, +	.start = iwl_mvm_mac_start, +	.reconfig_complete = iwl_mvm_mac_reconfig_complete, +	.stop = iwl_mvm_mac_stop, +	.add_interface = iwl_mvm_mld_mac_add_interface, +	.remove_interface = iwl_mvm_mld_mac_remove_interface, +	.config = iwl_mvm_mac_config, +	.prepare_multicast = iwl_mvm_prepare_multicast, +	.configure_filter = iwl_mvm_configure_filter, +	.config_iface_filter = iwl_mvm_mld_config_iface_filter, +	.link_info_changed = iwl_mvm_mld_link_info_changed, +	.vif_cfg_changed = iwl_mvm_mld_vif_cfg_changed, +	.hw_scan = iwl_mvm_mac_hw_scan, +	.cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, +	.sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, +	.sta_state = iwl_mvm_mld_mac_sta_state, +	.sta_notify = iwl_mvm_mac_sta_notify, +	.allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, +	.release_buffered_frames = iwl_mvm_mac_release_buffered_frames, +	.set_rts_threshold = iwl_mvm_mac_set_rts_threshold, +	.sta_rc_update = iwl_mvm_sta_rc_update, +	.conf_tx = iwl_mvm_mld_mac_conf_tx, +	.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, +	.mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, +	.mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, +	.flush = iwl_mvm_mac_flush, +	.sched_scan_start = iwl_mvm_mac_sched_scan_start, +	.sched_scan_stop = iwl_mvm_mac_sched_scan_stop, +	.set_key = iwl_mvm_mac_set_key, +	.update_tkip_key = iwl_mvm_mac_update_tkip_key, +	.remain_on_channel = iwl_mvm_mld_roc, +	.cancel_remain_on_channel = iwl_mvm_cancel_roc, +	.add_chanctx = iwl_mvm_add_chanctx, +	.remove_chanctx = iwl_mvm_remove_chanctx, +	.change_chanctx = iwl_mvm_change_chanctx, +	.assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, +	.unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx, +	.switch_vif_chanctx = iwl_mvm_mld_switch_vif_chanctx, + +	.start_ap = iwl_mvm_mld_start_ap, +	.stop_ap = iwl_mvm_mld_stop_ap, +	.join_ibss = iwl_mvm_mld_start_ibss, +	.leave_ibss = iwl_mvm_mld_stop_ibss, + +	.tx_last_beacon = iwl_mvm_tx_last_beacon, + +	.set_tim = iwl_mvm_set_tim, + +	.channel_switch = iwl_mvm_channel_switch, +	.pre_channel_switch = iwl_mvm_pre_channel_switch, +	.post_channel_switch = iwl_mvm_post_channel_switch, +	.abort_channel_switch = iwl_mvm_abort_channel_switch, +	.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon, + +	.tdls_channel_switch = iwl_mvm_tdls_channel_switch, +	.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, +	.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, + +	.event_callback = iwl_mvm_mac_event_callback, + +	.sync_rx_queues = iwl_mvm_sync_rx_queues, + +	CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) + +#ifdef CONFIG_PM_SLEEP +	/* look at d3.c */ +	.suspend = iwl_mvm_suspend, +	.resume = iwl_mvm_resume, +	.set_wakeup = iwl_mvm_set_wakeup, +	.set_rekey_data = iwl_mvm_set_rekey_data, +#if IS_ENABLED(CONFIG_IPV6) +	.ipv6_addr_change = iwl_mvm_ipv6_addr_change, +#endif +	.set_default_unicast_key = iwl_mvm_set_default_unicast_key, +#endif +	.get_survey = iwl_mvm_mac_get_survey, +	.sta_statistics = iwl_mvm_mac_sta_statistics, +	.get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats, +	.start_pmsr = iwl_mvm_start_pmsr, +	.abort_pmsr = iwl_mvm_abort_pmsr, + +#ifdef CONFIG_IWLWIFI_DEBUGFS +	.sta_add_debugfs = iwl_mvm_sta_add_debugfs, +#endif +	.set_hw_timestamp = iwl_mvm_set_hw_timestamp, + +	.change_vif_links = iwl_mvm_mld_change_vif_links, +	.change_sta_links = iwl_mvm_mld_change_sta_links, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c new file mode 100644 index 000000000000..85a4ce8449ad --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -0,0 +1,1165 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" +#include "time-sync.h" +#include "sta.h" + +u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, +			   int filter_link_id) +{ +	struct iwl_mvm_sta *mvmsta; +	unsigned int link_id; +	u32 result = 0; + +	if (!sta) +		return 0; + +	mvmsta = iwl_mvm_sta_from_mac80211(sta); + +	/* it's easy when the STA is not an MLD */ +	if (!sta->valid_links) +		return BIT(mvmsta->deflink.sta_id); + +	/* but if it is an MLD, get the mask of all the FW STAs it has ... */ +	for (link_id = 0; link_id < ARRAY_SIZE(mvmsta->link); link_id++) { +		struct iwl_mvm_link_sta *link_sta; + +		/* unless we have a specific link in mind */ +		if (filter_link_id >= 0 && link_id != filter_link_id) +			continue; + +		link_sta = +			rcu_dereference_check(mvmsta->link[link_id], +					      lockdep_is_held(&mvm->mutex)); +		if (!link_sta) +			continue; + +		result |= BIT(link_sta->sta_id); +	} + +	return result; +} + +static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm, +				    struct iwl_mvm_sta_cfg_cmd *cmd) +{ +	int ret = iwl_mvm_send_cmd_pdu(mvm, +				       WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), +				       0, sizeof(*cmd), cmd); +	if (ret) +		IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); +	return ret; +} + +/* + * Add an internal station to the FW table + */ +static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm, +					 struct iwl_mvm_int_sta *sta, +					 const u8 *addr, int link_id) +{ +	struct iwl_mvm_sta_cfg_cmd cmd; + +	lockdep_assert_held(&mvm->mutex); + +	memset(&cmd, 0, sizeof(cmd)); +	cmd.sta_id = cpu_to_le32((u8)sta->sta_id); + +	cmd.link_id = cpu_to_le32(link_id); + +	cmd.station_type = cpu_to_le32(sta->type); + +	if (addr) { +		memcpy(cmd.peer_mld_address, addr, ETH_ALEN); +		memcpy(cmd.peer_link_address, addr, ETH_ALEN); +	} + +	return iwl_mvm_mld_send_sta_cmd(mvm, &cmd); +} + +/* + * Remove a station from the FW table. Before sending the command to remove + * the station validate that the station is indeed known to the driver (sanity + * only). + */ +static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id) +{ +	struct iwl_mvm_remove_sta_cmd rm_sta_cmd = { +		.sta_id = cpu_to_le32(sta_id), +	}; +	int ret; + +	/* Note: internal stations are marked as error values */ +	if (!rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) { +		IWL_ERR(mvm, "Invalid station id %d\n", sta_id); +		return -EINVAL; +	} + +	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD), +				   0, sizeof(rm_sta_cmd), &rm_sta_cmd); +	if (ret) { +		IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); +		return ret; +	} + +	return 0; +} + +static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm, +				     struct iwl_mvm_int_sta *sta, +				     u32 lmac_id) +{ +	int ret; + +	struct iwl_mvm_aux_sta_cmd cmd = { +		.sta_id = cpu_to_le32(sta->sta_id), +		.lmac_id = cpu_to_le32(lmac_id), +	}; + +	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD), +				   0, sizeof(cmd), &cmd); +	if (ret) +		IWL_ERR(mvm, "Failed to send AUX_STA_CMD\n"); +	return ret; +} + +/* + * Adds an internal sta to the FW table with its queues + */ +static int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm, +					      struct iwl_mvm_int_sta *sta, +					      const u8 *addr, int link_id, +					      u16 *queue, u8 tid, +					      unsigned int *_wdg_timeout) +{ +	int ret, txq; +	unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout : +		mvm->trans->trans_cfg->base_params->wd_timeout; + +	if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) +		return -ENOSPC; + +	if (sta->type == STATION_TYPE_AUX) +		ret = iwl_mvm_add_aux_sta_to_fw(mvm, sta, link_id); +	else +		ret = iwl_mvm_mld_add_int_sta_to_fw(mvm, sta, addr, link_id); +	if (ret) +		return ret; + +	/* +	 * For 22000 firmware and on we cannot add queue to a station unknown +	 * to firmware so enable queue here - after the station was added +	 */ +	txq = iwl_mvm_tvqm_enable_txq(mvm, NULL, sta->sta_id, tid, +				      wdg_timeout); +	if (txq < 0) { +		iwl_mvm_mld_rm_sta_from_fw(mvm, sta->sta_id); +		return txq; +	} +	*queue = txq; + +	return 0; +} + +/* + * Adds a new int sta: allocate it in the driver, add it to the FW table, + * and add its queues. + */ +static int iwl_mvm_mld_add_int_sta(struct iwl_mvm *mvm, +				   struct iwl_mvm_int_sta *int_sta, u16 *queue, +				   enum nl80211_iftype iftype, +				   enum iwl_fw_sta_type sta_type, +				   int link_id, const u8 *addr, u8 tid, +				   unsigned int *wdg_timeout) +{ +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	/* qmask argument is not used in the new tx api, send a don't care */ +	ret = iwl_mvm_allocate_int_sta(mvm, int_sta, 0, iftype, +				       sta_type); +	if (ret) +		return ret; + +	ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, int_sta, addr, link_id, +						 queue, tid, wdg_timeout); +	if (ret) { +		iwl_mvm_dealloc_int_sta(mvm, int_sta); +		return ret; +	} + +	return 0; +} + +/* Allocate a new station entry for the broadcast station to the given vif, + * and send it to the FW. + * Note that each P2P mac should have its own broadcast station. + */ +int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *mvm_link = +		mvmvif->link[link_conf->link_id]; +	struct iwl_mvm_int_sta *bsta = &mvm_link->bcast_sta; +	static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +	const u8 *baddr = _baddr; +	unsigned int wdg_timeout = +		iwl_mvm_get_wd_timeout(mvm, vif, false, false); +	u16 *queue; + +	lockdep_assert_held(&mvm->mutex); + +	if (vif->type == NL80211_IFTYPE_ADHOC) +		baddr = link_conf->bssid; + +	if (vif->type == NL80211_IFTYPE_AP || +	    vif->type == NL80211_IFTYPE_ADHOC) { +		queue = &mvm_link->mgmt_queue; +	} else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { +		queue = &mvm->p2p_dev_queue; +	} else { +		WARN(1, "Missing required TXQ for adding bcast STA\n"); +		return -EINVAL; +	} + +	return iwl_mvm_mld_add_int_sta(mvm, bsta, queue, +				       ieee80211_vif_type_p2p(vif), +				       STATION_TYPE_BCAST_MGMT, +				       mvm_link->fw_link_id, baddr, +				       IWL_MAX_TID_COUNT, &wdg_timeout); +} + +/* Allocate a new station entry for the broadcast station to the given vif, + * and send it to the FW. + * Note that each AP/GO mac should have its own multicast station. + */ +int iwl_mvm_mld_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *mvm_link = +		mvmvif->link[link_conf->link_id]; +	struct iwl_mvm_int_sta *msta = &mvm_link->mcast_sta; +	static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; +	const u8 *maddr = _maddr; +	unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); + +	lockdep_assert_held(&mvm->mutex); + +	if (WARN_ON(vif->type != NL80211_IFTYPE_AP && +		    vif->type != NL80211_IFTYPE_ADHOC)) +		return -EOPNOTSUPP; + +	/* In IBSS, ieee80211_check_queues() sets the cab_queue to be +	 * invalid, so make sure we use the queue we want. +	 * Note that this is done here as we want to avoid making DQA +	 * changes in mac80211 layer. +	 */ +	if (vif->type == NL80211_IFTYPE_ADHOC) +		mvm_link->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; + +	return iwl_mvm_mld_add_int_sta(mvm, msta, &mvm_link->cab_queue, +				       vif->type, STATION_TYPE_MCAST, +				       mvm_link->fw_link_id, maddr, 0, +				       &timeout); +} + +/* Allocate a new station entry for the sniffer station to the given vif, + * and send it to the FW. + */ +int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *mvm_link = +		mvmvif->link[link_conf->link_id]; + +	lockdep_assert_held(&mvm->mutex); + +	return iwl_mvm_mld_add_int_sta(mvm, &mvm->snif_sta, &mvm->snif_queue, +				       vif->type, STATION_TYPE_BCAST_MGMT, +				       mvm_link->fw_link_id, NULL, +				       IWL_MAX_TID_COUNT, NULL); +} + +int iwl_mvm_mld_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id) +{ +	lockdep_assert_held(&mvm->mutex); + +	/* In CDB NICs we need to specify which lmac to use for aux activity; +	 * use the link_id argument place to send lmac_id to the function. +	 */ +	return iwl_mvm_mld_add_int_sta(mvm, &mvm->aux_sta, &mvm->aux_queue, +				       NL80211_IFTYPE_UNSPECIFIED, +				       STATION_TYPE_AUX, lmac_id, NULL, +				       IWL_MAX_TID_COUNT, NULL); +} + +static int iwl_mvm_mld_disable_txq(struct iwl_mvm *mvm, u32 sta_mask, +				   u16 *queueptr, u8 tid) +{ +	int queue = *queueptr; +	int ret = 0; + +	if (tid == IWL_MAX_TID_COUNT) +		tid = IWL_MGMT_TID; + +	if (mvm->sta_remove_requires_queue_remove) { +		u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, +				     SCD_QUEUE_CONFIG_CMD); +		struct iwl_scd_queue_cfg_cmd remove_cmd = { +			.operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), +			.u.remove.tid = cpu_to_le32(tid), +			.u.remove.sta_mask = cpu_to_le32(sta_mask), +		}; + +		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, +					   sizeof(remove_cmd), +					   &remove_cmd); +	} + +	iwl_trans_txq_free(mvm->trans, queue); +	*queueptr = IWL_MVM_INVALID_QUEUE; + +	return ret; +} + +/* Removes a sta from the FW table, disable its queues, and dealloc it + */ +static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm, +				  struct iwl_mvm_int_sta *int_sta, +				  bool flush, u8 tid, u16 *queuptr) +{ +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	if (WARN_ON_ONCE(int_sta->sta_id == IWL_MVM_INVALID_STA)) +		return -EINVAL; + +	if (flush) +		iwl_mvm_flush_sta(mvm, int_sta, true); + +	iwl_mvm_mld_disable_txq(mvm, BIT(int_sta->sta_id), queuptr, tid); + +	ret = iwl_mvm_mld_rm_sta_from_fw(mvm, int_sta->sta_id); +	if (ret) +		IWL_WARN(mvm, "Failed sending remove station\n"); + +	iwl_mvm_dealloc_int_sta(mvm, int_sta); + +	return ret; +} + +int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id]; +	u16 *queueptr; + +	lockdep_assert_held(&mvm->mutex); + +	switch (vif->type) { +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_ADHOC: +		queueptr = &link->mgmt_queue; +		break; +	case NL80211_IFTYPE_P2P_DEVICE: +		queueptr = &mvm->p2p_dev_queue; +		break; +	default: +		WARN(1, "Can't free bcast queue on vif type %d\n", +		     vif->type); +		return -EINVAL; +	} + +	return iwl_mvm_mld_rm_int_sta(mvm, &link->bcast_sta, +				      true, IWL_MAX_TID_COUNT, queueptr); +} + +/* Send the FW a request to remove the station from it's internal data + * structures, and in addition remove it from the local data structure. + */ +int iwl_mvm_mld_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *link = mvmvif->link[link_conf->link_id]; + +	lockdep_assert_held(&mvm->mutex); + +	return iwl_mvm_mld_rm_int_sta(mvm, &link->mcast_sta, true, 0, +				      &link->cab_queue); +} + +int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +	lockdep_assert_held(&mvm->mutex); + +	return iwl_mvm_mld_rm_int_sta(mvm, &mvm->snif_sta, false, +				      IWL_MAX_TID_COUNT, &mvm->snif_queue); +} + +int iwl_mvm_mld_rm_aux_sta(struct iwl_mvm *mvm) +{ +	lockdep_assert_held(&mvm->mutex); + +	return iwl_mvm_mld_rm_int_sta(mvm, &mvm->aux_sta, false, +				      IWL_MAX_TID_COUNT, &mvm->aux_queue); +} + +/* send a cfg sta command to add/update a sta in firmware */ +static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, +			       struct ieee80211_vif *vif, +			       struct ieee80211_link_sta *link_sta, +			       struct ieee80211_bss_conf *link_conf, +			       struct iwl_mvm_link_sta *mvm_link_sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *link_info = +					mvm_vif->link[link_conf->link_id]; +	struct iwl_mvm_sta_cfg_cmd cmd = { +		.sta_id = cpu_to_le32(mvm_link_sta->sta_id), +		.station_type = cpu_to_le32(mvm_sta->sta_type), +	}; +	u32 agg_size = 0, mpdu_dens = 0; + +	/* when adding sta, link should exist in FW */ +	if (WARN_ON(link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) +		return -EINVAL; + +	cmd.link_id = cpu_to_le32(link_info->fw_link_id); + +	memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN); +	memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN); + +	if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC) +		cmd.assoc_id = cpu_to_le32(sta->aid); + +	switch (link_sta->rx_nss) { +	case 1: +		cmd.mimo = cpu_to_le32(0); +		break; +	case 2 ... 8: +		cmd.mimo = cpu_to_le32(1); +		break; +	} + +	switch (sta->deflink.smps_mode) { +	case IEEE80211_SMPS_AUTOMATIC: +	case IEEE80211_SMPS_NUM_MODES: +		WARN_ON(1); +		break; +	case IEEE80211_SMPS_STATIC: +		/* override NSS */ +		cmd.mimo = cpu_to_le32(0); +		break; +	case IEEE80211_SMPS_DYNAMIC: +		cmd.mimo_protection = cpu_to_le32(1); +		break; +	case IEEE80211_SMPS_OFF: +		/* nothing */ +		break; +	} + +	mpdu_dens = iwl_mvm_get_sta_ampdu_dens(link_sta, link_conf, &agg_size); +	cmd.tx_ampdu_spacing = cpu_to_le32(mpdu_dens); +	cmd.tx_ampdu_max_size = cpu_to_le32(agg_size); + +	if (sta->wme) { +		cmd.sp_length = +			cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128); +		cmd.uapsd_acs = cpu_to_le32(iwl_mvm_get_sta_uapsd_acs(sta)); +	} + +	if (link_sta->he_cap.has_he) { +		cmd.trig_rnd_alloc = +			cpu_to_le32(link_conf->uora_exists ? 1 : 0); + +		/* PPE Thresholds */ +		iwl_mvm_set_sta_pkt_ext(mvm, link_sta, &cmd.pkt_ext); + +		/* HTC flags */ +		cmd.htc_flags = iwl_mvm_get_sta_htc_flags(sta, link_sta); + +		if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] & +		    IEEE80211_HE_MAC_CAP2_ACK_EN) +			cmd.ack_enabled = cpu_to_le32(1); +	} + +	return iwl_mvm_mld_send_sta_cmd(mvm, &cmd); +} + +static void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, +				      struct iwl_mvm_sta *mvm_sta, +				      struct iwl_mvm_link_sta *mvm_sta_link, +				      unsigned int link_id, +				      bool is_in_fw) +{ +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id], +			 is_in_fw ? ERR_PTR(-EINVAL) : NULL); +	RCU_INIT_POINTER(mvm->fw_id_to_link_sta[mvm_sta_link->sta_id], NULL); +	RCU_INIT_POINTER(mvm_sta->link[link_id], NULL); + +	if (mvm_sta_link != &mvm_sta->deflink) +		kfree_rcu(mvm_sta_link, rcu_head); +} + +static void iwl_mvm_mld_sta_rm_all_sta_links(struct iwl_mvm *mvm, +					     struct iwl_mvm_sta *mvm_sta) +{ +	unsigned int link_id; + +	for (link_id = 0; link_id < ARRAY_SIZE(mvm_sta->link); link_id++) { +		struct iwl_mvm_link_sta *link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (!link) +			continue; + +		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, link, link_id, false); +	} +} + +static int iwl_mvm_mld_alloc_sta_link(struct iwl_mvm *mvm, +				      struct ieee80211_vif *vif, +				      struct ieee80211_sta *sta, +				      unsigned int link_id) +{ +	struct ieee80211_link_sta *link_sta = +		link_sta_dereference_protected(sta, link_id); +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct iwl_mvm_link_sta *link; +	u32 sta_id = iwl_mvm_find_free_sta_id(mvm, +					  ieee80211_vif_type_p2p(vif)); + +	if (sta_id == IWL_MVM_INVALID_STA) +		return -ENOSPC; + +	if (rcu_access_pointer(sta->link[link_id]) == &sta->deflink) { +		link = &mvm_sta->deflink; +	} else { +		link = kzalloc(sizeof(*link), GFP_KERNEL); +		if (!link) +			return -ENOMEM; +	} + +	link->sta_id = sta_id; +	rcu_assign_pointer(mvm_sta->link[link_id], link); +	rcu_assign_pointer(mvm->fw_id_to_mac_id[link->sta_id], sta); +	rcu_assign_pointer(mvm->fw_id_to_link_sta[link->sta_id], +			   link_sta); + +	return 0; +} + +/* allocate all the links of a sta, called when the station is first added */ +static int iwl_mvm_mld_alloc_sta_links(struct iwl_mvm *mvm, +				       struct ieee80211_vif *vif, +				       struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	unsigned int link_id; +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) { +		if (!rcu_access_pointer(sta->link[link_id]) || +		    mvm_sta->link[link_id]) +			continue; + +		ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, link_id); +		if (ret) +			goto err; +	} + +	return 0; + +err: +	iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta); +	return ret; +} + +static void iwl_mvm_mld_set_ap_sta_id(struct ieee80211_sta *sta, +				      struct iwl_mvm_vif_link_info *vif_link, +				      struct iwl_mvm_link_sta *sta_link) +{ +	if (!sta->tdls) { +		WARN_ON(vif_link->ap_sta_id != IWL_MVM_INVALID_STA); +		vif_link->ap_sta_id = sta_link->sta_id; +	} else { +		WARN_ON(vif_link->ap_sta_id == IWL_MVM_INVALID_STA); +	} +} + +/* FIXME: consider waiting for mac80211 to add the STA instead of allocating + * queues here + */ +static int iwl_mvm_alloc_sta_after_restart(struct iwl_mvm *mvm, +					   struct ieee80211_vif *vif, +					   struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; +	/* no active link found */ +	int ret = -EINVAL; +	int sta_id; + +	/* First add an empty station since allocating a queue requires +	 * a valid station. Since we need a link_id to allocate a station, +	 * pick up the first valid one. +	 */ +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct iwl_mvm_vif_link_info *mvm_link; +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (!link_conf) +			continue; + +		mvm_link = mvmvif->link[link_conf->link_id]; + +		if (!mvm_link || !mvm_link_sta) +			continue; + +		sta_id = mvm_link_sta->sta_id; +		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, +					  link_conf, mvm_link_sta); +		if (ret) +			return ret; + +		rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); +		rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id], link_sta); +		ret = 0; +	} + +	iwl_mvm_realloc_queues_after_restart(mvm, sta); + +	return ret; +} + +int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			struct ieee80211_sta *sta) +{ +	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	unsigned long link_sta_added_to_fw = 0; +	struct ieee80211_link_sta *link_sta; +	int ret = 0; +	unsigned int link_id; + +	lockdep_assert_held(&mvm->mutex); + +	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { +		ret = iwl_mvm_mld_alloc_sta_links(mvm, vif, sta); +		if (ret) +			return ret; + +		spin_lock_init(&mvm_sta->lock); + +		ret = iwl_mvm_sta_init(mvm, vif, sta, IWL_MVM_INVALID_STA, +				       STATION_TYPE_PEER); +	} else { +		ret = iwl_mvm_alloc_sta_after_restart(mvm, vif, sta); +	} + +	if (ret) +		goto err; + +	/* at this stage sta link pointers are already allocated */ +	ret = iwl_mvm_mld_update_sta(mvm, vif, sta); + +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (WARN_ON(!link_conf || !mvm_link_sta)) +			goto err; + +		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf, +					  mvm_link_sta); +		if (ret) +			goto err; + +		link_sta_added_to_fw |= BIT(link_id); + +		if (vif->type == NL80211_IFTYPE_STATION) +			iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif->link[link_id], +						  mvm_link_sta); +	} + +	return 0; + +err: +	/* remove all already allocated stations in FW */ +	for_each_set_bit(link_id, &link_sta_added_to_fw, +			 IEEE80211_MLD_MAX_NUM_LINKS) { +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_link_sta->sta_id); +	} + +	/* free all sta resources in the driver */ +	iwl_mvm_mld_sta_rm_all_sta_links(mvm, mvm_sta); +	return ret; +} + +int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			   struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; +	int ret = -EINVAL; + +	lockdep_assert_held(&mvm->mutex); + +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (WARN_ON(!link_conf || !mvm_link_sta)) +			return -EINVAL; + +		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf, +					  mvm_link_sta); + +		if (ret) { +			IWL_ERR(mvm, "Failed to update sta link %d\n", link_id); +			break; +		} +	} + +	return ret; +} + +static void iwl_mvm_mld_disable_sta_queues(struct iwl_mvm *mvm, +					   struct ieee80211_vif *vif, +					   struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	u32 sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1); +	int i; + +	lockdep_assert_held(&mvm->mutex); + +	for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) { +		if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE) +			continue; + +		iwl_mvm_mld_disable_txq(mvm, sta_mask, +					&mvm_sta->tid_data[i].txq_id, i); +		mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; +	} + +	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { +		struct iwl_mvm_txq *mvmtxq = +			iwl_mvm_txq_from_mac80211(sta->txq[i]); + +		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; +	} +} + +int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		       struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id; +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	/* flush its queues here since we are freeing mvm_sta */ +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (WARN_ON(!mvm_link_sta)) +			return -EINVAL; + +		ret = iwl_mvm_flush_sta_tids(mvm, mvm_link_sta->sta_id, +					     0xffff); +		if (ret) +			return ret; +	} + +	ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); +	if (ret) +		return ret; + +	iwl_mvm_mld_disable_sta_queues(mvm, vif, sta); + +	for_each_sta_active_link(vif, sta, link_sta, link_id) { +		struct iwl_mvm_link_sta *mvm_link_sta = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); +		bool stay_in_fw; + +		stay_in_fw = iwl_mvm_sta_del(mvm, vif, sta, link_sta, &ret); +		if (ret) +			break; + +		if (!stay_in_fw) +			ret = iwl_mvm_mld_rm_sta_from_fw(mvm, +							 mvm_link_sta->sta_id); + +		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, +					  link_id, stay_in_fw); +	} + +	return ret; +} + +int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id) +{ +	int ret = iwl_mvm_mld_rm_sta_from_fw(mvm, sta_id); + +	lockdep_assert_held(&mvm->mutex); + +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); +	RCU_INIT_POINTER(mvm->fw_id_to_link_sta[sta_id], NULL); +	return ret; +} + +void iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm *mvm, +				       struct iwl_mvm_sta *mvmsta, +				       bool disable) +{ +	struct iwl_mvm_sta_disable_tx_cmd cmd; +	int ret; + +	cmd.sta_id = cpu_to_le32(mvmsta->deflink.sta_id); +	cmd.disable = cpu_to_le32(disable); + +	ret = iwl_mvm_send_cmd_pdu(mvm, +				   WIDE_ID(MAC_CONF_GROUP, STA_DISABLE_TX_CMD), +				   CMD_ASYNC, sizeof(cmd), &cmd); +	if (ret) +		IWL_ERR(mvm, +			"Failed to send STA_DISABLE_TX_CMD command (%d)\n", +			ret); +} + +void iwl_mvm_mld_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, +					  struct ieee80211_sta *sta, +					  bool disable) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + +	spin_lock_bh(&mvm_sta->lock); + +	if (mvm_sta->disable_tx == disable) { +		spin_unlock_bh(&mvm_sta->lock); +		return; +	} + +	iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable); + +	spin_unlock_bh(&mvm_sta->lock); +} + +void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm, +					   struct iwl_mvm_vif *mvmvif, +					   bool disable) +{ +	struct ieee80211_sta *sta; +	struct iwl_mvm_sta *mvm_sta; +	int i; + +	rcu_read_lock(); + +	/* Block/unblock all the stations of the given mvmvif */ +	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { +		sta = rcu_dereference(mvm->fw_id_to_mac_id[i]); +		if (IS_ERR_OR_NULL(sta)) +			continue; + +		mvm_sta = iwl_mvm_sta_from_mac80211(sta); +		if (mvm_sta->mac_id_n_color != +		    FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) +			continue; + +		iwl_mvm_mld_sta_modify_disable_tx(mvm, mvm_sta, disable); +	} + +	rcu_read_unlock(); +} + +static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm, +					 struct ieee80211_sta *sta, +					 u32 old_sta_mask, +					 u32 new_sta_mask) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct iwl_scd_queue_cfg_cmd cmd = { +		.operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY), +		.u.modify.old_sta_mask = cpu_to_le32(old_sta_mask), +		.u.modify.new_sta_mask = cpu_to_le32(new_sta_mask), +	}; +	struct iwl_host_cmd hcmd = { +		.id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD), +		.len[0] = sizeof(cmd), +		.data[0] = &cmd +	}; +	int tid; +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) { +		struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid]; +		int txq_id = tid_data->txq_id; + +		if (txq_id == IWL_MVM_INVALID_QUEUE) +			continue; + +		if (tid == IWL_MAX_TID_COUNT) +			cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID); +		else +			cmd.u.modify.tid = cpu_to_le32(tid); + +		ret = iwl_mvm_send_cmd(mvm, &hcmd); +		if (ret) +			return ret; +	} + +	return 0; +} + +static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm, +					u32 old_sta_mask, +					u32 new_sta_mask) +{ +	struct iwl_rx_baid_cfg_cmd cmd = { +		.action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY), +		.modify.old_sta_id_mask = cpu_to_le32(old_sta_mask), +		.modify.new_sta_id_mask = cpu_to_le32(new_sta_mask), +	}; +	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); +	int baid; + +	BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); + +	for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) { +		struct iwl_mvm_baid_data *data; +		int ret; + +		data = rcu_dereference_protected(mvm->baid_map[baid], +						 lockdep_is_held(&mvm->mutex)); +		if (!data) +			continue; + +		if (!(data->sta_mask & old_sta_mask)) +			continue; + +		WARN_ONCE(data->sta_mask != old_sta_mask, +			  "BAID data for %d corrupted - expected 0x%x found 0x%x\n", +			  baid, old_sta_mask, data->sta_mask); + +		cmd.modify.tid = cpu_to_le32(data->tid); + +		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); +		data->sta_mask = new_sta_mask; +		if (ret) +			return ret; +	} + +	return 0; +} + +static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif, +					    struct ieee80211_sta *sta, +					    u32 old_sta_mask, +					    u32 new_sta_mask) +{ +	int ret; + +	ret = iwl_mvm_mld_update_sta_queues(mvm, sta, +					    old_sta_mask, +					    new_sta_mask); +	if (ret) +		return ret; + +	ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta, +					  old_sta_mask, +					  new_sta_mask); +	if (ret) +		return ret; + +	return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask); +} + +int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif, +				 struct ieee80211_sta *sta, +				 u16 old_links, u16 new_links) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_link_sta *mvm_sta_link; +	struct iwl_mvm_vif_link_info *mvm_vif_link; +	unsigned long links_to_add = ~old_links & new_links; +	unsigned long links_to_rem = old_links & ~new_links; +	unsigned long old_links_long = old_links; +	u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0; +	unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0; +	unsigned int link_id; +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	for_each_set_bit(link_id, &old_links_long, +			 IEEE80211_MLD_MAX_NUM_LINKS) { +		mvm_sta_link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (WARN_ON(!mvm_sta_link)) { +			ret = -EINVAL; +			goto err; +		} + +		current_sta_mask |= BIT(mvm_sta_link->sta_id); +		if (links_to_rem & BIT(link_id)) +			sta_mask_to_rem |= BIT(mvm_sta_link->sta_id); +	} + +	if (sta_mask_to_rem) { +		ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta, +						       current_sta_mask, +						       current_sta_mask & +							~sta_mask_to_rem); +		if (WARN_ON(ret)) +			goto err; + +		current_sta_mask &= ~sta_mask_to_rem; +	} + +	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { +		mvm_sta_link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); +		mvm_vif_link = mvm_vif->link[link_id]; + +		if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) { +			ret = -EINVAL; +			goto err; +		} + +		ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id); +		if (WARN_ON(ret)) +			goto err; + +		if (vif->type == NL80211_IFTYPE_STATION) +			mvm_vif_link->ap_sta_id = IWL_MVM_INVALID_STA; + +		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id, +					  false); +	} + +	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { +		struct ieee80211_bss_conf *link_conf = +			link_conf_dereference_protected(vif, link_id); +		struct ieee80211_link_sta *link_sta = +			link_sta_dereference_protected(sta, link_id); +		mvm_vif_link = mvm_vif->link[link_id]; + +		if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta || +			    mvm_sta->link[link_id])) { +			ret = -EINVAL; +			goto err; +		} + +		ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta, link_id); +		if (WARN_ON(ret)) +			goto err; + +		link_sta->agg.max_rc_amsdu_len = 1; +		ieee80211_sta_recalc_aggregates(sta); + +		mvm_sta_link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		if (WARN_ON(!mvm_sta_link)) { +			ret = -EINVAL; +			goto err; +		} + +		if (vif->type == NL80211_IFTYPE_STATION) +			iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link, +						  mvm_sta_link); + +		link_sta_allocated |= BIT(link_id); + +		sta_mask_added |= BIT(mvm_sta_link->sta_id); + +		ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf, +					  mvm_sta_link); +		if (WARN_ON(ret)) +			goto err; + +		link_sta_added_to_fw |= BIT(link_id); + +		iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link); +	} + +	if (sta_mask_added) { +		ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta, +						       current_sta_mask, +						       current_sta_mask | +							sta_mask_added); +		if (WARN_ON(ret)) +			goto err; +	} + +	return 0; + +err: +	/* remove all already allocated stations in FW */ +	for_each_set_bit(link_id, &link_sta_added_to_fw, +			 IEEE80211_MLD_MAX_NUM_LINKS) { +		mvm_sta_link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id); +	} + +	/* remove all already allocated station links in driver */ +	for_each_set_bit(link_id, &link_sta_allocated, +			 IEEE80211_MLD_MAX_NUM_LINKS) { +		mvm_sta_link = +			rcu_dereference_protected(mvm_sta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); + +		iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id, +					  false); +	} + +	return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 90bc95d96a78..9e5008e0e47f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -16,6 +16,8 @@  #include <linux/thermal.h>  #endif +#include <linux/ptp_clock_kernel.h> +  #include <linux/ktime.h>  #include "iwl-op-mode.h" @@ -71,7 +73,11 @@  /* offchannel queue towards mac80211 */  #define IWL_MVM_OFFCHANNEL_QUEUE 0 +/* invalid value for FW link id */ +#define IWL_MVM_FW_LINK_ID_INVALID 0xff +  extern const struct ieee80211_ops iwl_mvm_hw_ops; +extern const struct ieee80211_ops iwl_mvm_mld_hw_ops;  /**   * struct iwl_mvm_mod_params - module parameters for iwlmvm @@ -278,11 +284,60 @@ struct iwl_probe_resp_data {  };  /** + * struct iwl_mvm_vif_link_info - per link data in Virtual Interface + * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA + * @fw_link_id: the id of the link according to the FW API + * @bssid: BSSID for this (client) interface + * @bcast_sta: station used for broadcast packets. Used by the following + *	vifs: P2P_DEVICE, GO and AP. + * @beacon_stats: beacon statistics, containing the # of received beacons, + *	# of received beacons accumulated over FW restart, and the current + *	average signal of beacons retrieved from the firmware + * @smps_requests: the SMPS requests of different parts of the driver, + *	combined on update to yield the overall request to mac80211. + * @probe_resp_data: data from FW notification to store NOA and CSA related + *	data to be inserted into probe response. + * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked + * @queue_params: QoS params for this MAC + * @mgmt_queue: queue number for unbufferable management frames + */ +struct iwl_mvm_vif_link_info { +	u8 bssid[ETH_ALEN]; +	u8 ap_sta_id; +	u8 fw_link_id; + +	struct iwl_mvm_int_sta bcast_sta; +	struct iwl_mvm_int_sta mcast_sta; + +	struct { +		u32 num_beacons, accu_num_beacons; +		u8 avg_signal; +	} beacon_stats; + +	enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; +	struct iwl_probe_resp_data __rcu *probe_resp_data; + +	bool he_ru_2mhz_block; +	bool active; + +	u16 cab_queue; +	/* Assigned while mac80211 has the link in a channel context, +	 * or, for P2P Device, while it exists. +	 */ +	struct iwl_mvm_phy_ctxt *phy_ctxt; +	/* QoS data from mac80211, need to store this here +	 * as mac80211 has a separate callback but we need +	 * to have the data for the MAC context +	 */ +	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + +	u16 mgmt_queue; +}; + +/**   * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context   * @id: between 0 and 3   * @color: to solve races upon MAC addition and removal - * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA - * @bssid: BSSID for this (client) interface   * @associated: indicates that we're currently associated, used only for   *	managing the firmware state in iwl_mvm_bss_info_changed_station()   * @ap_assoc_sta_count: count of stations associated to us - valid only @@ -290,7 +345,7 @@ struct iwl_probe_resp_data {   * @uploaded: indicates the MAC context has been added to the device   * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface   *	should get quota etc. - * @pm_enabled - Indicate if MAC power management is allowed + * @pm_enabled - indicate if MAC power management is allowed   * @monitor_active: indicates that monitor context is configured, and that the   *	interface should get quota etc.   * @low_latency: bit flags for low latency @@ -299,68 +354,31 @@ struct iwl_probe_resp_data {   *	as a result from low_latency bit flags and takes force into account.   * @authorized: indicates the AP station was set to authorized   * @ps_disabled: indicates that this interface requires PS to be disabled - * @queue_params: QoS params for this MAC - * @bcast_sta: station used for broadcast packets. Used by the following - *  vifs: P2P_DEVICE, GO and AP. - * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the SMPS requests of different parts of the driver, - *	combined on update to yield the overall request to mac80211. - * @beacon_stats: beacon statistics, containing the # of received beacons, - *	# of received beacons accumulated over FW restart, and the current - *	average signal of beacons retrieved from the firmware + * @csa_countdown: indicates that CSA countdown may be started   * @csa_failed: CSA failed to schedule time event, report an error later + * @csa_bcn_pending: indicates that we are waiting for a beacon on a new channel   * @features: hw features active for this vif - * @probe_resp_data: data from FW notification to store NOA and CSA related - *	data to be inserted into probe response.   */  struct iwl_mvm_vif {  	struct iwl_mvm *mvm;  	u16 id;  	u16 color; -	u8 ap_sta_id; -	u8 bssid[ETH_ALEN];  	bool associated;  	u8 ap_assoc_sta_count; - -	u16 cab_queue; -  	bool uploaded;  	bool ap_ibss_active;  	bool pm_enabled;  	bool monitor_active; +  	u8 low_latency: 6;  	u8 low_latency_actual: 1; +  	u8 authorized:1;  	bool ps_disabled; -	struct iwl_mvm_vif_bf_data bf_data; - -	struct { -		u32 num_beacons, accu_num_beacons; -		u8 avg_signal; -	} beacon_stats;  	u32 ap_beacon_time; - -	enum iwl_tsf_id tsf_id; - -	/* -	 * QoS data from mac80211, need to store this here -	 * as mac80211 has a separate callback but we need -	 * to have the data for the MAC context -	 */ -	struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; -	struct iwl_mvm_time_event_data time_event_data; -	struct iwl_mvm_time_event_data hs_time_event_data; - -	struct iwl_mvm_int_sta bcast_sta; -	struct iwl_mvm_int_sta mcast_sta; - -	/* -	 * Assigned while mac80211 has the interface in a channel context, -	 * or, for P2P Device, while it exists. -	 */ -	struct iwl_mvm_phy_ctxt *phy_ctxt; +	struct iwl_mvm_vif_bf_data bf_data;  #ifdef CONFIG_PM  	/* WoWLAN GTK rekey data */ @@ -396,40 +414,45 @@ struct iwl_mvm_vif {  	int dbgfs_quota_min;  #endif -	enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; -  	/* FW identified misbehaving AP */  	u8 uapsd_misbehaving_bssid[ETH_ALEN]; -  	struct delayed_work uapsd_nonagg_detected_wk; -	/* Indicates that CSA countdown may be started */  	bool csa_countdown;  	bool csa_failed; +	bool csa_bcn_pending;  	u16 csa_target_freq;  	u16 csa_count;  	u16 csa_misbehave;  	struct delayed_work csa_work; -	/* Indicates that we are waiting for a beacon on a new channel */ -	bool csa_bcn_pending; +	enum iwl_tsf_id tsf_id; + +	struct iwl_mvm_time_event_data time_event_data; +	struct iwl_mvm_time_event_data hs_time_event_data;  	/* TCP Checksum Offload */  	netdev_features_t features; -	struct iwl_probe_resp_data __rcu *probe_resp_data; +	struct ieee80211_sta *ap_sta;  	/* we can only have 2 GTK + 2 IGTK active at a time */  	struct ieee80211_key_conf *ap_early_keys[4]; -	/* 26-tone RU OFDMA transmissions should be blocked */ -	bool he_ru_2mhz_block; -  	struct {  		struct ieee80211_key_conf __rcu *keys[2];  	} bcn_prot; + +	struct iwl_mvm_vif_link_info deflink; +	struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];  }; +#define for_each_mvm_vif_valid_link(mvm_vif, link_id)			\ +	for (link_id = 0;						\ +	     link_id < ARRAY_SIZE((mvm_vif)->link);			\ +	     link_id++)							\ +		if ((mvm_vif)->link[link_id]) +  static inline struct iwl_mvm_vif *  iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)  { @@ -657,7 +680,7 @@ __aligned(roundup_pow_of_two(sizeof(struct _iwl_mvm_reorder_buf_entry)))  /**   * struct iwl_mvm_baid_data - BA session data - * @sta_id: station id + * @sta_mask: current station mask for the BAID   * @tid: tid of the session   * @baid baid of the session   * @timeout: the timeout set in the addba request @@ -671,7 +694,7 @@ __aligned(roundup_pow_of_two(sizeof(struct _iwl_mvm_reorder_buf_entry)))   */  struct iwl_mvm_baid_data {  	struct rcu_head rcu_head; -	u8 sta_id; +	u32 sta_mask;  	u8 tid;  	u8 baid;  	u16 timeout; @@ -729,7 +752,10 @@ struct iwl_mvm_txq {  	struct list_head list;  	u16 txq_id;  	atomic_t tx_request; -	bool stopped; +#define IWL_MVM_TXQ_STATE_STOP_FULL	0 +#define IWL_MVM_TXQ_STATE_STOP_REDIRECT	1 +#define IWL_MVM_TXQ_STATE_READY		2 +	unsigned long state;  };  static inline struct iwl_mvm_txq * @@ -769,6 +795,43 @@ struct iwl_mvm_dqa_txq_info {  	enum iwl_mvm_queue_status status;  }; +struct ptp_data { +	struct ptp_clock *ptp_clock; +	struct ptp_clock_info ptp_clock_info; + +	struct delayed_work dwork; + +	/* The last GP2 reading from the hw */ +	u32 last_gp2; + +	/* number of wraparounds since scale_update_adj_time_ns */ +	u32 wrap_counter; + +	/* GP2 time when the scale was last updated */ +	u32 scale_update_gp2; + +	/* Adjusted time when the scale was last updated in nanoseconds */ +	u64 scale_update_adj_time_ns; + +	/* clock frequency offset, scaled to 65536000000 */ +	u64 scaled_freq; + +	/* Delta between hardware clock and ptp clock in nanoseconds */ +	s64 delta; +}; + +struct iwl_time_sync_data { +	struct sk_buff_head frame_list; +	u8 peer_addr[ETH_ALEN]; +	bool active; +}; + +struct iwl_mei_scan_filter { +	bool is_mei_limited_scan; +	struct sk_buff_head scan_res; +	struct work_struct scan_work; +}; +  struct iwl_mvm {  	/* for logger access */  	struct device *dev; @@ -827,6 +890,7 @@ struct iwl_mvm {  		struct iwl_mvm_tvqm_txq_info tvqm_info[IWL_MAX_TVQM_QUEUES];  	};  	struct work_struct add_stream_wk; /* To add streams to queues */ +	spinlock_t add_stream_lock;  	const char *nvm_file_name;  	struct iwl_nvm_data *nvm_data; @@ -853,6 +917,8 @@ struct iwl_mvm {  	/* data related to data path */  	struct iwl_rx_phy_info last_phy_info;  	struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT_MAX]; +	struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_MVM_STATION_COUNT_MAX]; +	unsigned long fw_link_ids_map;  	u8 rx_ba_sessions;  	/* configured by mac80211 */ @@ -1079,6 +1145,8 @@ struct iwl_mvm {  	struct list_head resp_pasn_list; +	struct ptp_data ptp_data; +  	struct {  		u8 range_resp;  	} cmd_ver; @@ -1096,6 +1164,11 @@ struct iwl_mvm {  	/* does a monitor vif exist (only one can exist hence bool) */  	bool monitor_on; +	/* +	 * primary channel position relative to he whole bandwidth, +	 * in steps of 80 MHz +	 */ +	u8 monitor_p80;  	/* sniffer data to include in radiotap */  	__le16 cur_aid; @@ -1105,8 +1178,13 @@ struct iwl_mvm {  	unsigned long last_reset_or_resume_time_jiffies;  	bool sta_remove_requires_queue_remove; +	bool mld_api_is_used;  	bool pldr_sync; + +	struct iwl_time_sync_data time_sync; + +	struct iwl_mei_scan_filter mei_scan_filter;  };  /* Extract MVM priv from op_mode and _hw */ @@ -1306,7 +1384,7 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm)  {  	return fw_has_capa(&mvm->fw->ucode_capa,  			   IWL_UCODE_TLV_CAPA_CSUM_SUPPORT) && -               !IWL_MVM_HW_CSUM_DISABLE; +		!IWL_MVM_HW_CSUM_DISABLE;  }  static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) @@ -1331,6 +1409,12 @@ static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)  			   IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT);  } +static inline bool iwl_mvm_has_mld_api(const struct iwl_fw *fw) +{ +	return fw_has_capa(&fw->ucode_capa, +			   IWL_UCODE_TLV_CAPA_MLD_API_SUPPORT); +} +  static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm)  {  	/* TODO - replace with TLV once defined */ @@ -1432,6 +1516,19 @@ static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm)  			   IWL_UCODE_TLV_CAPA_CTDP_SUPPORT);  } +static inline bool iwl_mvm_has_new_tx_csum(struct iwl_mvm *mvm) +{ +	if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) +		return false; + +	if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ && +	    CSR_HW_REV_TYPE(mvm->trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && +	    mvm->trans->hw_rev_step <= SILICON_B_STEP) +		return false; + +	return true; +} +  extern const u8 iwl_mvm_ac_to_tx_fifo[];  extern const u8 iwl_mvm_ac_to_gen2_tx_fifo[]; @@ -1472,6 +1569,7 @@ void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags,  				  struct ieee80211_tx_rate *r);  u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx);  u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); +bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  static inline void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)  { @@ -1519,6 +1617,17 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk);  int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal);  int iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id, u16 tids); +/* Utils to extract sta related data */ +__le32 iwl_mvm_get_sta_htc_flags(struct ieee80211_sta *sta, +				 struct ieee80211_link_sta *link_sta); +u8 iwl_mvm_get_sta_uapsd_acs(struct ieee80211_sta *sta); +u32 iwl_mvm_get_sta_ampdu_dens(struct ieee80211_link_sta *link_sta, +			       struct ieee80211_bss_conf *link_conf, +			       u32 *_agg_size); +int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm, +			    struct ieee80211_link_sta *link_sta, +			    struct iwl_he_pkt_ext_v2 *pkt_ext); +  void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);  static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info, @@ -1618,6 +1727,7 @@ void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm,  				     struct iwl_rx_cmd_buffer *rxb);  /* MVM PHY */ +struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm);  int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,  			 struct cfg80211_chan_def *chandef,  			 u8 chains_static, u8 chains_dynamic); @@ -1633,22 +1743,60 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);  u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);  /* MAC (virtual interface) programming */ + +void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif); +void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *link_conf, +				__le32 *cck_rates, __le32 *ofdm_rates); +void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, +				     struct ieee80211_vif *vif, +				     struct ieee80211_bss_conf *link_conf, +				     __le32 *protection_flags, u32 ht_flag, +				     u32 tgg_flag); +void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			       struct ieee80211_bss_conf *link_conf, +			       struct iwl_ac_qos *ac, __le32 *qos_flags); +bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, +				   struct iwl_mvm_vif *mvmvif, +				   struct iwl_he_backoff_conf *trig_based_txf); +void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf, +			      __le64 *dtim_tsf, __le32 *dtim_time, +			      __le32 *assoc_beacon_arrive_time); +__le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, +					      struct ieee80211_vif *vif); +void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, +					      struct iwl_mvm_vif *mvmvif, +					      __le32 *filter_flags, +					      int accept_probe_req_flag, +					      int accept_beacon_flag); +int iwl_mvm_get_mac_type(struct ieee80211_vif *vif); +__le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, +						    struct ieee80211_vif *vif); +__le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, +					       struct ieee80211_vif *vif); +int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				 bool force_assoc_off); +int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			     bool force_assoc_off, const u8 *bssid_override);  int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, -				    struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, -				 struct ieee80211_vif *vif, -				 struct sk_buff *beacon); +				    struct ieee80211_vif *vif, +				    struct ieee80211_bss_conf *link_conf);  int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm,  				     struct sk_buff *beacon,  				     void *data, int len);  u8 iwl_mvm_mac_ctxt_get_beacon_rate(struct iwl_mvm *mvm,  				    struct ieee80211_tx_info *info,  				    struct ieee80211_vif *vif); +u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, +				    struct ieee80211_tx_info *info, +				    struct ieee80211_vif *vif);  u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw,  				      u8 rate_idx);  void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, @@ -1679,6 +1827,93 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm,  int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* Links */ +int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_bss_conf *link_conf); +int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 struct ieee80211_bss_conf *link_conf, +			 u32 changes, bool active); +int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			struct ieee80211_bss_conf *link_conf); +int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 struct ieee80211_bss_conf *link_conf); + +/* AP and IBSS */ +bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, int *ret); +void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif); + +/* BSS Info */ +/** + * struct iwl_mvm_bss_info_changed_ops - callbacks for the bss_info_changed() + * + * Since the only difference between both MLD and + * non-MLD versions of bss_info_changed() is these function calls, + * each version will send its specific function calls to + * %iwl_mvm_bss_info_changed_common(). + * + * @bss_info_changed_sta: pointer to the function that handles changes + *	in bss_info in sta mode + * @bss_info_changed_ap_ibss: pointer to the function that handles changes + *	in bss_info in ap and ibss modes + */ +struct iwl_mvm_bss_info_changed_ops { +	void (*bss_info_changed_sta)(struct iwl_mvm *mvm, +				     struct ieee80211_vif *vif, +				     struct ieee80211_bss_conf *bss_conf, +				     u64 changes); +	void (*bss_info_changed_ap_ibss)(struct iwl_mvm *mvm, +					 struct ieee80211_vif *vif, +					 struct ieee80211_bss_conf *bss_conf, +					 u64 changes); +}; + +void +iwl_mvm_bss_info_changed_common(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_bss_conf *bss_conf, +				struct iwl_mvm_bss_info_changed_ops *callbacks, +				u64 changes); +void +iwl_mvm_bss_info_changed_station_common(struct iwl_mvm *mvm, +					struct ieee80211_vif *vif, +					struct ieee80211_bss_conf *link_conf, +					u64 changes); +void iwl_mvm_bss_info_changed_station_assoc(struct iwl_mvm *mvm, +					    struct ieee80211_vif *vif, +					    u64 changes); + +/* ROC */ +/** + * struct iwl_mvm_roc_ops - callbacks for the remain_on_channel() + * + * Since the only difference between both MLD and + * non-MLD versions of remain_on_channel() is these function calls, + * each version will send its specific function calls to + * %iwl_mvm_roc_common(). + * + * @add_aux_sta_for_hs20: pointer to the function that adds an aux sta + *	for Hot Spot 2.0 + * @switch_phy_ctxt: pointer to the function that switches a vif from one + *	phy_ctx to another + */ +struct iwl_mvm_roc_ops { +	int (*add_aux_sta_for_hs20)(struct iwl_mvm *mvm, u32 lmac_id); +	int (*switch_phy_ctxt)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			       struct iwl_mvm_phy_ctxt *new_phy_ctxt); +}; + +int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       struct ieee80211_channel *channel, int duration, +		       enum ieee80211_roc_type type, +		       struct iwl_mvm_roc_ops *ops); +int iwl_mvm_cancel_roc(struct ieee80211_hw *hw, +		       struct ieee80211_vif *vif); +/*Session Protection */ +void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			   u32 duration_override); +  /* Quota management */  static inline size_t iwl_mvm_quota_cmd_size(struct iwl_mvm *mvm)  { @@ -1858,10 +2093,17 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,  /* SMPS */  void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  				enum iwl_mvm_smps_type_request req_type, -				enum ieee80211_smps_mode smps_request); +				enum ieee80211_smps_mode smps_request, +				unsigned int link_id); +void +iwl_mvm_update_smps_on_active_links(struct iwl_mvm *mvm, +				    struct ieee80211_vif *vif, +				    enum iwl_mvm_smps_type_request req_type, +				    enum ieee80211_smps_mode smps_request);  bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm,  				  struct iwl_mvm_phy_ctxt *ctxt); -void iwl_mvm_apply_fw_smps_request(struct ieee80211_vif *vif); +void iwl_mvm_update_link_smps(struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf);  /* Low latency */  int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -2070,7 +2312,11 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,  					  struct ieee80211_vif *vif,  					  const struct ieee80211_sta *sta,  					  u16 tid); +void iwl_mvm_mei_scan_filter_init(struct iwl_mei_scan_filter *mei_scan_filter); +void iwl_mvm_ptp_init(struct iwl_mvm *mvm); +void iwl_mvm_ptp_remove(struct iwl_mvm *mvm); +u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time);  int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b);  int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm);  int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm); @@ -2092,8 +2338,16 @@ int iwl_mvm_sec_key_del(struct iwl_mvm *mvm,  			struct ieee80211_sta *sta,  			struct ieee80211_key_conf *keyconf);  void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, -			       struct ieee80211_vif *vif); +			       struct ieee80211_vif *vif, +			       struct iwl_mvm_vif_link_info *link, +			       unsigned int link_id); +int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				u32 old_sta_mask, +				u32 new_sta_mask); +bool iwl_rfi_supported(struct iwl_mvm *mvm);  int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,  			    struct iwl_rfi_lut_entry *rfi_table);  struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm); @@ -2115,6 +2369,45 @@ static inline u8 iwl_mvm_phy_band_from_nl80211(enum nl80211_band band)  	}  } +/* Channel Switch */ +void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk); +int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif); + +/* Channel Context */ +/** + * struct iwl_mvm_switch_vif_chanctx_ops - callbacks for switch_vif_chanctx() + * + * Since the only difference between both MLD and + * non-MLD versions of switch_vif_chanctx() is these function calls, + * each version will send its specific function calls to + * %iwl_mvm_switch_vif_chanctx_common(). + * + * @__assign_vif_chanctx: pointer to the function that assigns a chanctx to + *	a given vif + * @__unassign_vif_chanctx: pointer to the function that unassigns a chanctx to + *	a given vif + */ +struct iwl_mvm_switch_vif_chanctx_ops { +	int (*__assign_vif_chanctx)(struct iwl_mvm *mvm, +				    struct ieee80211_vif *vif, +				    struct ieee80211_bss_conf *link_conf, +				    struct ieee80211_chanctx_conf *ctx, +				    bool switching_chanctx); +	void (*__unassign_vif_chanctx)(struct iwl_mvm *mvm, +				       struct ieee80211_vif *vif, +				       struct ieee80211_bss_conf *link_conf, +				       struct ieee80211_chanctx_conf *ctx, +				       bool switching_chanctx); +}; + +int +iwl_mvm_switch_vif_chanctx_common(struct ieee80211_hw *hw, +				  struct ieee80211_vif_chanctx_switch *vifs, +				  int n_vifs, +				  enum ieee80211_chanctx_switch_mode mode, +				  struct iwl_mvm_switch_vif_chanctx_ops *ops); +  /* Channel info utils */  static inline bool iwl_mvm_has_ultra_hb_channel(struct iwl_mvm *mvm)  { @@ -2232,8 +2525,147 @@ static inline void iwl_mvm_mei_set_sw_rfkill_state(struct iwl_mvm *mvm)  					 sw_rfkill);  } +static inline bool iwl_mvm_mei_filter_scan(struct iwl_mvm *mvm, +					   struct sk_buff *skb) +{ +	struct ieee80211_mgmt *mgmt = (void *)skb->data; + +	if (mvm->mei_scan_filter.is_mei_limited_scan && +	    (ieee80211_is_probe_resp(mgmt->frame_control) || +	     ieee80211_is_beacon(mgmt->frame_control))) { +		skb_queue_tail(&mvm->mei_scan_filter.scan_res, skb); +		schedule_work(&mvm->mei_scan_filter.scan_work); +		return true; +	} + +	return false; +} +  void iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm *mvm,  					  struct ieee80211_vif *vif,  					  bool forbidden); +bool iwl_mvm_is_vendor_in_approved_list(void); + +/* Callbacks for ieee80211_ops */ +void iwl_mvm_mac_tx(struct ieee80211_hw *hw, +		    struct ieee80211_tx_control *control, struct sk_buff *skb); +void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw, +			       struct ieee80211_txq *txq); + +int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     struct ieee80211_ampdu_params *params); +int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); +int iwl_mvm_mac_start(struct ieee80211_hw *hw); +void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, +				   enum ieee80211_reconfig_type reconfig_type); +void iwl_mvm_mac_stop(struct ieee80211_hw *hw); +static inline int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) +{ +	return 0; +} + +u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, +			      struct netdev_hw_addr_list *mc_list); +void iwl_mvm_configure_filter(struct ieee80211_hw *hw, +			      unsigned int changed_flags, +			      unsigned int *total_flags, u64 multicast); +int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			struct ieee80211_scan_request *hw_req); +void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif); +void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta); +void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			    enum sta_notify_cmd cmd, +			    struct ieee80211_sta *sta); +void +iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, +				  struct ieee80211_sta *sta, u16 tids, +				  int num_frames, +				  enum ieee80211_frame_release_type reason, +				  bool more_data); +void +iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, +				    struct ieee80211_sta *sta, u16 tids, +				    int num_frames, +				    enum ieee80211_frame_release_type reason, +				    bool more_data); +int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			   struct ieee80211_sta *sta, u32 changed); +void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_prep_tx_info *info); +void iwl_mvm_mac_mgd_complete_tx(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_prep_tx_info *info); +void iwl_mvm_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       u32 queues, bool drop); +int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct cfg80211_sched_scan_request *req, +				 struct ieee80211_scan_ies *ies); +int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif); +int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, +			struct ieee80211_vif *vif, struct ieee80211_sta *sta, +			struct ieee80211_key_conf *key); +void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_key_conf *keyconf, +				 struct ieee80211_sta *sta, +				 u32 iv32, u16 *phase1key); +int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, +			struct ieee80211_chanctx_conf *ctx); +void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, +			    struct ieee80211_chanctx_conf *ctx); +void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, +			    struct ieee80211_chanctx_conf *ctx, u32 changed); +int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw); +int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, +		    bool set); +void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			    struct ieee80211_channel_switch *chsw); +int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, +			       struct ieee80211_vif *vif, +			       struct ieee80211_channel_switch *chsw); +void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif); +void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw, +				      struct ieee80211_vif *vif, +				      struct ieee80211_channel_switch *chsw); +void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				const struct ieee80211_event *event); +void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw); +int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     void *data, int len); +int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, +			   struct survey_info *survey); +void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				struct station_info *sinfo); +int +iwl_mvm_mac_get_ftm_responder_stats(struct ieee80211_hw *hw, +				    struct ieee80211_vif *vif, +				    struct cfg80211_ftm_responder_stats *stats); +int iwl_mvm_start_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +		       struct cfg80211_pmsr_request *request); +void iwl_mvm_abort_pmsr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +			struct cfg80211_pmsr_request *request); + +bool iwl_mvm_have_links_same_channel(struct iwl_mvm_vif *vif1, +				     struct iwl_mvm_vif *vif2); +bool iwl_mvm_vif_is_active(struct iwl_mvm_vif *mvmvif); +int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			 s16 tx_power); +int iwl_mvm_set_hw_timestamp(struct ieee80211_hw *hw, +			     struct ieee80211_vif *vif, +			     struct cfg80211_set_hw_timestamp *hwts); +int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 6d18a1fd649b..fdf60afb0f3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -445,6 +445,11 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,  		struct iwl_mcc_update_resp *mcc_resp = (void *)pkt->data;  		n_channels =  __le32_to_cpu(mcc_resp->n_channels); +		if (iwl_rx_packet_payload_len(pkt) != +		    struct_size(mcc_resp, channels, n_channels)) { +			resp_cp = ERR_PTR(-EINVAL); +			goto exit; +		}  		resp_len = sizeof(struct iwl_mcc_update_resp) +  			   n_channels * sizeof(__le32);  		resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); @@ -456,6 +461,11 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,  		struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data;  		n_channels =  __le32_to_cpu(mcc_resp_v3->n_channels); +		if (iwl_rx_packet_payload_len(pkt) != +		    struct_size(mcc_resp_v3, channels, n_channels)) { +			resp_cp = ERR_PTR(-EINVAL); +			goto exit; +		}  		resp_len = sizeof(struct iwl_mcc_update_resp) +  			   n_channels * sizeof(__le32);  		resp_cp = kzalloc(resp_len, GFP_KERNEL); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f4e9446d9dc2..32625bfacaae 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -29,6 +29,7 @@  #include "fw-api.h"  #include "fw/acpi.h"  #include "fw/uefi.h" +#include "time-sync.h"  #define DRV_DESCRIPTION	"The new Intel(R) wireless AGN driver for Linux"  MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -208,24 +209,37 @@ static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm,  	ieee80211_disconnect(vif, true);  } -void iwl_mvm_apply_fw_smps_request(struct ieee80211_vif *vif) +void iwl_mvm_update_link_smps(struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm *mvm = mvmvif->mvm;  	enum ieee80211_smps_mode mode = IEEE80211_SMPS_AUTOMATIC; +	if (!link_conf) +		return; +  	if (mvm->fw_static_smps_request && -	    vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_160 && -	    vif->bss_conf.he_support) +	    link_conf->chandef.width == NL80211_CHAN_WIDTH_160 && +	    link_conf->he_support)  		mode = IEEE80211_SMPS_STATIC; -	iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_FW, mode); +	iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_FW, mode, +			    link_conf->link_id);  }  static void iwl_mvm_intf_dual_chain_req(void *data, u8 *mac,  					struct ieee80211_vif *vif)  { -	iwl_mvm_apply_fw_smps_request(vif); +	struct ieee80211_bss_conf *link_conf; +	unsigned int link_id; + +	rcu_read_lock(); + +	for_each_vif_active_link(vif, link_conf, link_id) +		iwl_mvm_update_link_smps(vif, link_conf); + +	rcu_read_unlock();  }  static void iwl_mvm_rx_thermal_dual_chain_req(struct iwl_mvm *mvm, @@ -404,6 +418,15 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {  	RX_HANDLER_GRP(SYSTEM_GROUP, RFI_DEACTIVATE_NOTIF,  		       iwl_rfi_deactivate_notif_handler, RX_HANDLER_ASYNC_UNLOCKED,  		       struct iwl_rfi_deactivate_notif), + +	RX_HANDLER_GRP(LEGACY_GROUP, +		       WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION, +		       iwl_mvm_time_sync_msmt_event, RX_HANDLER_SYNC, +		       struct iwl_time_msmt_notify), +	RX_HANDLER_GRP(LEGACY_GROUP, +		       WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION, +		       iwl_mvm_time_sync_msmt_confirm_event, RX_HANDLER_SYNC, +		       struct iwl_time_msmt_cfm_notify),  };  #undef RX_HANDLER  #undef RX_HANDLER_GRP @@ -449,6 +472,8 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {  	HCMD_NAME(SCAN_OFFLOAD_PROFILES_QUERY_CMD),  	HCMD_NAME(BT_COEX_UPDATE_REDUCED_TXP),  	HCMD_NAME(BT_COEX_CI), +	HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION), +	HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION),  	HCMD_NAME(PHY_CONFIGURATION_CMD),  	HCMD_NAME(CALIB_RES_NOTIF_PHY_DB),  	HCMD_NAME(PHY_DB_CMD), @@ -521,6 +546,12 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = {  static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = {  	HCMD_NAME(CHANNEL_SWITCH_TIME_EVENT_CMD),  	HCMD_NAME(SESSION_PROTECTION_CMD), +	HCMD_NAME(MAC_CONFIG_CMD), +	HCMD_NAME(LINK_CONFIG_CMD), +	HCMD_NAME(STA_CONFIG_CMD), +	HCMD_NAME(AUX_STA_CMD), +	HCMD_NAME(STA_REMOVE_CMD), +	HCMD_NAME(STA_DISABLE_TX_CMD),  	HCMD_NAME(SESSION_PROTECTION_NOTIF),  	HCMD_NAME(CHANNEL_SWITCH_START_NOTIF),  }; @@ -989,10 +1020,14 @@ static void iwl_mvm_me_conn_status(void *priv, const struct iwl_mei_conn_info *c  		kfree_rcu(prev_conn_info, rcu_head);  } -static void iwl_mvm_mei_rfkill(void *priv, bool blocked) +static void iwl_mvm_mei_rfkill(void *priv, bool blocked, +			       bool csme_taking_ownership)  {  	struct iwl_mvm *mvm = priv; +	if (blocked && !csme_taking_ownership) +		return; +  	mvm->mei_rfkill_blocked = blocked;  	if (!mvm->hw_registered)  		return; @@ -1098,6 +1133,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,  	 ********************************/  	hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +  				sizeof(struct iwl_mvm), +				iwl_mvm_has_mld_api(fw) ? &iwl_mvm_mld_hw_ops :  				&iwl_mvm_hw_ops);  	if (!hw)  		return NULL; @@ -1195,6 +1231,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,  	INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk);  	INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk);  	INIT_LIST_HEAD(&mvm->add_stream_txqs); +	spin_lock_init(&mvm->add_stream_lock);  	init_waitqueue_head(&mvm->rx_sync_waitq); @@ -1277,6 +1314,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,  	mvm->sta_remove_requires_queue_remove =  		trans_cfg.queue_alloc_cmd_ver > 0; +	mvm->mld_api_is_used = iwl_mvm_has_mld_api(mvm->fw); +  	/* Configure transport layer */  	iwl_trans_configure(mvm->trans, &trans_cfg); @@ -1332,10 +1371,16 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,  	else  		memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); +	iwl_mvm_ftm_initiator_smooth_config(mvm); + +	iwl_mvm_init_time_sync(&mvm->time_sync); +  	mvm->debugfs_dir = dbgfs_dir;  	mvm->mei_registered = !iwl_mei_register(mvm, &mei_ops); +	iwl_mvm_mei_scan_filter_init(&mvm->mei_scan_filter); +  	if (iwl_mvm_start_get_nvm(mvm)) {  		/*  		 * Getting NVM failed while CSME is the owner, but we are @@ -1429,6 +1474,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)  	kfree(mvm->error_recovery_buf);  	mvm->error_recovery_buf = NULL; +	iwl_mvm_ptp_remove(mvm); +  	iwl_trans_op_mode_leave(mvm->trans);  	iwl_phy_db_free(mvm->phy_db); @@ -1691,7 +1738,10 @@ static void iwl_mvm_queue_state_change(struct iwl_op_mode *op_mode,  		txq = sta->txq[tid];  		mvmtxq = iwl_mvm_txq_from_mac80211(txq); -		mvmtxq->stopped = !start; +		if (start) +			clear_bit(IWL_MVM_TXQ_STATE_STOP_FULL, &mvmtxq->state); +		else +			set_bit(IWL_MVM_TXQ_STATE_STOP_FULL, &mvmtxq->state);  		if (start && mvmsta->sta_state != IEEE80211_STA_NOTEXIST)  			iwl_mvm_mac_itxq_xmit(mvm->hw, txq); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index 06f4203fb989..3ab6fb83a175 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -382,12 +382,12 @@ static void iwl_mvm_binding_iterator(void *_data, u8 *mac,  	unsigned long *data = _data;  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	if (!mvmvif->phy_ctxt) +	if (!mvmvif->deflink.phy_ctxt)  		return;  	if (vif->type == NL80211_IFTYPE_STATION ||  	    vif->type == NL80211_IFTYPE_AP) -		__set_bit(mvmvif->phy_ctxt->id, data); +		__set_bit(mvmvif->deflink.phy_ctxt->id, data);  }  int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index f5744162d0d8..ac1dae52556f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -150,7 +150,7 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,  #endif  	for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { -		if (!mvmvif->queue_params[ac].uapsd) +		if (!mvmvif->deflink.queue_params[ac].uapsd)  			continue;  		if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) @@ -160,7 +160,7 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,  		cmd->uapsd_ac_flags |= BIT(ac);  		/* QNDP TID - the highest TID with no admission control */ -		if (!tid_found && !mvmvif->queue_params[ac].acm) { +		if (!tid_found && !mvmvif->deflink.queue_params[ac].acm) {  			tid_found = true;  			switch (ac) {  			case IEEE80211_AC_VO: @@ -279,18 +279,25 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,  static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)  {  	struct ieee80211_chanctx_conf *chanctx_conf; -	struct ieee80211_channel *chan; +	struct ieee80211_bss_conf *link_conf;  	bool radar_detect = false; +	unsigned int link_id;  	rcu_read_lock(); -	chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); -	WARN_ON(!chanctx_conf); -	if (chanctx_conf) { -		chan = chanctx_conf->def.chan; -		radar_detect = chan->flags & IEEE80211_CHAN_RADAR; +	for_each_vif_active_link(vif, link_conf, link_id) { +		chanctx_conf = rcu_dereference(link_conf->chanctx_conf); +		/* this happens on link switching, just ignore inactive ones */ +		if (!chanctx_conf) +			continue; + +		radar_detect = !!(chanctx_conf->def.chan->flags & +				  IEEE80211_CHAN_RADAR); +		if (radar_detect) +			goto out;  	} -	rcu_read_unlock(); +out: +	rcu_read_unlock();  	return radar_detect;  } @@ -509,8 +516,9 @@ static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,  	/* The ap_sta_id is not expected to change during current association  	 * so no explicit protection is needed  	 */ -	if (mvmvif->ap_sta_id == *ap_sta_id) -		memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, +	if (mvmvif->deflink.ap_sta_id == *ap_sta_id) +		memcpy(mvmvif->uapsd_misbehaving_bssid, +		       vif->bss_conf.bssid,  		       ETH_ALEN);  } @@ -552,7 +560,7 @@ static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	bool *disable_ps = _data; -	if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX) +	if (iwl_mvm_vif_is_active(mvmvif))  		*disable_ps |= mvmvif->ps_disabled;  } @@ -561,11 +569,13 @@ static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_power_vifs *power_iterator = _data; -	bool active = mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX; +	bool active;  	if (!mvmvif->uploaded)  		return; +	active = iwl_mvm_vif_is_active(mvmvif); +  	switch (ieee80211_vif_type_p2p(vif)) {  	case NL80211_IFTYPE_P2P_DEVICE:  		break; @@ -649,11 +659,12 @@ static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm,  	}  	if (vifs->bss_active && vifs->p2p_active) -		client_same_channel = (bss_mvmvif->phy_ctxt->id == -				       p2p_mvmvif->phy_ctxt->id); +		client_same_channel = +			iwl_mvm_have_links_same_channel(bss_mvmvif, p2p_mvmvif); +  	if (vifs->bss_active && vifs->ap_active) -		ap_same_channel = (bss_mvmvif->phy_ctxt->id == -				   ap_mvmvif->phy_ctxt->id); +		ap_same_channel = +			iwl_mvm_have_links_same_channel(bss_mvmvif, ap_mvmvif);  	/* clients are not stand alone: enable PM if DCM */  	if (!(client_same_channel || ap_same_channel)) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c new file mode 100644 index 000000000000..e89259de6f4c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2021 - 2023 Intel Corporation + */ + +#include "mvm.h" +#include "iwl-debug.h" +#include <linux/timekeeping.h> +#include <linux/math64.h> + +#define IWL_PTP_GP2_WRAP	0x100000000ULL +#define IWL_PTP_WRAP_TIME	(3600 * HZ) + +/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional + * part, which means that a value of 1 in one of those fields actually means + * 2^-16 ppm, and 2^16=65536 is 1 ppm. + */ +#define SCALE_FACTOR	65536000000ULL +#define IWL_PTP_WRAP_THRESHOLD_USEC	(5000) + +#define IWL_PTP_GET_CROSS_TS_NUM	5 + +static void iwl_mvm_ptp_update_new_read(struct iwl_mvm *mvm, u32 gp2) +{ +	/* If the difference is above the threshold, assume it's a wraparound. +	 * Otherwise assume it's an old read and ignore it. +	 */ +	if (gp2 < mvm->ptp_data.last_gp2 && +	    mvm->ptp_data.last_gp2 - gp2 < IWL_PTP_WRAP_THRESHOLD_USEC) { +		IWL_DEBUG_INFO(mvm, +			       "PTP: ignore old read (gp2=%u, last_gp2=%u)\n", +			       gp2, mvm->ptp_data.last_gp2); +		return; +	} + +	if (gp2 < mvm->ptp_data.last_gp2) { +		mvm->ptp_data.wrap_counter++; +		IWL_DEBUG_INFO(mvm, +			       "PTP: wraparound detected (new counter=%u)\n", +			       mvm->ptp_data.wrap_counter); +	} + +	mvm->ptp_data.last_gp2 = gp2; +	schedule_delayed_work(&mvm->ptp_data.dwork, IWL_PTP_WRAP_TIME); +} + +u64 iwl_mvm_ptp_get_adj_time(struct iwl_mvm *mvm, u64 base_time_ns) +{ +	struct ptp_data *data = &mvm->ptp_data; +	u64 last_gp2_ns = mvm->ptp_data.scale_update_gp2 * NSEC_PER_USEC; +	u64 res; +	u64 diff; + +	iwl_mvm_ptp_update_new_read(mvm, +				    div64_u64(base_time_ns, NSEC_PER_USEC)); + +	IWL_DEBUG_INFO(mvm, "base_time_ns=%llu, wrap_counter=%u\n", +		       (unsigned long long)base_time_ns, data->wrap_counter); + +	base_time_ns = base_time_ns + +		(data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC); + +	/* It is possible that a GP2 timestamp was received from fw before the +	 * last scale update. Since we don't know how to scale - ignore it. +	 */ +	if (base_time_ns < last_gp2_ns) { +		IWL_DEBUG_INFO(mvm, "Time before scale update - ignore\n"); +		return 0; +	} + +	diff = base_time_ns - last_gp2_ns; +	IWL_DEBUG_INFO(mvm, "diff ns=%llu\n", (unsigned long long)diff); + +	diff = mul_u64_u64_div_u64(diff, data->scaled_freq, +				   SCALE_FACTOR); +	IWL_DEBUG_INFO(mvm, "scaled diff ns=%llu\n", (unsigned long long)diff); + +	res = data->scale_update_adj_time_ns + data->delta + diff; + +	IWL_DEBUG_INFO(mvm, "base=%llu delta=%lld adj=%llu\n", +		       (unsigned long long)base_time_ns, (long long)data->delta, +		       (unsigned long long)res); +	return res; +} + +static int +iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time) +{ +	struct iwl_synced_time_cmd synced_time_cmd = { +		.operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH) +	}; +	struct iwl_host_cmd cmd = { +		.id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD), +		.flags = CMD_WANT_SKB, +		.data[0] = &synced_time_cmd, +		.len[0] = sizeof(synced_time_cmd), +	}; +	struct iwl_synced_time_rsp *resp; +	struct iwl_rx_packet *pkt; +	int ret; +	u64 gp2_10ns; + +	ret = iwl_mvm_send_cmd(mvm, &cmd); +	if (ret) +		return ret; + +	pkt = cmd.resp_pkt; + +	if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) { +		IWL_ERR(mvm, "PTP: Invalid command response\n"); +		iwl_free_resp(&cmd); +		return -EIO; +	} + +	resp = (void *)pkt->data; + +	gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 | +		le32_to_cpu(resp->gp2_timestamp_lo); +	*gp2 = div_u64(gp2_10ns, 100); + +	*sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | +		le32_to_cpu(resp->platform_timestamp_lo); + +	return ret; +} + +static void iwl_mvm_phc_get_crosstimestamp_loop(struct iwl_mvm *mvm, +						ktime_t *sys_time, u32 *gp2) +{ +	u64 diff = 0, new_diff; +	u64 tmp_sys_time; +	u32 tmp_gp2; +	int i; + +	for (i = 0; i < IWL_PTP_GET_CROSS_TS_NUM; i++) { +		iwl_mvm_get_sync_time(mvm, CLOCK_REALTIME, &tmp_gp2, NULL, +				      &tmp_sys_time); +		new_diff = tmp_sys_time - ((u64)tmp_gp2 * NSEC_PER_USEC); +		if (!diff || new_diff < diff) { +			*sys_time = tmp_sys_time; +			*gp2 = tmp_gp2; +			diff = new_diff; +			IWL_DEBUG_INFO(mvm, "PTP: new times: gp2=%u sys=%lld\n", +				       *gp2, *sys_time); +		} +	} +} + +static int +iwl_mvm_phc_get_crosstimestamp(struct ptp_clock_info *ptp, +			       struct system_device_crosststamp *xtstamp) +{ +	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, +					   ptp_data.ptp_clock_info); +	int ret = 0; +	/* Raw value read from GP2 register in usec */ +	u32 gp2; +	/* GP2 value in ns*/ +	s64 gp2_ns; +	/* System (wall) time */ +	ktime_t sys_time; + +	memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); + +	if (!mvm->ptp_data.ptp_clock) { +		IWL_ERR(mvm, "No PHC clock registered\n"); +		return -ENODEV; +	} + +	mutex_lock(&mvm->mutex); +	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SYNCED_TIME)) { +		ret = iwl_mvm_get_crosstimestamp_fw(mvm, &gp2, &sys_time); + +		if (ret) +			goto out; +	} else { +		iwl_mvm_phc_get_crosstimestamp_loop(mvm, &sys_time, &gp2); +	} + +	gp2_ns = iwl_mvm_ptp_get_adj_time(mvm, (u64)gp2 * NSEC_PER_USEC); + +	IWL_INFO(mvm, "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n", +		 gp2, mvm->ptp_data.last_gp2, gp2_ns, (s64)sys_time); + +	/* System monotonic raw time is not used */ +	xtstamp->device = (ktime_t)gp2_ns; +	xtstamp->sys_realtime = sys_time; + +out: +	mutex_unlock(&mvm->mutex); +	return ret; +} + +static void iwl_mvm_ptp_work(struct work_struct *wk) +{ +	struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, +					   ptp_data.dwork.work); +	u32 gp2; + +	mutex_lock(&mvm->mutex); +	gp2 = iwl_mvm_get_systime(mvm); +	iwl_mvm_ptp_update_new_read(mvm, gp2); +	mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_ptp_gettime(struct ptp_clock_info *ptp, +			       struct timespec64 *ts) +{ +	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, +					   ptp_data.ptp_clock_info); +	u64 gp2; +	u64 ns; + +	mutex_lock(&mvm->mutex); +	gp2 = iwl_mvm_get_systime(mvm); +	ns = iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC); +	mutex_unlock(&mvm->mutex); + +	*ts = ns_to_timespec64(ns); +	return 0; +} + +static int iwl_mvm_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ +	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, +					   ptp_data.ptp_clock_info); +	struct ptp_data *data = container_of(ptp, struct ptp_data, +					     ptp_clock_info); + +	mutex_lock(&mvm->mutex); +	data->delta += delta; +	IWL_DEBUG_INFO(mvm, "delta=%lld, new delta=%lld\n", (long long)delta, +		       (long long)data->delta); +	mutex_unlock(&mvm->mutex); +	return 0; +} + +static int iwl_mvm_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ +	struct iwl_mvm *mvm = container_of(ptp, struct iwl_mvm, +					   ptp_data.ptp_clock_info); +	struct ptp_data *data = &mvm->ptp_data; +	u32 gp2; + +	mutex_lock(&mvm->mutex); + +	/* Must call _iwl_mvm_ptp_get_adj_time() before updating +	 * data->scale_update_gp2 or data->scaled_freq since +	 * scale_update_adj_time_ns should reflect the previous scaled_freq. +	 */ +	gp2 = iwl_mvm_get_systime(mvm); +	data->scale_update_adj_time_ns = +		iwl_mvm_ptp_get_adj_time(mvm, gp2 * NSEC_PER_USEC); +	data->scale_update_gp2 = gp2; +	data->wrap_counter = 0; +	data->delta = 0; + +	data->scaled_freq = SCALE_FACTOR + scaled_ppm; +	IWL_DEBUG_INFO(mvm, "adjfine: scaled_ppm=%ld new=%llu\n", +		       scaled_ppm, (unsigned long long)data->scaled_freq); + +	mutex_unlock(&mvm->mutex); +	return 0; +} + +/* iwl_mvm_ptp_init - initialize PTP for devices which support it. + * @mvm: internal mvm structure, see &struct iwl_mvm. + * + * Performs the required steps for enabling PTP support. + */ +void iwl_mvm_ptp_init(struct iwl_mvm *mvm) +{ +	/* Warn if the interface already has a ptp_clock defined */ +	if (WARN_ON(mvm->ptp_data.ptp_clock)) +		return; + +	mvm->ptp_data.ptp_clock_info.owner = THIS_MODULE; +	mvm->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; +	mvm->ptp_data.ptp_clock_info.getcrosststamp = +					iwl_mvm_phc_get_crosstimestamp; +	mvm->ptp_data.ptp_clock_info.adjfine = iwl_mvm_ptp_adjfine; +	mvm->ptp_data.ptp_clock_info.adjtime = iwl_mvm_ptp_adjtime; +	mvm->ptp_data.ptp_clock_info.gettime64 = iwl_mvm_ptp_gettime; +	mvm->ptp_data.scaled_freq = SCALE_FACTOR; + +	/* Give a short 'friendly name' to identify the PHC clock */ +	snprintf(mvm->ptp_data.ptp_clock_info.name, +		 sizeof(mvm->ptp_data.ptp_clock_info.name), +		 "%s", "iwlwifi-PTP"); + +	INIT_DELAYED_WORK(&mvm->ptp_data.dwork, iwl_mvm_ptp_work); + +	mvm->ptp_data.ptp_clock = +		ptp_clock_register(&mvm->ptp_data.ptp_clock_info, mvm->dev); + +	if (IS_ERR(mvm->ptp_data.ptp_clock)) { +		IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n", +			PTR_ERR(mvm->ptp_data.ptp_clock)); +		mvm->ptp_data.ptp_clock = NULL; +	} else if (mvm->ptp_data.ptp_clock) { +		IWL_INFO(mvm, "Registered PHC clock: %s, with index: %d\n", +			 mvm->ptp_data.ptp_clock_info.name, +			 ptp_clock_index(mvm->ptp_data.ptp_clock)); +	} +} + +/* iwl_mvm_ptp_remove - disable PTP device. + * @mvm: internal mvm structure, see &struct iwl_mvm. + * + * Disable PTP support. + */ +void iwl_mvm_ptp_remove(struct iwl_mvm *mvm) +{ +	if (mvm->ptp_data.ptp_clock) { +		IWL_INFO(mvm, "Unregistering PHC clock: %s, with index: %d\n", +			 mvm->ptp_data.ptp_clock_info.name, +			 ptp_clock_index(mvm->ptp_data.ptp_clock)); + +		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); +	} +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index cea1a34f9130..aad2614af9ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -33,11 +33,11 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,  	if (vif == data->disabled_vif)  		return; -	if (!mvmvif->phy_ctxt) +	if (!mvmvif->deflink.phy_ctxt)  		return;  	/* currently, PHY ID == binding ID */ -	id = mvmvif->phy_ctxt->id; +	id = mvmvif->deflink.phy_ctxt->id;  	/* need at least one binding per PHY */  	BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); @@ -67,9 +67,10 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,  	}  	if (data->colors[id] < 0) -		data->colors[id] = mvmvif->phy_ctxt->color; +		data->colors[id] = mvmvif->deflink.phy_ctxt->color;  	else -		WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); +		WARN_ON_ONCE(data->colors[id] != +			     mvmvif->deflink.phy_ctxt->color);  	data->n_interfaces[id]++; @@ -99,7 +100,7 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,  	if (!mvmvif->ap_ibss_active)  		return; -	phy_id = mvmvif->phy_ctxt->id; +	phy_id = mvmvif->deflink.phy_ctxt->id;  	beacon_int = mvm->noa_vif->bss_conf.beacon_int;  	for (i = 0; i < MAX_BINDINGS; i++) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c b/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c index bb77bc9aa821..2ecd32bed752 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rfi.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2020 - 2021 Intel Corporation + * Copyright (C) 2020 - 2022 Intel Corporation   */  #include "mvm.h" @@ -70,6 +70,16 @@ static const struct iwl_rfi_lut_entry iwl_rfi_table[IWL_RFI_LUT_SIZE] = {  		PHY_BAND_6, PHY_BAND_6,}},  }; +bool iwl_rfi_supported(struct iwl_mvm *mvm) +{ +	/* The feature depends on a platform bugfix, so for now +	 * it's always disabled. +	 * When the platform support detection is implemented we should +	 * check FW TLV and platform support instead. +	 */ +	return false; +} +  int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, struct iwl_rfi_lut_entry *rfi_table)  {  	int ret; @@ -81,7 +91,7 @@ int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, struct iwl_rfi_lut_entry *rfi_t  		.len[0] = sizeof(cmd),  	}; -	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RFIM_SUPPORT)) +	if (!iwl_rfi_supported(mvm))  		return -EOPNOTSUPP;  	lockdep_assert_held(&mvm->mutex); @@ -113,7 +123,7 @@ struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm)  		.flags = CMD_WANT_SKB,  	}; -	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RFIM_SUPPORT)) +	if (!iwl_rfi_supported(mvm))  		return ERR_PTR(-EOPNOTSUPP);  	mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index f30eeab5505b..c3a00bfbeef2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -9,9 +9,9 @@  #include "iwl-op-mode.h"  #include "mvm.h" -static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_sta *sta) +static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)  { -	switch (sta->deflink.bandwidth) { +	switch (link_sta->bandwidth) {  	case IEEE80211_STA_RX_BW_320:  		return IWL_TLC_MNG_CH_WIDTH_320MHZ;  	case IEEE80211_STA_RX_BW_160: @@ -38,11 +38,11 @@ static u8 rs_fw_set_active_chains(u8 chains)  	return fw_chains;  } -static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) +static u8 rs_fw_sgi_cw_support(struct ieee80211_link_sta *link_sta)  { -	struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; -	struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -	struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; +	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;  	u8 supp = 0;  	if (he_cap->has_he) @@ -61,12 +61,14 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)  }  static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, -				  struct ieee80211_sta *sta, +				  struct ieee80211_vif *vif, +				  struct ieee80211_link_sta *link_sta,  				  struct ieee80211_supported_band *sband)  { -	struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; -	struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -	struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; +	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_he_cap *sband_he_cap;  	bool vht_ena = vht_cap->vht_supported;  	u16 flags = 0; @@ -92,17 +94,19 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm,  	    IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))  		flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; -	if (sband->iftype_data && sband->iftype_data->he_cap.has_he && -	    !(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[1] & -	     IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) +	sband_he_cap = ieee80211_get_he_iftype_cap(sband, +						   ieee80211_vif_type_p2p(vif)); +	if (sband_he_cap && +	    !(sband_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 (he_cap->has_he &&  	    (he_cap->he_cap_elem.phy_cap_info[3] &  	     IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && -	     sband->iftype_data && -	     sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[3] & -	     IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) +	     sband_he_cap && +	     sband_he_cap->he_cap_elem.phy_cap_info[3] & +			IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK))  		flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;  	return flags; @@ -132,20 +136,20 @@ int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap,  }  static void -rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta, +rs_fw_vht_set_enabled_rates(const struct ieee80211_link_sta *link_sta,  			    const struct ieee80211_sta_vht_cap *vht_cap,  			    struct iwl_tlc_config_cmd_v4 *cmd)  {  	u16 supp;  	int i, highest_mcs; -	u8 max_nss = sta->deflink.rx_nss; +	u8 max_nss = link_sta->rx_nss;  	struct ieee80211_vht_cap ieee_vht_cap = {  		.vht_cap_info = cpu_to_le32(vht_cap->cap),  		.supp_mcs = vht_cap->vht_mcs,  	};  	/* the station support only a single receive chain */ -	if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) +	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)  		max_nss = 1;  	for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { @@ -156,7 +160,7 @@ rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta,  			continue;  		supp = BIT(highest_mcs + 1) - 1; -		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) +		if (link_sta->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_le16(supp); @@ -165,7 +169,7 @@ rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta,  		 * configuration is supported - only for MCS 0 since we already  		 * decoded the MCS bits anyway ourselves.  		 */ -		if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160 && +		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 &&  		    ieee80211_get_vht_max_nss(&ieee_vht_cap,  					      IEEE80211_VHT_CHANWIDTH_160MHZ,  					      0, true, nss) >= nss) @@ -192,11 +196,11 @@ static u16 rs_fw_he_ieee80211_mcs_to_rs_mcs(u16 mcs)  }  static void -rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta, +rs_fw_he_set_enabled_rates(const struct ieee80211_link_sta *link_sta,  			   struct ieee80211_supported_band *sband,  			   struct iwl_tlc_config_cmd_v4 *cmd)  { -	const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; +	const struct ieee80211_sta_he_cap *he_cap = &link_sta->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 = @@ -204,10 +208,10 @@ rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta,  	u16 tx_mcs_160 =  		le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160);  	int i; -	u8 nss = sta->deflink.rx_nss; +	u8 nss = link_sta->rx_nss;  	/* the station support only a single receive chain */ -	if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) +	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)  		nss = 1;  	for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { @@ -282,13 +286,15 @@ rs_fw_rs_mcs2eht_mcs(enum IWL_TLC_MCS_PER_BW bw,  	}  } -static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta, -					struct ieee80211_supported_band *sband, -					struct iwl_tlc_config_cmd_v4 *cmd) +static void +rs_fw_eht_set_enabled_rates(struct ieee80211_vif *vif, +			    const struct ieee80211_link_sta *link_sta, +			    struct ieee80211_supported_band *sband, +			    struct iwl_tlc_config_cmd_v4 *cmd)  {  	/* peer RX mcs capa */  	const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = -		&sta->deflink.eht_cap.eht_mcs_nss_supp; +		&link_sta->eht_cap.eht_mcs_nss_supp;  	/* our TX mcs capa */  	const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =  		&sband->iftype_data->eht_cap.eht_mcs_nss_supp; @@ -298,7 +304,8 @@ static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta,  	struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20;  	/* peer is 20Mhz only */ -	if (!(sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & +	if (vif->type == NL80211_IFTYPE_AP && +	    !(link_sta->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 { @@ -337,10 +344,14 @@ static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta,  		const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx =  			rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs); -		/* got unsuppored index for bw */ +		/* got unsupported index for bw */  		if (!mcs_rx || !mcs_tx)  			continue; +		/* break out if we don't support the bandwidth */ +		if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ)) +			break; +  		rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,  				      MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0));  		rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, @@ -350,25 +361,26 @@ static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta,  	}  	/* the station support only a single receive chain */ -	if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC || -	    sta->deflink.rx_nss < 2) +	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC || +	    link_sta->rx_nss < 2)  		memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,  		       sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));  } -static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, +static void rs_fw_set_supp_rates(struct ieee80211_vif *vif, +				 struct ieee80211_link_sta *link_sta,  				 struct ieee80211_supported_band *sband,  				 struct iwl_tlc_config_cmd_v4 *cmd)  {  	int i;  	u16 supp = 0;  	unsigned long tmp; /* must be unsigned long for for_each_set_bit */ -	const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; -	const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -	const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; +	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 */ -	tmp = sta->deflink.supp_rates[sband->band]; +	tmp = link_sta->supp_rates[sband->band];  	for_each_set_bit(i, &tmp, BITS_PER_LONG)  		supp |= BIT(sband->bitrates[i].hw_value); @@ -376,22 +388,22 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,  	cmd->mode = IWL_TLC_MNG_MODE_NON_HT;  	/* HT/VHT rates */ -	if (sta->deflink.eht_cap.has_eht) { +	if (link_sta->eht_cap.has_eht) {  		cmd->mode = IWL_TLC_MNG_MODE_EHT; -		rs_fw_eht_set_enabled_rates(sta, sband, cmd); +		rs_fw_eht_set_enabled_rates(vif, link_sta, sband, cmd);  	} else if (he_cap->has_he) {  		cmd->mode = IWL_TLC_MNG_MODE_HE; -		rs_fw_he_set_enabled_rates(sta, sband, cmd); +		rs_fw_he_set_enabled_rates(link_sta, sband, cmd);  	} else if (vht_cap->vht_supported) {  		cmd->mode = IWL_TLC_MNG_MODE_VHT; -		rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); +		rs_fw_vht_set_enabled_rates(link_sta, vht_cap, cmd);  	} else if (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_le16(ht_cap->mcs.rx_mask[0]);  		/* the station support only a single receive chain */ -		if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC) +		if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)  			cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =  				0;  		else @@ -406,15 +418,18 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,  	struct iwl_rx_packet *pkt = rxb_addr(rxb);  	struct iwl_tlc_update_notif *notif;  	struct ieee80211_sta *sta; +	struct ieee80211_link_sta *link_sta;  	struct iwl_mvm_sta *mvmsta; +	struct iwl_mvm_link_sta *mvm_link_sta;  	struct iwl_lq_sta_rs_fw *lq_sta;  	u32 flags;  	rcu_read_lock();  	notif = (void *)pkt->data; +	link_sta = rcu_dereference(mvm->fw_id_to_link_sta[notif->sta_id]);  	sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); -	if (IS_ERR_OR_NULL(sta)) { +	if (IS_ERR_OR_NULL(sta) || !link_sta) {  		/* can happen in remove station flow where mvm removed internally  		 * the station before removing from FW  		 */ @@ -434,7 +449,14 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,  	flags = le32_to_cpu(notif->flags); -	lq_sta = &mvmsta->lq_sta.rs_fw; +	mvm_link_sta = rcu_dereference(mvmsta->link[link_sta->link_id]); +	if (!mvm_link_sta) { +		IWL_DEBUG_RATE(mvm, +			       "Invalid mvmsta RCU pointer for link (%d) of  sta id (%d) in TLC notification\n", +			       link_sta->link_id, notif->sta_id); +		goto out; +	} +	lq_sta = &mvm_link_sta->lq_sta.rs_fw;  	if (flags & IWL_TLC_NOTIF_FLAG_RATE) {  		char pretty_rate[100]; @@ -461,9 +483,9 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,  		u16 size = le32_to_cpu(notif->amsdu_size);  		int i; -		if (sta->deflink.agg.max_amsdu_len < size) { +		if (link_sta->agg.max_amsdu_len < size) {  			/* -			 * In debug sta->deflink.agg.max_amsdu_len < size +			 * In debug link_sta->agg.max_amsdu_len < size  			 * so also check with orig_amsdu_len which holds the  			 * original data before debugfs changed the value  			 */ @@ -473,18 +495,18 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,  		mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled);  		mvmsta->max_amsdu_len = size; -		sta->deflink.agg.max_rc_amsdu_len = mvmsta->max_amsdu_len; +		link_sta->agg.max_rc_amsdu_len = mvmsta->max_amsdu_len;  		for (i = 0; i < IWL_MAX_TID_COUNT; i++) {  			if (mvmsta->amsdu_enabled & BIT(i)) -				sta->deflink.agg.max_tid_amsdu_len[i] = +				link_sta->agg.max_tid_amsdu_len[i] =  					iwl_mvm_max_amsdu_size(mvm, sta, i);  			else  				/*  				 * Not so elegant, but this will effectively  				 * prevent AMSDU on this TID  				 */ -				sta->deflink.agg.max_tid_amsdu_len[i] = 1; +				link_sta->agg.max_tid_amsdu_len[i] = 1;  		}  		IWL_DEBUG_RATE(mvm, @@ -496,14 +518,18 @@ out:  	rcu_read_unlock();  } -u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta) +u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta, +			    struct ieee80211_bss_conf *link_conf, +			    struct ieee80211_link_sta *link_sta)  { -	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; -	const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; +	const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; +	const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + +	if (WARN_ON_ONCE(!link_conf->chandef.chan)) +		return IEEE80211_MAX_MPDU_LEN_VHT_3895; -	if (mvmsta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) { -		switch (le16_get_bits(sta->deflink.he_6ghz_capa.capa, +	if (link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { +		switch (le16_get_bits(link_sta->he_6ghz_capa.capa,  				      IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {  		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:  			return IEEE80211_MAX_MPDU_LEN_VHT_11454; @@ -538,34 +564,52 @@ u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta)  	return 0;  } -void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, -		     enum nl80211_band band, bool update) +void iwl_mvm_rs_fw_rate_init(struct iwl_mvm *mvm, +			     struct ieee80211_vif *vif, +			     struct ieee80211_sta *sta, +			     struct ieee80211_bss_conf *link_conf, +			     struct ieee80211_link_sta *link_sta, +			     enum nl80211_band band)  {  	struct ieee80211_hw *hw = mvm->hw;  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;  	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD);  	struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; -	u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta); +	u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta, link_conf, link_sta); +	struct iwl_mvm_link_sta *mvm_link_sta; +	struct iwl_lq_sta_rs_fw *lq_sta;  	struct iwl_tlc_config_cmd_v4 cfg_cmd = { -		.sta_id = mvmsta->sta_id, -		.max_ch_width = update ? -			rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20, -		.flags = cpu_to_le16(rs_fw_get_config_flags(mvm, sta, sband)), +		.max_ch_width = mvmsta->authorized ? +			rs_fw_bw_from_sta_bw(link_sta) : IWL_TLC_MNG_CH_WIDTH_20MHZ, +		.flags = cpu_to_le16(rs_fw_get_config_flags(mvm, vif, link_sta, +							    sband)),  		.chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), -		.sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), +		.sgi_ch_width_supp = rs_fw_sgi_cw_support(link_sta),  		.max_mpdu_len = iwl_mvm_is_csum_supported(mvm) ?  				cpu_to_le16(max_amsdu_len) : 0,  	}; -	int ret; +	unsigned int link_id = link_conf->link_id;  	int cmd_ver; +	int ret; + +	rcu_read_lock(); +	mvm_link_sta = rcu_dereference(mvmsta->link[link_id]); +	if (WARN_ON_ONCE(!mvm_link_sta)) { +		rcu_read_unlock(); +		return; +	} + +	cfg_cmd.sta_id = mvm_link_sta->sta_id; +	lq_sta = &mvm_link_sta->lq_sta.rs_fw;  	memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); +	rcu_read_unlock(); +  #ifdef CONFIG_IWLWIFI_DEBUGFS  	iwl_mvm_reset_frame_stats(mvm);  #endif -	rs_fw_set_supp_rates(sta, sband, &cfg_cmd); +	rs_fw_set_supp_rates(vif, link_sta, sband, &cfg_cmd);  	/*  	 * since TLC offload works with one mode we can assume @@ -635,16 +679,18 @@ int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  	return 0;  } -void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta) +void iwl_mvm_rs_add_sta_link(struct iwl_mvm *mvm, +			     struct iwl_mvm_link_sta *link_sta)  { -	struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; +	struct iwl_lq_sta_rs_fw *lq_sta; -	IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); +	lq_sta = &link_sta->lq_sta.rs_fw;  	lq_sta->pers.drv = mvm; -	lq_sta->pers.sta_id = mvmsta->sta_id; +	lq_sta->pers.sta_id = link_sta->sta_id;  	lq_sta->pers.chains = 0; -	memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); +	memset(lq_sta->pers.chain_signal, 0, +	       sizeof(lq_sta->pers.chain_signal));  	lq_sta->pers.last_rssi = S8_MIN;  	lq_sta->last_rate_n_flags = 0; @@ -652,3 +698,20 @@ void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta)  	lq_sta->pers.dbg_fixed_rate = 0;  #endif  } + +void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta) +{ +	unsigned int link_id; + +	IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); + +	for (link_id = 0; link_id < ARRAY_SIZE(mvmsta->link); link_id++) { +		struct iwl_mvm_link_sta *link = +			rcu_dereference_protected(mvmsta->link[link_id], +						  lockdep_is_held(&mvm->mutex)); +		if (!link) +			continue; + +		iwl_mvm_rs_add_sta_link(mvm, link); +	} +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 0b50b816684a..9a20468345e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1,7 +1,7 @@  // SPDX-License-Identifier: GPL-2.0-only  /******************************************************************************   * - * Copyright(c) 2005 - 2014, 2018 - 2021 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014, 2018 - 2022 Intel Corporation. All rights reserved.   * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH   * Copyright(c) 2016 - 2017 Intel Deutschland GmbH   *****************************************************************************/ @@ -512,10 +512,10 @@ static char *rs_pretty_rate(const struct rs_rate *rate)  		 (rate->index <= IWL_RATE_MCS_9_INDEX))  		rate_str = ht_vht_rates[rate->index];  	else -		rate_str = "BAD_RATE"; +		rate_str = NULL;  	sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), -		iwl_rs_pretty_ant(rate->ant), rate_str); +		iwl_rs_pretty_ant(rate->ant), rate_str ?: "BAD_RATE");  	return buf;  } @@ -754,7 +754,7 @@ static int rs_collect_tlc_data(struct iwl_mvm *mvm,  		return -EINVAL;  	if (tbl->column != RS_COLUMN_INVALID) { -		struct lq_sta_pers *pers = &mvmsta->lq_sta.rs_drv.pers; +		struct lq_sta_pers *pers = &mvmsta->deflink.lq_sta.rs_drv.pers;  		pers->tx_stats[tbl->column][scale_index].total += attempts;  		pers->tx_stats[tbl->column][scale_index].success += successes; @@ -895,8 +895,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,  			WARN_ON_ONCE(1);  		}  	} else if (ucode_rate & RATE_MCS_VHT_MSK_V1) { -		nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> -		       RATE_VHT_MCS_NSS_POS) + 1; +		nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1;  		if (nss == 1) {  			rate->type = LQ_VHT_SISO; @@ -910,8 +909,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,  			WARN_ON_ONCE(1);  		}  	} else if (ucode_rate & RATE_MCS_HE_MSK_V1) { -		nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> -		      RATE_VHT_MCS_NSS_POS) + 1; +		nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1;  		if (nss == 1) {  			rate->type = LQ_HE_SISO; @@ -1489,9 +1487,11 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			     enum rs_action scale_action)  {  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); +	struct ieee80211_bss_conf *bss_conf = &mvmsta->vif->bss_conf;  	int i; -	sta->deflink.agg.max_amsdu_len = rs_fw_get_max_amsdu_len(sta); +	sta->deflink.agg.max_amsdu_len = +		rs_fw_get_max_amsdu_len(sta, bss_conf, &sta->deflink);  	/*  	 * In case TLC offload is not active amsdu_enabled is either 0xFFFF @@ -1504,7 +1504,7 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	else  		mvmsta->amsdu_enabled = 0xFFFF; -	if (mvmsta->vif->bss_conf.he_support && +	if (bss_conf->he_support &&  	    !iwlwifi_mod_params.disable_11ax)  		mvmsta->max_amsdu_len = sta->deflink.agg.max_amsdu_len;  	else @@ -2601,7 +2601,7 @@ void rs_update_last_rssi(struct iwl_mvm *mvm,  			 struct iwl_mvm_sta *mvmsta,  			 struct ieee80211_rx_status *rx_status)  { -	struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; +	struct iwl_lq_sta *lq_sta = &mvmsta->deflink.lq_sta.rs_drv;  	int i;  	lq_sta->pers.chains = rx_status->chains; @@ -2684,7 +2684,6 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,  		/* if vif isn't initialized mvm doesn't know about  		 * this station, so don't do anything with the it  		 */ -		sta = NULL;  		mvm_sta = NULL;  	} @@ -2692,6 +2691,8 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,  		return;  	lq_sta = mvm_sta; + +	spin_lock_bh(&lq_sta->pers.lock);  	iwl_mvm_hwrate_to_tx_rate_v1(lq_sta->last_rate_n_flags,  				     info->band, &info->control.rates[0]);  	info->control.rates[0].count = 1; @@ -2706,6 +2707,7 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,  		iwl_mvm_hwrate_to_tx_rate_v1(last_ucode_rate, info->band,  					     &txrc->reported_rate);  	} +	spin_unlock_bh(&lq_sta->pers.lock);  }  static void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, @@ -2714,7 +2716,7 @@ static void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;  	struct iwl_mvm *mvm  = IWL_OP_MODE_GET_MVM(op_mode); -	struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; +	struct iwl_lq_sta *lq_sta = &mvmsta->deflink.lq_sta.rs_drv;  	IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); @@ -2885,8 +2887,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)  		nss = ((rate & RATE_HT_MCS_NSS_MSK_V1) >> RATE_HT_MCS_NSS_POS_V1) + 1;  	} else if (rate & RATE_MCS_VHT_MSK_V1) {  		mvm->drv_rx_stats.vht_frames++; -		nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> -		       RATE_VHT_MCS_NSS_POS) + 1; +		nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;  	} else {  		mvm->drv_rx_stats.legacy_frames++;  	} @@ -2921,18 +2922,18 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;  	struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; +	struct iwl_lq_sta *lq_sta = &mvmsta->deflink.lq_sta.rs_drv;  	struct ieee80211_supported_band *sband;  	unsigned long supp; /* must be unsigned long for for_each_set_bit */ -	lockdep_assert_held(&mvmsta->lq_sta.rs_drv.pers.lock); +	lockdep_assert_held(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);  	/* clear all non-persistent lq data */  	memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));  	sband = hw->wiphy->bands[band]; -	lq_sta->lq.sta_id = mvmsta->sta_id; +	lq_sta->lq.sta_id = mvmsta->deflink.sta_id;  	mvmsta->amsdu_enabled = 0;  	mvmsta->max_amsdu_len = sta->cur->max_amsdu_len; @@ -2944,7 +2945,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	IWL_DEBUG_RATE(mvm,  		       "LQ: *** rate scale station global init for station %d ***\n", -		       mvmsta->sta_id); +		       mvmsta->deflink.sta_id);  	/* TODO: what is a good starting rate for STA? About middle? Maybe not  	 * the lowest or the highest rate.. Could consider using RSSI from  	 * previous packets? Need to have IEEE 802.1X auth succeed immediately @@ -3006,17 +3007,20 @@ static void rs_drv_rate_update(void *mvm_r,  			       void *priv_sta, u32 changed)  {  	struct iwl_op_mode *op_mode = mvm_r; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);  	u8 tid; -	if (!iwl_mvm_sta_from_mac80211(sta)->vif) +	if (!mvmsta->vif)  		return;  	/* Stop any ongoing aggregations as rs starts off assuming no agg */  	for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)  		ieee80211_stop_tx_ba_session(sta, tid); -	iwl_mvm_rs_rate_init(mvm, sta, sband->band, true); +	iwl_mvm_rs_rate_init(mvm, mvmsta->vif, sta, +			     &mvmsta->vif->bss_conf, &sta->deflink, +			     sband->band);  }  static void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, @@ -3036,7 +3040,7 @@ static void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm,  	u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);  	u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; +	struct iwl_lq_sta *lq_sta = &mvmsta->deflink.lq_sta.rs_drv;  	if (!lq_sta->pers.drv) {  		IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); @@ -3260,11 +3264,11 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	/* If it's locked we are in middle of init flow  	 * just wait for next tx status to update the lq_sta data  	 */ -	if (!spin_trylock(&mvmsta->lq_sta.rs_drv.pers.lock)) +	if (!spin_trylock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock))  		return;  	__iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp); -	spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); +	spin_unlock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);  }  #ifdef CONFIG_MAC80211_DEBUGFS @@ -3440,7 +3444,7 @@ static void rs_bfer_active_iter(void *_data,  {  	struct rs_bfer_active_iter_data *data = _data;  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.rs_drv.lq; +	struct iwl_lq_cmd *lq_cmd = &mvmsta->deflink.lq_sta.rs_drv.lq;  	u32 ss_params = le32_to_cpu(lq_cmd->ss_params);  	if (sta == data->exclude_sta) @@ -3471,7 +3475,8 @@ static int rs_bfer_priority(struct iwl_mvm_sta *sta)  		prio = 1;  		break;  	default: -		WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); +		WARN_ONCE(true, "viftype %d sta_id %d", viftype, +			  sta->deflink.sta_id);  		prio = -1;  	} @@ -3548,12 +3553,12 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,  	}  	IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", -		       bfer_mvmsta->sta_id); +		       bfer_mvmsta->deflink.sta_id);  	/* Disallow BFER on another STA if active and we're a higher priority */  	if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) {  		struct iwl_lq_cmd *bfersta_lq_cmd = -			&bfer_mvmsta->lq_sta.rs_drv.lq; +			&bfer_mvmsta->deflink.lq_sta.rs_drv.lq;  		u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params);  		bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; @@ -3563,7 +3568,7 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,  		ss_params |= LQ_SS_BFER_ALLOWED;  		IWL_DEBUG_RATE(mvm,  			       "Lower priority BFER sta found (%d). Switch BFER\n", -			       bfer_mvmsta->sta_id); +			       bfer_mvmsta->deflink.sta_id);  	}  out:  	lq_cmd->ss_params = cpu_to_le32(ss_params); @@ -3605,7 +3610,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,  	    num_of_ant(initial_rate->ant) == 1)  		lq_cmd->single_stream_ant_msk = initial_rate->ant; -	lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; +	lq_cmd->agg_frame_cnt_limit = lq_sta->pers.max_agg_bufsize;  	/*  	 * In case of low latency, tell the firmware to leave a frame in the @@ -3665,8 +3670,7 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate)  	if (rate & RATE_MCS_VHT_MSK_V1) {  		type = "VHT";  		mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; -		nss = ((rate & RATE_VHT_MCS_NSS_MSK) -		       >> RATE_VHT_MCS_NSS_POS) + 1; +		nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;  	} else if (rate & RATE_MCS_HT_MSK_V1) {  		type = "HT";  		mcs = rate & RATE_HT_MCS_INDEX_MSK_V1; @@ -3675,8 +3679,7 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate)  	} else if (rate & RATE_MCS_HE_MSK_V1) {  		type = "HE";  		mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; -		nss = ((rate & RATE_VHT_MCS_NSS_MSK) -		       >> RATE_VHT_MCS_NSS_POS) + 1; +		nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1;  	} else {  		type = "Unknown"; /* shouldn't happen */  	} @@ -3750,7 +3753,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,  	struct iwl_lq_sta *lq_sta = file->private_data;  	struct iwl_mvm_sta *mvmsta = -		container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv); +		container_of(lq_sta, struct iwl_mvm_sta, deflink.lq_sta.rs_drv);  	struct iwl_mvm *mvm;  	struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);  	struct rs_rate *rate = &tbl->rate; @@ -4051,7 +4054,8 @@ static void rs_drv_add_sta_debugfs(void *mvm, void *priv_sta,  	struct iwl_lq_sta *lq_sta = priv_sta;  	struct iwl_mvm_sta *mvmsta; -	mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv); +	mvmsta = container_of(lq_sta, struct iwl_mvm_sta, +			      deflink.lq_sta.rs_drv);  	if (!mvmsta->vif)  		return; @@ -4100,17 +4104,22 @@ static const struct rate_control_ops rs_mvm_ops_drv = {  	.capa = RATE_CTRL_CAPA_VHT_EXT_NSS_BW,  }; -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, -			  enum nl80211_band band, bool update) +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, +			  struct ieee80211_vif *vif, +			  struct ieee80211_sta *sta, +			  struct ieee80211_bss_conf *link_conf, +			  struct ieee80211_link_sta *link_sta, +			  enum nl80211_band band)  {  	if (iwl_mvm_has_tlc_offload(mvm)) { -		rs_fw_rate_init(mvm, sta, band, update); +		iwl_mvm_rs_fw_rate_init(mvm, vif, sta, link_conf, +					link_sta, band);  	} else {  		struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -		spin_lock(&mvmsta->lq_sta.rs_drv.pers.lock); +		spin_lock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);  		rs_drv_rate_init(mvm, sta, band); -		spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); +		spin_unlock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);  	}  } @@ -4127,7 +4136,7 @@ void iwl_mvm_rate_control_unregister(void)  static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  				bool enable)  { -	struct iwl_lq_cmd *lq = &mvmsta->lq_sta.rs_drv.lq; +	struct iwl_lq_cmd *lq = &mvmsta->deflink.lq_sta.rs_drv.lq;  	lockdep_assert_held(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index b7bc8c1b2dda..1ca375a5cf6b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -1,10 +1,9 @@  /* SPDX-License-Identifier: GPL-2.0-only */  /******************************************************************************   * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.   * Copyright(c) 2015 Intel Mobile Communications GmbH   * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2018 - 2019 Intel Corporation + * Copyright (C) 2003 - 2014, 2018 - 2022 Intel Corporation   *****************************************************************************/  #ifndef __rs_h__ @@ -204,6 +203,7 @@ struct rs_rate {  /**   * struct iwl_lq_sta_rs_fw - rate and related statistics for RS in FW   * @last_rate_n_flags: last rate reported by FW + * @max_agg_bufsize: the maximal size of the AGG buffer for this station   * @sta_id: the id of the station  #ifdef CONFIG_MAC80211_DEBUGFS   * @dbg_fixed_rate: for debug, use fixed rate if not 0 @@ -353,6 +353,7 @@ struct iwl_lq_sta {  	/* last tx rate_n_flags */  	u32 last_rate_n_flags; +  	/* packets destined for this STA are aggregated */  	u8 is_agg; @@ -371,6 +372,7 @@ struct iwl_lq_sta {  		u8 chains;  		s8 chain_signal[IEEE80211_MAX_CHAINS];  		s8 last_rssi; +		u16 max_agg_bufsize;  		struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];  		struct iwl_mvm *drv;  		spinlock_t lock; /* for races in reinit/update table */ @@ -392,8 +394,12 @@ struct iwl_lq_sta {  				   ((_c) << RS_DRV_DATA_LQ_COLOR_POS)))  /* Initialize station's rate scaling information after adding station */ -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, -			  enum nl80211_band band, bool init); +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, +			  struct ieee80211_vif *vif, +			  struct ieee80211_sta *sta, +			  struct ieee80211_bss_conf *link_conf, +			  struct ieee80211_link_sta *link_sta, +			  enum nl80211_band band);  /* Notify RS about Tx status */  void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, @@ -428,13 +434,24 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm);  #endif +struct iwl_mvm_link_sta; +  void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta); -void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, -		     enum nl80211_band band, bool update); +void iwl_mvm_rs_add_sta_link(struct iwl_mvm *mvm, +			     struct iwl_mvm_link_sta *link_sta); + +void iwl_mvm_rs_fw_rate_init(struct iwl_mvm *mvm, +			     struct ieee80211_vif *vif, +			     struct ieee80211_sta *sta, +			     struct ieee80211_bss_conf *link_conf, +			     struct ieee80211_link_sta *link_sta, +			     enum nl80211_band band);  int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  			bool enable);  void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,  			      struct iwl_rx_cmd_buffer *rxb); -u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta); +u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta, +			    struct ieee80211_bss_conf *link_conf, +			    struct ieee80211_link_sta *link_sta);  #endif /* __rs__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 49ca1e168fc5..b38b24246675 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -190,7 +190,7 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,  	default:  		/* Expected in monitor (not having the keys) */  		if (!mvm->monitor_on) -			IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); +			IWL_WARN(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);  	}  	return 0; @@ -237,11 +237,11 @@ static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,  	if (mdata->opened_rx_ba_sessions ||  	    mdata->uapsd_nonagg_detect.detected || -	    (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && -	     !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && -	     !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && -	     !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) || -	    mvmsta->sta_id != mvmvif->ap_sta_id) +	    (!mvmvif->deflink.queue_params[IEEE80211_AC_VO].uapsd && +	     !mvmvif->deflink.queue_params[IEEE80211_AC_VI].uapsd && +	     !mvmvif->deflink.queue_params[IEEE80211_AC_BE].uapsd && +	     !mvmvif->deflink.queue_params[IEEE80211_AC_BK].uapsd) || +	    mvmsta->deflink.sta_id != mvmvif->deflink.ap_sta_id)  		return;  	if (rate_n_flags & RATE_MCS_HT_MSK_V1) { @@ -253,8 +253,7 @@ static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,  				ARRAY_SIZE(thresh_tpt)))  			return;  		thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK]; -		thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> -					RATE_VHT_MCS_NSS_POS); +		thr *= 1 + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags);  	}  	thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK_V1) >> @@ -384,9 +383,10 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,  		 * Don't even try to decrypt a MCAST frame that was received  		 * before the managed vif is authorized, we'd fail anyway.  		 */ -		if (vif->type == NL80211_IFTYPE_STATION && +		if (is_multicast_ether_addr(hdr->addr1) && +		    vif->type == NL80211_IFTYPE_STATION &&  		    !mvmvif->authorized && -		    is_multicast_ether_addr(hdr->addr1)) { +		    ieee80211_has_protected(hdr->frame_control)) {  			IWL_DEBUG_DROP(mvm, "MCAST before the vif is authorized\n");  			kfree_skb(skb);  			rcu_read_unlock(); @@ -500,8 +500,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,  		u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >>  				RATE_MCS_STBC_POS;  		rx_status->nss = -			((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> -						RATE_VHT_MCS_NSS_POS) + 1; +			FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1;  		rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;  		rx_status->encoding = RX_ENC_VHT;  		rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; @@ -630,9 +629,9 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,  	 * data copied into the "data" struct, but rather the data from  	 * the notification directly.  	 */ -	mvmvif->beacon_stats.num_beacons = +	mvmvif->deflink.beacon_stats.num_beacons =  		le32_to_cpu(data->beacon_counter[vif_id]); -	mvmvif->beacon_stats.avg_signal = +	mvmvif->deflink.beacon_stats.avg_signal =  		-data->beacon_average_energy[vif_id];  	if (mvmvif->id != id) @@ -645,8 +644,8 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,  	 * request to clear statistics  	 */  	if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) -		mvmvif->beacon_stats.accu_num_beacons += -			mvmvif->beacon_stats.num_beacons; +		mvmvif->deflink.beacon_stats.accu_num_beacons += +			mvmvif->deflink.beacon_stats.num_beacons;  	iwl_mvm_update_vif_sig(vif, sig);  } @@ -668,17 +667,17 @@ static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac,  	mac_stats = &data->per_mac_stats[vif_id]; -	mvmvif->beacon_stats.num_beacons = +	mvmvif->deflink.beacon_stats.num_beacons =  		le32_to_cpu(mac_stats->beacon_counter); -	mvmvif->beacon_stats.avg_signal = +	mvmvif->deflink.beacon_stats.avg_signal =  		-le32_to_cpu(mac_stats->beacon_average_energy);  	/* make sure that beacon statistics don't go backwards with TCM  	 * request to clear statistics  	 */  	if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR) -		mvmvif->beacon_stats.accu_num_beacons += -			mvmvif->beacon_stats.num_beacons; +		mvmvif->deflink.beacon_stats.accu_num_beacons += +			mvmvif->deflink.beacon_stats.num_beacons;  	sig = -le32_to_cpu(mac_stats->beacon_filter_average_energy);  	iwl_mvm_update_vif_sig(vif, sig); @@ -714,14 +713,14 @@ static void iwl_mvm_stats_energy_iter(void *_data,  {  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	u8 *energy = _data; -	u32 sta_id = mvmsta->sta_id; +	u32 sta_id = mvmsta->deflink.sta_id;  	if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT_MAX, "sta_id %d >= %d",  		      sta_id, IWL_MVM_STATION_COUNT_MAX))  		return;  	if (energy[sta_id]) -		mvmsta->avg_energy = energy[sta_id]; +		mvmsta->deflink.avg_energy = energy[sta_id];  } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 549dbe0be223..6226e4e54a51 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023 Intel Corporation   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH   * Copyright (C) 2015-2017 Intel Deutschland GmbH   */ @@ -9,6 +9,7 @@  #include "iwl-trans.h"  #include "mvm.h"  #include "fw-api.h" +#include "time-sync.h"  static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,  				   int queue, struct ieee80211_sta *sta) @@ -105,28 +106,12 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb,  	/*  	 * For non monitor interface strip the bytes the RADA might not have -	 * removed. As monitor interface cannot exist with other interfaces -	 * this removal is safe. +	 * removed (it might be disabled, e.g. for mgmt frames). As a monitor +	 * interface cannot exist with other interfaces, this removal is safe +	 * and sufficient, in monitor mode there's no decryption being done.  	 */ -	if (mic_crc_len && !ieee80211_hw_check(mvm->hw, RX_INCLUDES_FCS)) { -		u32 pkt_flags = le32_to_cpu(pkt->len_n_flags); - -		/* -		 * If RADA was not enabled then decryption was not performed so -		 * the MIC cannot be removed. -		 */ -		if (!(pkt_flags & FH_RSCSR_RADA_EN)) { -			if (WARN_ON(crypt_len > mic_crc_len)) -				return -EINVAL; - -			mic_crc_len -= crypt_len; -		} - -		if (WARN_ON(mic_crc_len > len)) -			return -EINVAL; - +	if (len > mic_crc_len && !ieee80211_hw_check(mvm->hw, RX_INCLUDES_FCS))  		len -= mic_crc_len; -	}  	/* If frame is small enough to fit in skb->head, pull it completely.  	 * If not, only pull ieee80211_hdr (including crypto if present, and @@ -171,8 +156,7 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb,  	 * Starting from Bz hardware, it calculates starting directly after  	 * the MAC header, so that matches mac80211's expectation.  	 */ -	if (skb->ip_summed == CHECKSUM_COMPLETE && -	    mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) { +	if (skb->ip_summed == CHECKSUM_COMPLETE) {  		struct {  			u8 hdr[6];  			__be16 type; @@ -187,7 +171,7 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb,  			      shdr->type != htons(ETH_P_PAE) &&  			      shdr->type != htons(ETH_P_TDLS))))  			skb->ip_summed = CHECKSUM_NONE; -		else +		else if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)  			/* mac80211 assumes full CSUM including SNAP header */  			skb_postpush_rcsum(skb, shdr, sizeof(*shdr));  	} @@ -205,49 +189,69 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb,  	return 0;  } +/* put a TLV on the skb and return data pointer + * + * Also pad to 4 the len and zero out all data part + */ +static void * +iwl_mvm_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len) +{ +	struct ieee80211_radiotap_tlv *tlv; + +	tlv = skb_put(skb, sizeof(*tlv)); +	tlv->type = cpu_to_le16(type); +	tlv->len = cpu_to_le16(len); +	return skb_put_zero(skb, ALIGN(len, 4)); +} +  static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm,  					    struct sk_buff *skb)  {  	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); -	struct ieee80211_vendor_radiotap *radiotap; -	const int size = sizeof(*radiotap) + sizeof(__le16); +	struct ieee80211_radiotap_vendor_content *radiotap; +	const u16 vendor_data_len = sizeof(mvm->cur_aid);  	if (!mvm->cur_aid)  		return; -	/* ensure alignment */ -	BUILD_BUG_ON((size + 2) % 4); +	radiotap = iwl_mvm_radiotap_put_tlv(skb, +					    IEEE80211_RADIOTAP_VENDOR_NAMESPACE, +					    sizeof(*radiotap) + vendor_data_len); -	radiotap = skb_put(skb, size + 2); -	radiotap->align = 1;  	/* Intel OUI */  	radiotap->oui[0] = 0xf6;  	radiotap->oui[1] = 0x54;  	radiotap->oui[2] = 0x25;  	/* radiotap sniffer config sub-namespace */ -	radiotap->subns = 1; -	radiotap->present = 0x1; -	radiotap->len = size - sizeof(*radiotap); -	radiotap->pad = 2; +	radiotap->oui_subtype = 1; +	radiotap->vendor_type = 0;  	/* fill the data now */  	memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid)); -	/* and clear the padding */ -	memset(radiotap->data + sizeof(__le16), 0, radiotap->pad); -	rx_status->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA; +	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;  }  /* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */  static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,  					    struct napi_struct *napi,  					    struct sk_buff *skb, int queue, -					    struct ieee80211_sta *sta) +					    struct ieee80211_sta *sta, +					    struct ieee80211_link_sta *link_sta)  { -	if (iwl_mvm_check_pn(mvm, skb, queue, sta)) +	if (unlikely(iwl_mvm_check_pn(mvm, skb, queue, sta))) {  		kfree_skb(skb); -	else -		ieee80211_rx_napi(mvm->hw, sta, skb, napi); +		return; +	} + +	if (sta && sta->valid_links && link_sta) { +		struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + +		rx_status->link_valid = 1; +		rx_status->link_id = link_sta->link_id; +	} + +	ieee80211_rx_napi(mvm->hw, sta, skb, napi);  }  static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, @@ -391,9 +395,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))  			return -1; -		stats->flag |= RX_FLAG_DECRYPTED; -		if (pkt_flags & FH_RSCSR_RADA_EN) -			stats->flag |= RX_FLAG_MIC_STRIPPED; +		stats->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED;  		*crypt_len = IEEE80211_CCMP_HDR_LEN;  		return 0;  	case IWL_RX_MPDU_STATUS_SEC_TKIP: @@ -443,7 +445,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		 */  		if (!is_multicast_ether_addr(hdr->addr1) &&  		    !mvm->monitor_on && net_ratelimit()) -			IWL_ERR(mvm, "Unhandled alg: 0x%x\n", status); +			IWL_WARN(mvm, "Unhandled alg: 0x%x\n", status);  	}  	return 0; @@ -620,7 +622,7 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,  		while ((skb = __skb_dequeue(skb_list))) {  			iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb,  							reorder_buf->queue, -							sta); +							sta, NULL /* FIXME */);  			reorder_buf->num_stored--;  		}  	} @@ -685,10 +687,15 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t)  	if (expired) {  		struct ieee80211_sta *sta;  		struct iwl_mvm_sta *mvmsta; -		u8 sta_id = baid_data->sta_id; +		u8 sta_id = ffs(baid_data->sta_mask) - 1;  		rcu_read_lock();  		sta = rcu_dereference(buf->mvm->fw_id_to_mac_id[sta_id]); +		if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { +			rcu_read_unlock(); +			goto out; +		} +  		mvmsta = iwl_mvm_sta_from_mac80211(sta);  		/* SN is set to the last expired frame + 1 */ @@ -710,6 +717,8 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t)  			  entries[index].e.reorder_time +  			  1 + RX_REORDER_BUF_TIMEOUT_MQ);  	} + +out:  	spin_unlock(&buf->lock);  } @@ -720,6 +729,7 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue,  	struct ieee80211_sta *sta;  	struct iwl_mvm_reorder_buffer *reorder_buf;  	u8 baid = data->baid; +	u32 sta_id;  	if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid))  		return; @@ -730,7 +740,9 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue,  	if (WARN_ON_ONCE(!ba_data))  		goto out; -	sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); +	/* pick any STA ID to find the pointer */ +	sta_id = ffs(ba_data->sta_mask) - 1; +	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);  	if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta)))  		goto out; @@ -757,6 +769,7 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm,  	struct ieee80211_sta *sta;  	struct iwl_mvm_reorder_buffer *reorder_buf;  	struct iwl_mvm_baid_data *ba_data; +	u32 sta_id;  	IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n",  		     baid, nssn); @@ -774,7 +787,9 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm,  		goto out;  	} -	sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); +	/* pick any STA ID to find the pointer */ +	sta_id = ffs(ba_data->sta_mask) - 1; +	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);  	if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta)))  		goto out; @@ -915,7 +930,6 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,  {  	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); -	struct iwl_mvm_sta *mvm_sta;  	struct iwl_mvm_baid_data *baid_data;  	struct iwl_mvm_reorder_buffer *buffer;  	struct sk_buff *tail; @@ -927,6 +941,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,  	u8 sub_frame_idx = desc->amsdu_info &  			   IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;  	struct iwl_mvm_reorder_buf_entry *entries; +	u32 sta_mask;  	int index;  	u16 nssn, sn;  	u8 baid; @@ -949,8 +964,6 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,  		      "Got valid BAID without a valid station assigned\n"))  		return false; -	mvm_sta = iwl_mvm_sta_from_mac80211(sta); -  	/* not a data packet or a bar */  	if (!ieee80211_is_back_req(hdr->frame_control) &&  	    (!ieee80211_is_data_qos(hdr->frame_control) || @@ -968,10 +981,14 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,  		return false;  	} -	if (WARN(tid != baid_data->tid || mvm_sta->sta_id != baid_data->sta_id, -		 "baid 0x%x is mapped to sta:%d tid:%d, but was received for sta:%d tid:%d\n", -		 baid, baid_data->sta_id, baid_data->tid, mvm_sta->sta_id, -		 tid)) +	rcu_read_lock(); +	sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1); +	rcu_read_unlock(); + +	if (WARN(tid != baid_data->tid || +		 !(sta_mask & baid_data->sta_mask), +		 "baid 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n", +		 baid, baid_data->sta_mask, baid_data->tid, sta_mask, tid))  		return false;  	nssn = reorder & IWL_RX_MPDU_REORDER_NSSN_MASK; @@ -1167,8 +1184,11 @@ static void iwl_mvm_flip_address(u8 *addr)  struct iwl_mvm_rx_phy_data {  	enum iwl_rx_phy_info_type info_type; -	__le32 d0, d1, d2, d3; +	__le32 d0, d1, d2, d3, eht_d4, d5;  	__le16 d4; +	bool with_data; +	bool first_subframe; +	__le32 rx_vec[4];  	u32 rate_n_flags;  	u32 gp2_on_air_rise; @@ -1446,6 +1466,528 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm,  	}  } +#define LE32_DEC_ENC(value, dec_bits, enc_bits) \ +	le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +#define IWL_MVM_ENC_USIG_VALUE_MASK(usig, in_value, dec_bits, enc_bits) do { \ +	typeof(enc_bits) _enc_bits = enc_bits; \ +	typeof(usig) _usig = usig; \ +	(_usig)->mask |= cpu_to_le32(_enc_bits); \ +	(_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \ +} while (0) + +#define __IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ +	eht->data[(rt_data)] |= \ +		(cpu_to_le32 \ +		 (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \ +		 LE32_DEC_ENC(data ## fw_data, \ +			      IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \ +			      IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru)) + +#define _IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru)	\ +	__IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) + +#define IEEE80211_RADIOTAP_RU_DATA_1_1_1	1 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_1	2 +#define IEEE80211_RADIOTAP_RU_DATA_1_1_2	2 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_2	2 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_1	3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_1	3 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_2	3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_2	4 + +#define IWL_RX_RU_DATA_A1			2 +#define IWL_RX_RU_DATA_A2			2 +#define IWL_RX_RU_DATA_B1			2 +#define IWL_RX_RU_DATA_B2			3 +#define IWL_RX_RU_DATA_C1			3 +#define IWL_RX_RU_DATA_C2			3 +#define IWL_RX_RU_DATA_D1			4 +#define IWL_RX_RU_DATA_D2			4 + +#define IWL_MVM_ENC_EHT_RU(rt_ru, fw_ru)				\ +	_IWL_MVM_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru,	\ +			    rt_ru,					\ +			    IWL_RX_RU_DATA_ ## fw_ru,			\ +			    fw_ru) + +static void iwl_mvm_decode_eht_ext_mu(struct iwl_mvm *mvm, +				      struct iwl_mvm_rx_phy_data *phy_data, +				      struct ieee80211_rx_status *rx_status, +				      struct ieee80211_radiotap_eht *eht, +				      struct ieee80211_radiotap_eht_usig *usig) +{ +	if (phy_data->with_data) { +		__le32 data1 = phy_data->d1; +		__le32 data2 = phy_data->d2; +		__le32 data3 = phy_data->d3; +		__le32 data4 = phy_data->eht_d4; +		__le32 data5 = phy_data->d5; +		u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, +					    IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, +					    IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data4, +					    IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); +		IWL_MVM_ENC_USIG_VALUE_MASK +			(usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2, +			 IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + +		eht->user_info[0] |= +			cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | +			LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR, +				     IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); + +		eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M); +		eht->data[7] |= LE32_DEC_ENC +			(data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA, +			 IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + +		/* +		 * Hardware labels the content channels/RU allocation values +		 * as follows: +		 *           Content Channel 1		Content Channel 2 +		 *   20 MHz: A1 +		 *   40 MHz: A1				B1 +		 *   80 MHz: A1 C1			B1 D1 +		 *  160 MHz: A1 C1 A2 C2		B1 D1 B2 D2 +		 *  320 MHz: A1 C1 A2 C2 A3 C3 A4 C4	B1 D1 B2 D2 B3 D3 B4 D4 +		 * +		 * However firmware can only give us A1-D2, so the higher +		 * frequencies are missing. +		 */ + +		switch (phy_bw) { +		case RATE_MCS_CHAN_WIDTH_320: +			/* additional values are missing in RX metadata */ +		case RATE_MCS_CHAN_WIDTH_160: +			/* content channel 1 */ +			IWL_MVM_ENC_EHT_RU(1_2_1, A2); +			IWL_MVM_ENC_EHT_RU(1_2_2, C2); +			/* content channel 2 */ +			IWL_MVM_ENC_EHT_RU(2_2_1, B2); +			IWL_MVM_ENC_EHT_RU(2_2_2, D2); +			fallthrough; +		case RATE_MCS_CHAN_WIDTH_80: +			/* content channel 1 */ +			IWL_MVM_ENC_EHT_RU(1_1_2, C1); +			/* content channel 2 */ +			IWL_MVM_ENC_EHT_RU(2_1_2, D1); +			fallthrough; +		case RATE_MCS_CHAN_WIDTH_40: +			/* content channel 2 */ +			IWL_MVM_ENC_EHT_RU(2_1_1, B1); +			fallthrough; +		case RATE_MCS_CHAN_WIDTH_20: +			IWL_MVM_ENC_EHT_RU(1_1_1, A1); +			break; +		} +	} else { +		__le32 usig_a1 = phy_data->rx_vec[0]; +		__le32 usig_a2 = phy_data->rx_vec[1]; + +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, +					    IWL_RX_USIG_A1_DISREGARD, +					    IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, +					    IWL_RX_USIG_A1_VALIDATE, +					    IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_PPDU_TYPE, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_PUNC_CHANNEL, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_SIG_MCS, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); +		IWL_MVM_ENC_USIG_VALUE_MASK +			(usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM, +			 IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_CRC_OK, +					    IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC); +	} +} + +static void iwl_mvm_decode_eht_ext_tb(struct iwl_mvm *mvm, +				      struct iwl_mvm_rx_phy_data *phy_data, +				      struct ieee80211_rx_status *rx_status, +				      struct ieee80211_radiotap_eht *eht, +				      struct ieee80211_radiotap_eht_usig *usig) +{ +	if (phy_data->with_data) { +		__le32 data5 = phy_data->d5; + +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, +					    IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, +					    IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, +					    IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); +	} else { +		__le32 usig_a1 = phy_data->rx_vec[0]; +		__le32 usig_a2 = phy_data->rx_vec[1]; + +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, +					    IWL_RX_USIG_A1_DISREGARD, +					    IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_PPDU_TYPE, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD); +		IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, +					    IWL_RX_USIG_A2_EHT_CRC_OK, +					    IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC); +	} +} + +static void iwl_mvm_decode_eht_ru(struct iwl_mvm *mvm, +				  struct ieee80211_rx_status *rx_status, +				  struct ieee80211_radiotap_eht *eht) +{ +	u32 ru = le32_get_bits(eht->data[8], +			       IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); +	enum nl80211_eht_ru_alloc nl_ru; + +	/* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields +	 * in an EHT variant User Info field +	 */ + +	switch (ru) { +	case 0 ... 36: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_26; +		break; +	case 37 ... 52: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52; +		break; +	case 53 ... 60: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106; +		break; +	case 61 ... 64: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_242; +		break; +	case 65 ... 66: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484; +		break; +	case 67: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996; +		break; +	case 68: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; +		break; +	case 69: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; +		break; +	case 70 ... 81: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; +		break; +	case 82 ... 89: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; +		break; +	case 90 ... 93: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; +		break; +	case 94 ... 95: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; +		break; +	case 96 ... 99: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; +		break; +	case 100 ... 103: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; +		break; +	case 104: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; +		break; +	case 105 ... 106: +		nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; +		break; +	default: +		return; +	} + +	rx_status->bw = RATE_INFO_BW_EHT_RU; +	rx_status->eht.ru = nl_ru; +} + +static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, +					struct iwl_mvm_rx_phy_data *phy_data, +					struct ieee80211_rx_status *rx_status, +					struct ieee80211_radiotap_eht *eht, +					struct ieee80211_radiotap_eht_usig *usig) + +{ +	__le32 data0 = phy_data->d0; +	__le32 data1 = phy_data->d1; +	__le32 usig_a1 = phy_data->rx_vec[0]; +	u8 info_type = phy_data->info_type; + +	/* Not in EHT range */ +	if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU || +	    info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT) +		return; + +	usig->common |= cpu_to_le32 +		(IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | +		 IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); +	if (phy_data->with_data) { +		usig->common |= LE32_DEC_ENC(data0, +					     IWL_RX_PHY_DATA0_EHT_UPLINK, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); +		usig->common |= LE32_DEC_ENC(data0, +					     IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); +	} else { +		usig->common |= LE32_DEC_ENC(usig_a1, +					     IWL_RX_USIG_A1_UL_FLAG, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); +		usig->common |= LE32_DEC_ENC(usig_a1, +					     IWL_RX_USIG_A1_BSS_COLOR, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); +	} + +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE); +	eht->data[0] |= LE32_DEC_ENC(data0, +				     IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK, +				     IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + +	/* All RU allocating size/index is in TB format */ +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT); +	eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160, +				     IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160); +	eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_B0, +				     IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0); +	eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_B1_B7_ALLOC, +				     IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + +	iwl_mvm_decode_eht_ru(mvm, rx_status, eht); + +	/* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set +	 * which is on only in case of monitor mode so no need to check monitor +	 * mode +	 */ +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80); +	eht->data[1] |= +		le32_encode_bits(mvm->monitor_p80, +				 IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80); + +	usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); +	if (phy_data->with_data) +		usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); +	else +		usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION, +					     IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM); +	eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM, +				     IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM); +	eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK, +				    IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM); +	eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG, +				     IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + +	/* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */ + +	if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK)) +		usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + +	usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN); +	usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER, +				     IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER); + +	/* +	 * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE, +	 *			 IWL_RX_PHY_DATA1_EHT_TB_LOW_SS +	 */ + +	eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF); +	eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM, +				     IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + +	if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT || +	    info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB) +		iwl_mvm_decode_eht_ext_tb(mvm, phy_data, rx_status, eht, usig); + +	if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT || +	    info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU) +		iwl_mvm_decode_eht_ext_mu(mvm, phy_data, rx_status, eht, usig); +} + +static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, +			   struct iwl_mvm_rx_phy_data *phy_data, +			   int queue) +{ +	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + +	struct ieee80211_radiotap_eht *eht; +	struct ieee80211_radiotap_eht_usig *usig; +	size_t eht_len = sizeof(*eht); + +	u32 rate_n_flags = phy_data->rate_n_flags; +	u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; +	/* EHT and HE have the same valus for LTF */ +	u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; +	u16 phy_info = phy_data->phy_info; +	u32 bw; + +	/* u32 for 1 user_info */ +	if (phy_data->with_data) +		eht_len += sizeof(u32); + +	eht = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len); + +	usig = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, +					sizeof(*usig)); +	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; +	usig->common |= +		cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN); + +	/* specific handling for 320MHz */ +	bw = FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags); +	if (bw == RATE_MCS_CHAN_WIDTH_320_VAL) +		bw += FIELD_GET(IWL_RX_PHY_DATA0_EHT_BW320_SLOT, +				le32_to_cpu(phy_data->d0)); + +	usig->common |= cpu_to_le32 +		(FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw)); + +	/* report the AMPDU-EOF bit on single frames */ +	if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { +		rx_status->flag |= RX_FLAG_AMPDU_DETAILS; +		rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; +		if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) +			rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; +	} + +	/* update aggregation data for monitor sake on default queue */ +	if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && +	    (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { +		rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; +		if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) +			rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; +	} + +	if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) +		iwl_mvm_decode_eht_phy_data(mvm, phy_data, rx_status, eht, usig); + +#define CHECK_TYPE(F)							\ +	BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F !=	\ +		     (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + +	CHECK_TYPE(SU); +	CHECK_TYPE(EXT_SU); +	CHECK_TYPE(MU); +	CHECK_TYPE(TRIG); + +	switch (FIELD_GET(RATE_MCS_HE_GI_LTF_MSK, rate_n_flags)) { +	case 0: +		if (he_type == RATE_MCS_HE_TYPE_TRIG) { +			rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; +			ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; +		} else { +			rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; +			ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; +		} +		break; +	case 1: +		rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; +		ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; +		break; +	case 2: +		ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; +		if (he_type == RATE_MCS_HE_TYPE_TRIG) +			rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; +		else +			rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; +		break; +	case 3: +		if (he_type != RATE_MCS_HE_TYPE_TRIG) { +			ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; +			rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; +		} +		break; +	default: +		/* nothing here */ +		break; +	} + +	if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) { +		eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI); +		eht->data[0] |= cpu_to_le32 +			(FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF, +				    ltf) | +			 FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI, +				    rx_status->eht.gi)); +	} + + +	if (!phy_data->with_data) { +		eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | +					  IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S); +		eht->data[7] |= +			le32_encode_bits(le32_get_bits(phy_data->rx_vec[2], +						       RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK), +					 IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); +		if (rate_n_flags & RATE_MCS_BF_MSK) +			eht->data[7] |= +				cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); +	} else { +		eht->user_info[0] |= +			cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | +				    IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | +				    IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | +				    IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | +				    IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); + +		if (rate_n_flags & RATE_MCS_BF_MSK) +			eht->user_info[0] |= +				cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + +		if (rate_n_flags & RATE_MCS_LDPC_MSK) +			eht->user_info[0] |= +				cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); + +		eht->user_info[0] |= cpu_to_le32 +			(FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, +				    FIELD_GET(RATE_VHT_MCS_RATE_CODE_MSK, +					      rate_n_flags)) | +			 FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, +				    FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags))); +	} +} +  static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,  			  struct iwl_mvm_rx_phy_data *phy_data,  			  int queue) @@ -1497,15 +2039,10 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,  	/* update aggregation data for monitor sake on default queue */  	if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && -	    (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { -		bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; - -		/* toggle is switched whenever new aggregation starts */ -		if (toggle_bit != mvm->ampdu_toggle) { -			rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; -			if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF)) -				rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; -		} +	    (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { +		rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; +		if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) +			rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;  	}  	if (he_type == RATE_MCS_HE_TYPE_EXT_SU && @@ -1593,6 +2130,10 @@ static void iwl_mvm_decode_lsig(struct sk_buff *skb,  	case IWL_RX_PHY_INFO_TYPE_HE_MU:  	case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:  	case IWL_RX_PHY_INFO_TYPE_HE_TB: +	case IWL_RX_PHY_INFO_TYPE_EHT_MU: +	case IWL_RX_PHY_INFO_TYPE_EHT_TB: +	case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: +	case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:  		lsig = skb_put(skb, sizeof(*lsig));  		lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN);  		lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->d1, @@ -1689,6 +2230,10 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm,  	iwl_mvm_get_signal_strength(mvm, rx_status, rate_n_flags,  				    phy_data->energy_a, phy_data->energy_b); +	/* using TLV format and must be after all fixed len fields */ +	if (format == RATE_MCS_EHT_MSK) +		iwl_mvm_rx_eht(mvm, skb, phy_data, queue); +  	if (unlikely(mvm->monitor_on))  		iwl_mvm_add_rtap_sniffer_config(mvm, skb); @@ -1758,6 +2303,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  	u32 len;  	u32 pkt_len = iwl_rx_packet_payload_len(pkt);  	struct ieee80211_sta *sta = NULL; +	struct ieee80211_link_sta *link_sta = NULL;  	struct sk_buff *skb;  	u8 crypt_len = 0;  	size_t desc_size; @@ -1788,6 +2334,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  		phy_data.d1 = desc->v3.phy_data1;  		phy_data.d2 = desc->v3.phy_data2;  		phy_data.d3 = desc->v3.phy_data3; +		phy_data.eht_d4 = desc->phy_eht_data4; +		phy_data.d5 = desc->v3.phy_data5;  	} else {  		phy_data.rate_n_flags = le32_to_cpu(desc->v1.rate_n_flags);  		phy_data.channel = desc->v1.channel; @@ -1817,6 +2365,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  		return;  	} +	phy_data.with_data = true;  	phy_data.phy_info = le16_to_cpu(desc->phy_info);  	phy_data.d4 = desc->phy_data4; @@ -1897,6 +2446,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  			if (mvm->ampdu_ref == 0)  				mvm->ampdu_ref++;  			mvm->ampdu_toggle = toggle_bit; +			phy_data.first_subframe = true;  		}  		rx_status->ampdu_reference = mvm->ampdu_ref;  	} @@ -1910,6 +2460,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  			sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);  			if (IS_ERR(sta))  				sta = NULL; +			link_sta = rcu_dereference(mvm->fw_id_to_link_sta[id]);  		}  	} else if (!is_multicast_ether_addr(hdr->addr2)) {  		/* @@ -1968,7 +2519,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  				RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);  				/* Unblock BCAST / MCAST station */  				iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); -				cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); +				cancel_delayed_work(&mvm->cs_tx_unblock_dwork);  			}  		} @@ -2043,9 +2594,11 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,  		goto out;  	} -	if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc)) -		iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, -						sta); +	if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc) && +	    likely(!iwl_mvm_time_sync_frame(mvm, skb, hdr->addr2)) && +	    likely(!iwl_mvm_mei_filter_scan(mvm, skb))) +		iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta, +						link_sta);  out:  	rcu_read_unlock();  } @@ -2079,6 +2632,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,  	phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK);  	phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK);  	phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); +	phy_data.with_data = false;  	if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,  				    RX_NO_DATA_NOTIF, 0) < 2) { @@ -2097,6 +2651,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,  		    sizeof(struct iwl_rx_no_data_ver_3)))  		/* invalid len for ver 3 */  			return; +		memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec));  	} else {  		if (format == RATE_MCS_EHT_MSK)  			/* no support for EHT before version 3 API */ @@ -2123,7 +2678,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,  			IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING;  		break;  	case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED: -	case RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED: +	case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED:  		rx_status->zero_length_psdu_type =  			IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED;  		break; @@ -2142,11 +2697,8 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,  	 *  	 * We mark it as mac header, for upper layers to know where  	 * all radio tap header ends. -	 * -	 * Since data doesn't move data while putting data on skb and that is -	 * the only way we use, data + len is the next place that hdr would be put  	 */ -	skb_set_mac_header(skb, skb->len); +	skb_reset_mac_header(skb);  	/*  	 * Override the nss from the rx_vec since the rate_n_flags has @@ -2220,9 +2772,10 @@ void iwl_mvm_rx_bar_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,  		goto out;  	} -	if (WARN(tid != baid_data->tid || sta_id != baid_data->sta_id, -		 "baid 0x%x is mapped to sta:%d tid:%d, but BAR release received for sta:%d tid:%d\n", -		 baid, baid_data->sta_id, baid_data->tid, sta_id, +	if (WARN(tid != baid_data->tid || sta_id > IWL_MVM_STATION_COUNT_MAX || +		 !(baid_data->sta_mask & BIT(sta_id)), +		 "baid 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n", +		 baid, baid_data->sta_mask, baid_data->tid, sta_id,  		 tid))  		goto out; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index acd8803dbcdd..175615755d9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -45,6 +45,9 @@  /* minimal number of 2GHz and 5GHz channels in the regular scan request */  #define IWL_MVM_6GHZ_PASSIVE_SCAN_MIN_CHANS 4 +/* Number of iterations on the channel for mei filtered scan */ +#define IWL_MEI_SCAN_NUM_ITER	5U +  struct iwl_mvm_scan_timing_params {  	u32 suspend_time;  	u32 max_out_time; @@ -98,6 +101,7 @@ struct iwl_mvm_scan_params {  	bool scan_6ghz;  	bool enable_6ghz_passive;  	bool respect_p2p_go, respect_p2p_go_hb; +	u8 bssid[ETH_ALEN] __aligned(2);  };  static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm) @@ -193,8 +197,9 @@ static void iwl_mvm_scan_iterator(void *_data, u8 *mac,  	struct iwl_mvm_scan_iter_data *data = _data;  	struct iwl_mvm_vif *curr_mvmvif; -	if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && -	    mvmvif->phy_ctxt->id < NUM_PHY_CTX) +	if (vif->type != NL80211_IFTYPE_P2P_DEVICE && +	    mvmvif->deflink.phy_ctxt && +	    mvmvif->deflink.phy_ctxt->id < NUM_PHY_CTX)  		data->global_cnt += 1;  	if (!data->current_vif || vif == data->current_vif) @@ -203,8 +208,8 @@ static void iwl_mvm_scan_iterator(void *_data, u8 *mac,  	curr_mvmvif = iwl_mvm_vif_from_mac80211(data->current_vif);  	if (vif->type == NL80211_IFTYPE_AP && vif->p2p && -	    mvmvif->phy_ctxt && curr_mvmvif->phy_ctxt && -	    mvmvif->phy_ctxt->id != curr_mvmvif->phy_ctxt->id) +	    mvmvif->deflink.phy_ctxt && curr_mvmvif->deflink.phy_ctxt && +	    mvmvif->deflink.phy_ctxt->id != curr_mvmvif->deflink.phy_ctxt->id)  		data->is_dcm_with_p2p_go = true;  } @@ -239,8 +244,9 @@ iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm,  		 * set all scan requests as fast-balance scan  		 */  		if (vif && vif->type == NL80211_IFTYPE_STATION && -		    vif->bss_conf.dtim_period < 220 && -		    data.is_dcm_with_p2p_go) +		    data.is_dcm_with_p2p_go && +		    ((vif->bss_conf.beacon_int * +		      vif->bss_conf.dtim_period) < 220))  			return IWL_SCAN_TYPE_FAST_BALANCE;  	} @@ -758,7 +764,7 @@ iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);  	eth_broadcast_addr(frame->da); -	eth_broadcast_addr(frame->bssid); +	ether_addr_copy(frame->bssid, params->bssid);  	frame->seq_ctrl = 0;  	pos = frame->u.probe_req.variable; @@ -2080,6 +2086,11 @@ static u8 iwl_mvm_scan_umac_flags2(struct iwl_mvm *mvm,  				IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB;  	} +	if (params->scan_6ghz && +	    fw_has_capa(&mvm->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_SCAN_DONT_TOGGLE_ANT)) +		flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT; +  	return flags;  } @@ -2291,7 +2302,7 @@ iwl_mvm_scan_umac_fill_general_p_v11(struct iwl_mvm *mvm,  	iwl_mvm_scan_umac_dwell_v11(mvm, gp, params); -	IWL_DEBUG_SCAN(mvm, "Gerenal: flags=0x%x, flags2=0x%x\n", +	IWL_DEBUG_SCAN(mvm, "General: flags=0x%x, flags2=0x%x\n",  		       gen_flags, gen_flags2);  	gp->flags = cpu_to_le16(gen_flags); @@ -2616,6 +2627,89 @@ static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = {  	IWL_SCAN_UMAC_HANDLER(12),  }; +static void iwl_mvm_mei_scan_work(struct work_struct *wk) +{ +	struct iwl_mei_scan_filter *scan_filter = +		container_of(wk, struct iwl_mei_scan_filter, scan_work); +	struct iwl_mvm *mvm = +		container_of(scan_filter, struct iwl_mvm, mei_scan_filter); +	struct iwl_mvm_csme_conn_info *info; +	struct sk_buff *skb; +	u8 bssid[ETH_ALEN]; + +	mutex_lock(&mvm->mutex); +	info = iwl_mvm_get_csme_conn_info(mvm); +	memcpy(bssid, info->conn_info.bssid, ETH_ALEN); +	mutex_unlock(&mvm->mutex); + +	while ((skb = skb_dequeue(&scan_filter->scan_res))) { +		struct ieee80211_mgmt *mgmt = (void *)skb->data; + +		if (!memcmp(mgmt->bssid, bssid, ETH_ALEN)) +			ieee80211_rx_irqsafe(mvm->hw, skb); +		else +			kfree_skb(skb); +	} +} + +void iwl_mvm_mei_scan_filter_init(struct iwl_mei_scan_filter *mei_scan_filter) +{ +	skb_queue_head_init(&mei_scan_filter->scan_res); +	INIT_WORK(&mei_scan_filter->scan_work, iwl_mvm_mei_scan_work); +} + +/* In case CSME is connected and has link protection set, this function will + * override the scan request to scan only the associated channel and only for + * the associated SSID. + */ +static void iwl_mvm_mei_limited_scan(struct iwl_mvm *mvm, +				     struct iwl_mvm_scan_params *params) +{ +	struct iwl_mvm_csme_conn_info *info = iwl_mvm_get_csme_conn_info(mvm); +	struct iwl_mei_conn_info *conn_info; +	struct ieee80211_channel *chan; +	int scan_iters, i; + +	if (!info) { +		IWL_DEBUG_SCAN(mvm, "mei_limited_scan: no connection info\n"); +		return; +	} + +	conn_info = &info->conn_info; +	if (!info->conn_info.lp_state || !info->conn_info.ssid_len) +		return; + +	if (!params->n_channels || !params->n_ssids) +		return; + +	mvm->mei_scan_filter.is_mei_limited_scan = true; + +	chan = ieee80211_get_channel(mvm->hw->wiphy, +				     ieee80211_channel_to_frequency(conn_info->channel, +								    conn_info->band)); +	if (!chan) { +		IWL_DEBUG_SCAN(mvm, +			       "Failed to get CSME channel (chan=%u band=%u)\n", +			       conn_info->channel, conn_info->band); +		return; +	} + +	/* The mei filtered scan must find the AP, otherwise CSME will +	 * take the NIC ownership. Add several iterations on the channel to +	 * make the scan more robust. +	 */ +	scan_iters = min(IWL_MEI_SCAN_NUM_ITER, params->n_channels); +	params->n_channels = scan_iters; +	for (i = 0; i < scan_iters; i++) +		params->channels[i] = chan; + +	IWL_DEBUG_SCAN(mvm, "Mei scan: num iterations=%u\n", scan_iters); + +	params->n_ssids = 1; +	params->ssids[0].ssid_len = conn_info->ssid_len; +	memcpy(params->ssids[0].ssid, conn_info->ssid, conn_info->ssid_len); +} +  static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm,  				  struct ieee80211_vif *vif,  				  struct iwl_host_cmd *hcmd, @@ -2628,6 +2722,8 @@ static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex);  	memset(mvm->scan_cmd, 0, mvm->scan_cmd_size); +	iwl_mvm_mei_limited_scan(mvm, params); +  	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {  		hcmd->id = SCAN_OFFLOAD_REQUEST_CMD; @@ -2676,11 +2772,23 @@ static void iwl_mvm_scan_respect_p2p_go_iter(void *_data, u8 *mac,  	if (vif == data->current_vif)  		return; -	if (vif->type == NL80211_IFTYPE_AP && vif->p2p && -	    mvmvif->phy_ctxt->id < NUM_PHY_CTX && -	    (data->band == NUM_NL80211_BANDS || -	     mvmvif->phy_ctxt->channel->band == data->band)) -		data->p2p_go = true; +	if (vif->type == NL80211_IFTYPE_AP && vif->p2p) { +		u32 link_id; + +		for (link_id = 0; +		     link_id < ARRAY_SIZE(mvmvif->link); +		     link_id++) { +			struct iwl_mvm_vif_link_info *link = +				mvmvif->link[link_id]; + +			if (link && link->phy_ctxt->id < NUM_PHY_CTX && +			    (data->band == NUM_NL80211_BANDS || +			     link->phy_ctxt->channel->band == data->band)) { +				data->p2p_go = true; +				break; +			} +		} +	}  }  static bool _iwl_mvm_get_respect_p2p_go(struct iwl_mvm *mvm, @@ -2782,6 +2890,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	params.pass_all = true;  	params.n_match_sets = 0;  	params.match_sets = NULL; +	ether_addr_copy(params.bssid, req->bssid);  	params.scan_plans = &scan_plan;  	params.n_scan_plans = 1; @@ -2875,6 +2984,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,  	params.pass_all =  iwl_mvm_scan_pass_all(mvm, req);  	params.n_match_sets = req->n_match_sets;  	params.match_sets = req->match_sets; +	eth_broadcast_addr(params.bssid);  	if (!req->n_scan_plans)  		return -EINVAL; @@ -2970,6 +3080,8 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,  	u32 uid = __le32_to_cpu(notif->uid);  	bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); +	mvm->mei_scan_filter.is_mei_limited_scan = false; +  	if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status)))  		return; @@ -2980,7 +3092,7 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,  			.scan_start_tsf = mvm->scan_start,  		}; -		memcpy(info.tsf_bssid, mvm->scan_vif->bssid, ETH_ALEN); +		memcpy(info.tsf_bssid, mvm->scan_vif->deflink.bssid, ETH_ALEN);  		ieee80211_scan_completed(mvm->hw, &info);  		mvm->scan_vif = NULL;  		cancel_delayed_work(&mvm->scan_timeout_dwork); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index 1f4ac1e93cee..98f330fcf678 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -8,7 +8,7 @@  /* For counting bound interfaces */  struct iwl_mvm_active_iface_iterator_data {  	struct ieee80211_vif *ignore_vif; -	u8 sta_vif_ap_sta_id; +	struct ieee80211_sta *sta_vif_ap_sta;  	enum iwl_sf_state sta_vif_state;  	u32 num_active_macs;  }; @@ -23,14 +23,14 @@ static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,  	struct iwl_mvm_active_iface_iterator_data *data = _data;  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	if (vif == data->ignore_vif || !mvmvif->phy_ctxt || +	if (vif == data->ignore_vif || !mvmvif->deflink.phy_ctxt ||  	    vif->type == NL80211_IFTYPE_P2P_DEVICE)  		return;  	data->num_active_macs++;  	if (vif->type == NL80211_IFTYPE_STATION) { -		data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; +		data->sta_vif_ap_sta = mvmvif->ap_sta;  		if (vif->cfg.assoc)  			data->sta_vif_state = SF_FULL_ON;  		else @@ -98,6 +98,10 @@ static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,  				    struct ieee80211_sta *sta)  {  	int i, j, watermark; +	u8 max_rx_nss = 0; +	bool is_legacy = true; +	struct ieee80211_link_sta *link_sta; +	unsigned int link_id;  	sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); @@ -106,10 +110,25 @@ static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,  	 * capabilities of the AP station, and choose the watermark accordingly.  	 */  	if (sta) { -		if (sta->deflink.ht_cap.ht_supported || -		    sta->deflink.vht_cap.vht_supported || -		    sta->deflink.he_cap.has_he) { -			switch (sta->deflink.rx_nss) { +		/* find the maximal NSS number among all links (if relevant) */ +		rcu_read_lock(); +		for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) { +			link_sta = rcu_dereference(sta->link[link_id]); +			if (!link_sta) +				continue; + +			if (link_sta->ht_cap.ht_supported || +			    link_sta->vht_cap.vht_supported || +			    link_sta->eht_cap.has_eht || +			    link_sta->he_cap.has_he) { +				is_legacy = false; +				max_rx_nss = max(max_rx_nss, link_sta->rx_nss); +			} +		} +		rcu_read_unlock(); + +		if (!is_legacy) { +			switch (max_rx_nss) {  			case 1:  				watermark = SF_W_MARK_SISO;  				break; @@ -151,16 +170,14 @@ static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,  		memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,  		       sizeof(sf_full_timeout_def));  	} -  } -static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, +static int iwl_mvm_sf_config(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			     enum iwl_sf_state new_state)  {  	struct iwl_sf_cfg_cmd sf_cmd = {  		.state = cpu_to_le32(new_state),  	}; -	struct ieee80211_sta *sta;  	int ret = 0;  	if (mvm->cfg->disable_dummy_notification) @@ -178,20 +195,12 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,  		iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);  		break;  	case SF_FULL_ON: -		if (sta_id == IWL_MVM_INVALID_STA) { +		if (!sta) {  			IWL_ERR(mvm,  				"No station: Cannot switch SF to FULL_ON\n");  			return -EINVAL;  		} -		rcu_read_lock(); -		sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); -		if (IS_ERR_OR_NULL(sta)) { -			IWL_ERR(mvm, "Invalid station id\n"); -			rcu_read_unlock(); -			return -EINVAL; -		}  		iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta); -		rcu_read_unlock();  		break;  	case SF_INIT_OFF:  		iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); @@ -219,13 +228,12 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,  		      bool remove_vif)  {  	enum iwl_sf_state new_state; -	u8 sta_id = IWL_MVM_INVALID_STA;  	struct iwl_mvm_vif *mvmvif = NULL;  	struct iwl_mvm_active_iface_iterator_data data = {  		.ignore_vif = changed_vif,  		.sta_vif_state = SF_UNINIT, -		.sta_vif_ap_sta_id = IWL_MVM_INVALID_STA,  	}; +	struct ieee80211_sta *sta = NULL;  	/*  	 * Ignore the call if we are in HW Restart flow, or if the handled @@ -255,7 +263,7 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,  			 * and we filled the relevant data during iteration  			 */  			new_state = data.sta_vif_state; -			sta_id = data.sta_vif_ap_sta_id; +			sta = data.sta_vif_ap_sta;  		} else {  			if (WARN_ON(!changed_vif))  				return -EINVAL; @@ -264,7 +272,7 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,  			} else if (changed_vif->cfg.assoc &&  				   changed_vif->bss_conf.dtim_period) {  				mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); -				sta_id = mvmvif->ap_sta_id; +				sta = mvmvif->ap_sta;  				new_state = SF_FULL_ON;  			} else {  				new_state = SF_INIT_OFF; @@ -275,5 +283,6 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,  		/* If there are multiple active macs - change to SF_UNINIT */  		new_state = SF_UNINIT;  	} -	return iwl_mvm_sf_config(mvm, sta_id, new_state); + +	return iwl_mvm_sf_config(mvm, sta, new_state);  } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 69634fb82a9b..05a54a69c135 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -24,8 +24,7 @@ static inline int iwl_mvm_add_sta_cmd_size(struct iwl_mvm *mvm)  		return sizeof(struct iwl_mvm_add_sta_cmd_v7);  } -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, -				    enum nl80211_iftype iftype) +int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, enum nl80211_iftype iftype)  {  	int sta_id;  	u32 reserved_ids = 0; @@ -51,13 +50,79 @@ static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,  	return IWL_MVM_INVALID_STA;  } +/* Calculate the ampdu density and max size */ +u32 iwl_mvm_get_sta_ampdu_dens(struct ieee80211_link_sta *link_sta, +			       struct ieee80211_bss_conf *link_conf, +			       u32 *_agg_size) +{ +	u32 agg_size = 0, mpdu_dens = 0; + +	if (WARN_ON(!link_sta)) +		return 0; + +	if (link_sta->ht_cap.ht_supported) +		mpdu_dens = link_sta->ht_cap.ampdu_density; + +	if (link_conf->chandef.chan->band == +	    NL80211_BAND_6GHZ) { +		mpdu_dens = le16_get_bits(link_sta->he_6ghz_capa.capa, +					  IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); +		agg_size = le16_get_bits(link_sta->he_6ghz_capa.capa, +					 IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); +	} else if (link_sta->vht_cap.vht_supported) { +		agg_size = link_sta->vht_cap.cap & +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +		agg_size >>= +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +	} else if (link_sta->ht_cap.ht_supported) { +		agg_size = link_sta->ht_cap.ampdu_factor; +	} + +	/* D6.0 10.12.2 A-MPDU length limit rules +	 * A STA indicates the maximum length of the A-MPDU preEOF padding +	 * that it can receive in an HE PPDU in the Maximum A-MPDU Length +	 * Exponent field in its HT Capabilities, VHT Capabilities, +	 * and HE 6 GHz Band Capabilities elements (if present) and the +	 * Maximum AMPDU Length Exponent Extension field in its HE +	 * Capabilities element +	 */ +	if (link_sta->he_cap.has_he) +		agg_size += +			u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3], +				    IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); + +	/* Limit to max A-MPDU supported by FW */ +	if (agg_size > (STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT)) +		agg_size = (STA_FLG_MAX_AGG_SIZE_4M >> +			    STA_FLG_MAX_AGG_SIZE_SHIFT); + +	*_agg_size = agg_size; +	return mpdu_dens; +} + +u8 iwl_mvm_get_sta_uapsd_acs(struct ieee80211_sta *sta) +{ +	u8 uapsd_acs = 0; + +	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) +		uapsd_acs |= BIT(AC_BK); +	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) +		uapsd_acs |= BIT(AC_BE); +	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) +		uapsd_acs |= BIT(AC_VI); +	if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) +		uapsd_acs |= BIT(AC_VO); + +	return uapsd_acs | uapsd_acs << 4; +} +  /* send station add/update command to firmware */  int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			   bool update, unsigned int flags)  {  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd add_sta_cmd = { -		.sta_id = mvm_sta->sta_id, +		.sta_id = mvm_sta->deflink.sta_id,  		.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),  		.add_modify = update ? 1 : 0,  		.station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | @@ -134,68 +199,26 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		break;  	} -	if (sta->deflink.ht_cap.ht_supported) { -		add_sta_cmd.station_flags_msk |= -			cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | -				    STA_FLG_AGG_MPDU_DENS_MSK); - -		mpdu_dens = sta->deflink.ht_cap.ampdu_density; -	} - -	if (mvm_sta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) { +	if (sta->deflink.ht_cap.ht_supported || +	    mvm_sta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ)  		add_sta_cmd.station_flags_msk |=  			cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK |  				    STA_FLG_AGG_MPDU_DENS_MSK); -		mpdu_dens = le16_get_bits(sta->deflink.he_6ghz_capa.capa, -					  IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); -		agg_size = le16_get_bits(sta->deflink.he_6ghz_capa.capa, -					 IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); -	} else if (sta->deflink.vht_cap.vht_supported) { -		agg_size = sta->deflink.vht_cap.cap & -			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; -		agg_size >>= -			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; -	} else if (sta->deflink.ht_cap.ht_supported) { -		agg_size = sta->deflink.ht_cap.ampdu_factor; -	} - -	/* D6.0 10.12.2 A-MPDU length limit rules -	 * A STA indicates the maximum length of the A-MPDU preEOF padding -	 * that it can receive in an HE PPDU in the Maximum A-MPDU Length -	 * Exponent field in its HT Capabilities, VHT Capabilities, -	 * and HE 6 GHz Band Capabilities elements (if present) and the -	 * Maximum AMPDU Length Exponent Extension field in its HE -	 * Capabilities element -	 */ -	if (sta->deflink.he_cap.has_he) -		agg_size += u8_get_bits(sta->deflink.he_cap.he_cap_elem.mac_cap_info[3], -					IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); - -	/* Limit to max A-MPDU supported by FW */ -	if (agg_size > (STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT)) -		agg_size = (STA_FLG_MAX_AGG_SIZE_4M >> -			    STA_FLG_MAX_AGG_SIZE_SHIFT); - +	mpdu_dens = iwl_mvm_get_sta_ampdu_dens(&sta->deflink, +					       &mvm_sta->vif->bss_conf, +					       &agg_size);  	add_sta_cmd.station_flags |=  		cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);  	add_sta_cmd.station_flags |=  		cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); +  	if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC)  		add_sta_cmd.assoc_id = cpu_to_le16(sta->aid);  	if (sta->wme) {  		add_sta_cmd.modify_mask |= STA_MODIFY_UAPSD_ACS; - -		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) -			add_sta_cmd.uapsd_acs |= BIT(AC_BK); -		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) -			add_sta_cmd.uapsd_acs |= BIT(AC_BE); -		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) -			add_sta_cmd.uapsd_acs |= BIT(AC_VI); -		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) -			add_sta_cmd.uapsd_acs |= BIT(AC_VO); -		add_sta_cmd.uapsd_acs |= add_sta_cmd.uapsd_acs << 4; +		add_sta_cmd.uapsd_acs = iwl_mvm_get_sta_uapsd_acs(sta);  		add_sta_cmd.sp_length = sta->max_sp ? sta->max_sp * 2 : 128;  	} @@ -228,6 +251,7 @@ static void iwl_mvm_rx_agg_session_expired(struct timer_list *t)  	struct ieee80211_sta *sta;  	struct iwl_mvm_sta *mvm_sta;  	unsigned long timeout; +	unsigned int sta_id;  	rcu_read_lock(); @@ -246,7 +270,8 @@ static void iwl_mvm_rx_agg_session_expired(struct timer_list *t)  	}  	/* Timer expired */ -	sta = rcu_dereference(ba_data->mvm->fw_id_to_mac_id[ba_data->sta_id]); +	sta_id = ffs(ba_data->sta_mask) - 1; /* don't care which one */ +	sta = rcu_dereference(ba_data->mvm->fw_id_to_mac_id[sta_id]);  	/*  	 * sta should be valid unless the following happens: @@ -256,7 +281,7 @@ static void iwl_mvm_rx_agg_session_expired(struct timer_list *t)  	 * A-MDPU and hence the timer continues to run. Then, the  	 * timer expires and sta is NULL.  	 */ -	if (!sta) +	if (IS_ERR_OR_NULL(sta))  		goto unlock;  	mvm_sta = iwl_mvm_sta_from_mac80211(sta); @@ -296,7 +321,7 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue,  	mvmsta->tid_disable_agg |= disable_agg_tids;  	cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); -	cmd.sta_id = mvmsta->sta_id; +	cmd.sta_id = mvmsta->deflink.sta_id;  	cmd.add_modify = STA_MODE_MODIFY;  	cmd.modify_mask = STA_MODIFY_QUEUES;  	if (disable_agg_tids) @@ -333,10 +358,14 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  					     SCD_QUEUE_CONFIG_CMD);  			struct iwl_scd_queue_cfg_cmd remove_cmd = {  				.operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), -				.u.remove.tid = cpu_to_le32(tid),  				.u.remove.sta_mask = cpu_to_le32(BIT(sta_id)),  			}; +			if (tid == IWL_MAX_TID_COUNT) +				tid = IWL_MGMT_TID; + +			remove_cmd.u.remove.tid = cpu_to_le32(tid); +  			ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0,  						   sizeof(remove_cmd),  						   &remove_cmd); @@ -384,8 +413,11 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		struct iwl_mvm_txq *mvmtxq =  			iwl_mvm_txq_from_tid(sta, tid); -		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; +		spin_lock_bh(&mvm->add_stream_lock);  		list_del_init(&mvmtxq->list); +		clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state); +		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; +		spin_unlock_bh(&mvm->add_stream_lock);  	}  	/* Regardless if this is a reserved TXQ for a STA - mark it as false */ @@ -479,8 +511,11 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)  			disable_agg_tids |= BIT(tid);  		mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; -		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; +		spin_lock_bh(&mvm->add_stream_lock);  		list_del_init(&mvmtxq->list); +		clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state); +		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; +		spin_unlock_bh(&mvm->add_stream_lock);  	}  	mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ @@ -693,7 +728,7 @@ static int iwl_mvm_redirect_queue(struct iwl_mvm *mvm, int queue, int tid,  			    queue, iwl_mvm_ac_to_tx_fifo[ac]);  	/* Stop the queue and wait for it to empty */ -	txq->stopped = true; +	set_bit(IWL_MVM_TXQ_STATE_STOP_REDIRECT, &txq->state);  	ret = iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(queue));  	if (ret) { @@ -736,7 +771,7 @@ static int iwl_mvm_redirect_queue(struct iwl_mvm *mvm, int queue, int tid,  out:  	/* Continue using the queue */ -	txq->stopped = false; +	clear_bit(IWL_MVM_TXQ_STATE_STOP_REDIRECT, &txq->state);  	return ret;  } @@ -766,32 +801,52 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,  	return -ENOSPC;  } -static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, -				   u8 sta_id, u8 tid, unsigned int timeout) +static int iwl_mvm_get_queue_size(struct ieee80211_sta *sta) +{ +	int max_size = IWL_DEFAULT_QUEUE_SIZE; +	unsigned int link_id; + +	/* this queue isn't used for traffic (cab_queue) */ +	if (!sta) +		return IWL_MGMT_QUEUE_SIZE; + +	rcu_read_lock(); + +	for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) { +		struct ieee80211_link_sta *link = +			rcu_dereference(sta->link[link_id]); + +		if (!link) +			continue; + +		/* support for 1k ba size */ +		if (link->eht_cap.has_eht && +		    max_size < IWL_DEFAULT_QUEUE_SIZE_EHT) +			max_size = IWL_DEFAULT_QUEUE_SIZE_EHT; + +		/* support for 256 ba size */ +		if (link->he_cap.has_he && +		    max_size < IWL_DEFAULT_QUEUE_SIZE_HE) +			max_size = IWL_DEFAULT_QUEUE_SIZE_HE; +	} + +	rcu_read_unlock(); +	return max_size; +} + +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, +			    struct ieee80211_sta *sta, +			    u8 sta_id, u8 tid, unsigned int timeout)  {  	int queue, size; +	u32 sta_mask = 0;  	if (tid == IWL_MAX_TID_COUNT) {  		tid = IWL_MGMT_TID;  		size = max_t(u32, IWL_MGMT_QUEUE_SIZE,  			     mvm->trans->cfg->min_txq_size);  	} else { -		struct ieee80211_sta *sta; - -		rcu_read_lock(); -		sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - -		/* this queue isn't used for traffic (cab_queue) */ -		if (IS_ERR_OR_NULL(sta)) { -			size = IWL_MGMT_QUEUE_SIZE; -		} else if (sta->deflink.he_cap.has_he) { -			/* support for 256 ba size */ -			size = IWL_DEFAULT_QUEUE_SIZE_HE; -		} else { -			size = IWL_DEFAULT_QUEUE_SIZE; -		} - -		rcu_read_unlock(); +		size = iwl_mvm_get_queue_size(sta);  	}  	/* take the min with bc tbl entries allowed */ @@ -800,22 +855,45 @@ static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm,  	/* size needs to be power of 2 values for calculating read/write pointers */  	size = rounddown_pow_of_two(size); +	if (sta) { +		struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); +		unsigned int link_id; + +		for (link_id = 0; +		     link_id < ARRAY_SIZE(mvmsta->link); +		     link_id++) { +			struct iwl_mvm_link_sta *link = +				rcu_dereference_protected(mvmsta->link[link_id], +							  lockdep_is_held(&mvm->mutex)); + +			if (!link) +				continue; + +			sta_mask |= BIT(link->sta_id); +		} +	} else { +		sta_mask |= BIT(sta_id); +	} + +	if (!sta_mask) +		return -EINVAL; +  	do { -		queue = iwl_trans_txq_alloc(mvm->trans, 0, BIT(sta_id), +		queue = iwl_trans_txq_alloc(mvm->trans, 0, sta_mask,  					    tid, size, timeout);  		if (queue < 0)  			IWL_DEBUG_TX_QUEUES(mvm, -					    "Failed allocating TXQ of size %d for sta %d tid %d, ret: %d\n", -					    size, sta_id, tid, queue); +					    "Failed allocating TXQ of size %d for sta mask %x tid %d, ret: %d\n", +					    size, sta_mask, tid, queue);  		size /= 2;  	} while (queue < 0 && size >= 16);  	if (queue < 0)  		return queue; -	IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta %d tid %d\n", -			    queue, sta_id, tid); +	IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta mask 0x%x tid %d\n", +			    queue, sta_mask, tid);  	return queue;  } @@ -835,14 +913,15 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm,  	IWL_DEBUG_TX_QUEUES(mvm,  			    "Allocating queue for sta %d on tid %d\n", -			    mvmsta->sta_id, tid); -	queue = iwl_mvm_tvqm_enable_txq(mvm, mvmsta->sta_id, tid, wdg_timeout); +			    mvmsta->deflink.sta_id, tid); +	queue = iwl_mvm_tvqm_enable_txq(mvm, sta, mvmsta->deflink.sta_id, +					tid, wdg_timeout);  	if (queue < 0)  		return queue;  	mvmtxq->txq_id = queue;  	mvm->tvqm_info[queue].txq_tid = tid; -	mvm->tvqm_info[queue].sta_id = mvmsta->sta_id; +	mvm->tvqm_info[queue].sta_id = mvmsta->deflink.sta_id;  	IWL_DEBUG_TX_QUEUES(mvm, "Allocated queue is %d\n", queue); @@ -1027,7 +1106,7 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)  		mvmsta->tid_disable_agg &= ~BIT(tid);  		cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); -		cmd.sta_id = mvmsta->sta_id; +		cmd.sta_id = mvmsta->deflink.sta_id;  		cmd.add_modify = STA_MODE_MODIFY;  		cmd.modify_mask = STA_MODIFY_TID_DISABLE_TX;  		cmd.tfd_queue_msk = cpu_to_le32(mvmsta->tfd_queue_msk); @@ -1252,7 +1331,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_trans_txq_scd_cfg cfg = {  		.fifo = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac), -		.sta_id = mvmsta->sta_id, +		.sta_id = mvmsta->deflink.sta_id,  		.tid = tid,  		.frame_limit = IWL_FRAME_LIMIT,  	}; @@ -1278,7 +1357,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,  	spin_unlock_bh(&mvmsta->lock);  	if (tid == IWL_MAX_TID_COUNT) { -		queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, +		queue = iwl_mvm_find_free_queue(mvm, mvmsta->deflink.sta_id,  						IWL_MVM_DQA_MIN_MGMT_QUEUE,  						IWL_MVM_DQA_MAX_MGMT_QUEUE);  		if (queue >= IWL_MVM_DQA_MIN_MGMT_QUEUE) @@ -1297,12 +1376,12 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,  	}  	if (queue < 0) -		queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, +		queue = iwl_mvm_find_free_queue(mvm, mvmsta->deflink.sta_id,  						IWL_MVM_DQA_MIN_DATA_QUEUE,  						IWL_MVM_DQA_MAX_DATA_QUEUE);  	if (queue < 0) {  		/* try harder - perhaps kill an inactive queue */ -		queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id); +		queue = iwl_mvm_inactivity_check(mvm, mvmsta->deflink.sta_id);  	}  	/* No free queue - we'll have to share */ @@ -1342,7 +1421,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,  	IWL_DEBUG_TX_QUEUES(mvm,  			    "Allocating %squeue #%d to sta %d on tid %d\n",  			    shared_queue ? "shared " : "", queue, -			    mvmsta->sta_id, tid); +			    mvmsta->deflink.sta_id, tid);  	if (shared_queue) {  		/* Disable any open aggs on this queue */ @@ -1409,7 +1488,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,  out_err:  	queue_tmp = queue; -	iwl_mvm_disable_txq(mvm, sta, mvmsta->sta_id, &queue_tmp, tid); +	iwl_mvm_disable_txq(mvm, sta, mvmsta->deflink.sta_id, &queue_tmp, tid);  	return ret;  } @@ -1444,12 +1523,22 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk)  		 * a queue in the function itself.  		 */  		if (iwl_mvm_sta_alloc_queue(mvm, txq->sta, txq->ac, tid)) { +			spin_lock_bh(&mvm->add_stream_lock);  			list_del_init(&mvmtxq->list); +			spin_unlock_bh(&mvm->add_stream_lock);  			continue;  		} -		list_del_init(&mvmtxq->list); +		/* now we're ready, any remaining races/concurrency will be +		 * handled in iwl_mvm_mac_itxq_xmit() +		 */ +		set_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state); +  		local_bh_disable(); +		spin_lock(&mvm->add_stream_lock); +		list_del_init(&mvmtxq->list); +		spin_unlock(&mvm->add_stream_lock); +  		iwl_mvm_mac_itxq_xmit(mvm->hw, txq);  		local_bh_enable();  	} @@ -1478,12 +1567,12 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,  	     IWL_MVM_QUEUE_FREE))  		queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE;  	else -		queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, +		queue = iwl_mvm_find_free_queue(mvm, mvmsta->deflink.sta_id,  						IWL_MVM_DQA_MIN_DATA_QUEUE,  						IWL_MVM_DQA_MAX_DATA_QUEUE);  	if (queue < 0) {  		/* try again - this time kick out a queue if needed */ -		queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id); +		queue = iwl_mvm_inactivity_check(mvm, mvmsta->deflink.sta_id);  		if (queue < 0) {  			IWL_ERR(mvm, "No available queues for new station\n");  			return -ENOSPC; @@ -1494,7 +1583,7 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,  	mvmsta->reserved_queue = queue;  	IWL_DEBUG_TX_QUEUES(mvm, "Reserving data queue #%d for sta_id %d\n", -			    queue, mvmsta->sta_id); +			    queue, mvmsta->deflink.sta_id);  	return 0;  } @@ -1506,15 +1595,15 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,   *   * Note that re-enabling aggregations isn't done in this function.   */ -static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, -						 struct ieee80211_sta *sta) +void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, +					  struct ieee80211_sta *sta)  {  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);  	unsigned int wdg =  		iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif, false, false);  	int i;  	struct iwl_trans_txq_scd_cfg cfg = { -		.sta_id = mvm_sta->sta_id, +		.sta_id = mvm_sta->deflink.sta_id,  		.frame_limit = IWL_FRAME_LIMIT,  	}; @@ -1536,8 +1625,9 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm,  		if (iwl_mvm_has_new_tx_api(mvm)) {  			IWL_DEBUG_TX_QUEUES(mvm,  					    "Re-mapping sta %d tid %d\n", -					    mvm_sta->sta_id, i); -			txq_id = iwl_mvm_tvqm_enable_txq(mvm, mvm_sta->sta_id, +					    mvm_sta->deflink.sta_id, i); +			txq_id = iwl_mvm_tvqm_enable_txq(mvm, sta, +							 mvm_sta->deflink.sta_id,  							 i, wdg);  			/*  			 * on failures, just set it to IWL_MVM_INVALID_QUEUE @@ -1566,7 +1656,8 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm,  			IWL_DEBUG_TX_QUEUES(mvm,  					    "Re-mapping sta %d tid %d to queue %d\n", -					    mvm_sta->sta_id, i, txq_id); +					    mvm_sta->deflink.sta_id, i, +					    txq_id);  			iwl_mvm_enable_txq(mvm, sta, txq_id, seq, &cfg, wdg);  			mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; @@ -1624,74 +1715,45 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,  	return ret;  } -int iwl_mvm_add_sta(struct iwl_mvm *mvm, -		    struct ieee80211_vif *vif, -		    struct ieee80211_sta *sta) +/* Initialize driver data of a new sta */ +int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_sta *sta, int sta_id, u8 sta_type)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_rxq_dup_data *dup_data; -	int i, ret, sta_id; -	bool sta_update = false; -	unsigned int sta_flags = 0; +	int i, ret = 0;  	lockdep_assert_held(&mvm->mutex); -	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) -		sta_id = iwl_mvm_find_free_sta_id(mvm, -						  ieee80211_vif_type_p2p(vif)); -	else -		sta_id = mvm_sta->sta_id; - -	if (sta_id == IWL_MVM_INVALID_STA) -		return -ENOSPC; - -	spin_lock_init(&mvm_sta->lock); +	mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, +						      mvmvif->color); +	mvm_sta->vif = vif; -	/* if this is a HW restart re-alloc existing queues */ -	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { -		struct iwl_mvm_int_sta tmp_sta = { -			.sta_id = sta_id, -			.type = mvm_sta->sta_type, -		}; +	/* for MLD sta_id(s) should be allocated for each link before calling +	 * this function +	 */ +	if (!mvm->mld_api_is_used) { +		if (WARN_ON(sta_id == IWL_MVM_INVALID_STA)) +			return -EINVAL; -		/* -		 * First add an empty station since allocating -		 * a queue requires a valid station -		 */ -		ret = iwl_mvm_add_int_sta_common(mvm, &tmp_sta, sta->addr, -						 mvmvif->id, mvmvif->color); -		if (ret) -			goto err; +		mvm_sta->deflink.sta_id = sta_id; +		rcu_assign_pointer(mvm_sta->link[0], &mvm_sta->deflink); -		iwl_mvm_realloc_queues_after_restart(mvm, sta); -		sta_update = true; -		sta_flags = iwl_mvm_has_new_tx_api(mvm) ? 0 : STA_MODIFY_QUEUES; -		goto update_fw; +		if (!mvm->trans->trans_cfg->gen2) +			mvm_sta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize = +				LINK_QUAL_AGG_FRAME_LIMIT_DEF; +		else +			mvm_sta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize = +				LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF;  	} -	mvm_sta->sta_id = sta_id; -	mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, -						      mvmvif->color); -	mvm_sta->vif = vif; -	if (!mvm->trans->trans_cfg->gen2) -		mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; -	else -		mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF; -	mvm_sta->tx_protection = 0;  	mvm_sta->tt_tx_protection = false; -	mvm_sta->sta_type = sta->tdls ? IWL_STA_TDLS_LINK : IWL_STA_LINK; +	mvm_sta->sta_type = sta_type; -	/* HW restart, don't assume the memory has been zeroed */  	mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ -	mvm_sta->tfd_queue_msk = 0; -	/* for HW restart - reset everything but the sequence number */  	for (i = 0; i <= IWL_MAX_TID_COUNT; i++) { -		u16 seq = mvm_sta->tid_data[i].seq_number; -		memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); -		mvm_sta->tid_data[i].seq_number = seq; -  		/*  		 * Mark all queues for this STA as unallocated and defer TX  		 * frames until the queue is allocated @@ -1708,10 +1770,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,  		atomic_set(&mvmtxq->tx_request, 0);  	} -	mvm_sta->agg_tids = 0; - -	if (iwl_mvm_has_new_rx_api(mvm) && -	    !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { +	if (iwl_mvm_has_new_rx_api(mvm)) {  		int q;  		dup_data = kcalloc(mvm->trans->num_rx_queues, @@ -1737,7 +1796,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,  		ret = iwl_mvm_reserve_sta_stream(mvm, sta,  						 ieee80211_vif_type_p2p(vif));  		if (ret) -			goto err; +			return ret;  	}  	/* @@ -1747,10 +1806,60 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,  	if (iwl_mvm_has_tlc_offload(mvm))  		iwl_mvm_rs_add_sta(mvm, mvm_sta);  	else -		spin_lock_init(&mvm_sta->lq_sta.rs_drv.pers.lock); +		spin_lock_init(&mvm_sta->deflink.lq_sta.rs_drv.pers.lock);  	iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant); +	return 0; +} + +int iwl_mvm_add_sta(struct iwl_mvm *mvm, +		    struct ieee80211_vif *vif, +		    struct ieee80211_sta *sta) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	int ret, sta_id; +	bool sta_update = false; +	unsigned int sta_flags = 0; + +	lockdep_assert_held(&mvm->mutex); + +	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) +		sta_id = iwl_mvm_find_free_sta_id(mvm, +						  ieee80211_vif_type_p2p(vif)); +	else +		sta_id = mvm_sta->deflink.sta_id; + +	if (sta_id == IWL_MVM_INVALID_STA) +		return -ENOSPC; + +	spin_lock_init(&mvm_sta->lock); + +	/* if this is a HW restart re-alloc existing queues */ +	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { +		struct iwl_mvm_int_sta tmp_sta = { +			.sta_id = sta_id, +			.type = mvm_sta->sta_type, +		}; + +		/* First add an empty station since allocating +		 * a queue requires a valid station +		 */ +		ret = iwl_mvm_add_int_sta_common(mvm, &tmp_sta, sta->addr, +						 mvmvif->id, mvmvif->color); +		if (ret) +			goto err; + +		iwl_mvm_realloc_queues_after_restart(mvm, sta); +		sta_update = true; +		sta_flags = iwl_mvm_has_new_tx_api(mvm) ? 0 : STA_MODIFY_QUEUES; +		goto update_fw; +	} + +	ret = iwl_mvm_sta_init(mvm, vif, sta, sta_id, +			       sta->tdls ? IWL_STA_TDLS_LINK : IWL_STA_LINK); +  update_fw:  	ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags);  	if (ret) @@ -1758,10 +1867,10 @@ update_fw:  	if (vif->type == NL80211_IFTYPE_STATION) {  		if (!sta->tdls) { -			WARN_ON(mvmvif->ap_sta_id != IWL_MVM_INVALID_STA); -			mvmvif->ap_sta_id = sta_id; +			WARN_ON(mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA); +			mvmvif->deflink.ap_sta_id = sta_id;  		} else { -			WARN_ON(mvmvif->ap_sta_id == IWL_MVM_INVALID_STA); +			WARN_ON(mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA);  		}  	} @@ -1783,7 +1892,7 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  	lockdep_assert_held(&mvm->mutex);  	cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); -	cmd.sta_id = mvmsta->sta_id; +	cmd.sta_id = mvmsta->deflink.sta_id;  	cmd.add_modify = STA_MODE_MODIFY;  	cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0;  	cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); @@ -1798,12 +1907,12 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,  	switch (status & IWL_ADD_STA_STATUS_MASK) {  	case ADD_STA_SUCCESS:  		IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", -			       mvmsta->sta_id); +			       mvmsta->deflink.sta_id);  		break;  	default:  		ret = -EIO;  		IWL_ERR(mvm, "Couldn't drain frames for staid %d\n", -			mvmsta->sta_id); +			mvmsta->deflink.sta_id);  		break;  	} @@ -1855,7 +1964,7 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm,  		if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE)  			continue; -		iwl_mvm_disable_txq(mvm, sta, mvm_sta->sta_id, +		iwl_mvm_disable_txq(mvm, sta, mvm_sta->deflink.sta_id,  				    &mvm_sta->tid_data[i].txq_id, i);  		mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE;  	} @@ -1864,8 +1973,11 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm,  		struct iwl_mvm_txq *mvmtxq =  			iwl_mvm_txq_from_mac80211(sta->txq[i]); +		spin_lock_bh(&mvm->add_stream_lock);  		mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;  		list_del_init(&mvmtxq->list); +		clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state); +		spin_unlock_bh(&mvm->add_stream_lock);  	}  } @@ -1893,42 +2005,27 @@ int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm,  	return 0;  } -int iwl_mvm_rm_sta(struct iwl_mvm *mvm, -		   struct ieee80211_vif *vif, -		   struct ieee80211_sta *sta) +/* Execute the common part for both MLD and non-MLD modes. + * Returns if we're done with removing the station, either + * with error or success + */ +bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_sta *sta, +		     struct ieee80211_link_sta *link_sta, int *ret)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	struct iwl_mvm_vif_link_info *mvm_link = +		mvmvif->link[link_sta->link_id];  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); -	u8 sta_id = mvm_sta->sta_id; -	int ret; +	struct iwl_mvm_link_sta *mvm_link_sta; +	u8 sta_id;  	lockdep_assert_held(&mvm->mutex); -	if (iwl_mvm_has_new_rx_api(mvm)) -		kfree(mvm_sta->dup_data); - -	ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); -	if (ret) -		return ret; - -	/* flush its queues here since we are freeing mvm_sta */ -	ret = iwl_mvm_flush_sta(mvm, mvm_sta, false); -	if (ret) -		return ret; -	if (iwl_mvm_has_new_tx_api(mvm)) { -		ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); -	} else { -		u32 q_mask = mvm_sta->tfd_queue_msk; - -		ret = iwl_trans_wait_tx_queues_empty(mvm->trans, -						     q_mask); -	} -	if (ret) -		return ret; - -	ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); - -	iwl_mvm_disable_sta_queues(mvm, vif, sta); +	mvm_link_sta = +		rcu_dereference_protected(mvm_sta->link[link_sta->link_id], +					  lockdep_is_held(&mvm->mutex)); +	sta_id = mvm_link_sta->sta_id;  	/* If there is a TXQ still marked as reserved - free it */  	if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { @@ -1944,23 +2041,24 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,  		if (WARN((*status != IWL_MVM_QUEUE_RESERVED) &&  			 (*status != IWL_MVM_QUEUE_FREE),  			 "sta_id %d reserved txq %d status %d", -			 sta_id, reserved_txq, *status)) -			return -EINVAL; +			 sta_id, reserved_txq, *status)) { +			*ret = -EINVAL; +			return true; +		}  		*status = IWL_MVM_QUEUE_FREE;  	} -	if (vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id == sta_id) { +	if (vif->type == NL80211_IFTYPE_STATION) {  		/* if associated - we can't remove the AP STA now */  		if (vif->cfg.assoc) -			return ret; +			return true;  		/* first remove remaining keys */ -		iwl_mvm_sec_key_remove_ap(mvm, vif); +		iwl_mvm_sec_key_remove_ap(mvm, vif, mvm_link, 0);  		/* unassoc - go ahead - remove the AP STA now */ -		mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; +		mvm_link->ap_sta_id = IWL_MVM_INVALID_STA;  	}  	/* @@ -1979,8 +2077,46 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,  	spin_lock_bh(&mvm_sta->lock);  	spin_unlock_bh(&mvm_sta->lock); -	ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); -	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); +	return false; +} + +int iwl_mvm_rm_sta(struct iwl_mvm *mvm, +		   struct ieee80211_vif *vif, +		   struct ieee80211_sta *sta) +{ +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	int ret; + +	lockdep_assert_held(&mvm->mutex); + +	ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); +	if (ret) +		return ret; + +	/* flush its queues here since we are freeing mvm_sta */ +	ret = iwl_mvm_flush_sta(mvm, mvm_sta, false); +	if (ret) +		return ret; +	if (iwl_mvm_has_new_tx_api(mvm)) { +		ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); +	} else { +		u32 q_mask = mvm_sta->tfd_queue_msk; + +		ret = iwl_trans_wait_tx_queues_empty(mvm->trans, +						     q_mask); +	} +	if (ret) +		return ret; + +	ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); + +	iwl_mvm_disable_sta_queues(mvm, vif, sta); + +	if (iwl_mvm_sta_del(mvm, vif, sta, &sta->deflink, &ret)) +		return ret; + +	ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->deflink.sta_id); +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->deflink.sta_id], NULL);  	return ret;  } @@ -2000,7 +2136,7 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,  int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,  			     struct iwl_mvm_int_sta *sta,  			     u32 qmask, enum nl80211_iftype iftype, -			     enum iwl_sta_type type) +			     u8 type)  {  	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||  	    sta->sta_id == IWL_MVM_INVALID_STA) { @@ -2013,7 +2149,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,  	sta->type = type;  	/* put a non-NULL value so iterating over the stations won't stop */ -	rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL));  	return 0;  } @@ -2049,7 +2185,7 @@ static int iwl_mvm_enable_aux_snif_queue_tvqm(struct iwl_mvm *mvm, u8 sta_id)  	WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); -	return iwl_mvm_tvqm_enable_txq(mvm, sta_id, IWL_MAX_TID_COUNT, +	return iwl_mvm_tvqm_enable_txq(mvm, NULL, sta_id, IWL_MAX_TID_COUNT,  				       wdg_timeout);  } @@ -2094,11 +2230,13 @@ static int iwl_mvm_add_int_sta_with_queue(struct iwl_mvm *mvm, int macidx,  int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id)  {  	int ret; +	u32 qmask = mvm->aux_queue == IWL_MVM_INVALID_QUEUE ? 0 : +		BIT(mvm->aux_queue);  	lockdep_assert_held(&mvm->mutex);  	/* Allocate aux station and assign to it the aux queue */ -	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), +	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, qmask,  				       NL80211_IFTYPE_UNSPECIFIED,  				       IWL_STA_AUX_ACTIVITY);  	if (ret) @@ -2184,7 +2322,7 @@ void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm)  int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; +	struct iwl_mvm_int_sta *bsta = &mvmvif->deflink.bcast_sta;  	static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};  	const u8 *baddr = _baddr;  	int queue; @@ -2193,7 +2331,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  		iwl_mvm_get_wd_timeout(mvm, vif, false, false);  	struct iwl_trans_txq_scd_cfg cfg = {  		.fifo = IWL_MVM_TX_FIFO_VO, -		.sta_id = mvmvif->bcast_sta.sta_id, +		.sta_id = mvmvif->deflink.bcast_sta.sta_id,  		.tid = IWL_MAX_TID_COUNT,  		.aggregate = false,  		.frame_limit = IWL_FRAME_LIMIT, @@ -2233,7 +2371,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	 * to firmware so enable queue here - after the station was added  	 */  	if (iwl_mvm_has_new_tx_api(mvm)) { -		queue = iwl_mvm_tvqm_enable_txq(mvm, bsta->sta_id, +		queue = iwl_mvm_tvqm_enable_txq(mvm, NULL, bsta->sta_id,  						IWL_MAX_TID_COUNT,  						wdg_timeout);  		if (queue < 0) { @@ -2242,24 +2380,32 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  		}  		if (vif->type == NL80211_IFTYPE_AP || -		    vif->type == NL80211_IFTYPE_ADHOC) +		    vif->type == NL80211_IFTYPE_ADHOC) { +			/* for queue management */  			mvm->probe_queue = queue; -		else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) +			/* for use in TX */ +			mvmvif->deflink.mgmt_queue = queue; +		} else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {  			mvm->p2p_dev_queue = queue; +		} +	} else if (vif->type == NL80211_IFTYPE_AP || +		   vif->type == NL80211_IFTYPE_ADHOC) { +		/* set it for use in TX */ +		mvmvif->deflink.mgmt_queue = mvm->probe_queue;  	}  	return 0;  } -static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, -					  struct ieee80211_vif *vif) +void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, +				   struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	u16 *queueptr, queue;  	lockdep_assert_held(&mvm->mutex); -	iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true); +	iwl_mvm_flush_sta(mvm, &mvmvif->deflink.bcast_sta, true);  	switch (vif->type) {  	case NL80211_IFTYPE_AP: @@ -2276,13 +2422,17 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm,  	}  	queue = *queueptr; -	iwl_mvm_disable_txq(mvm, NULL, mvmvif->bcast_sta.sta_id, +	iwl_mvm_disable_txq(mvm, NULL, mvmvif->deflink.bcast_sta.sta_id,  			    queueptr, IWL_MAX_TID_COUNT); + +	if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) +		mvmvif->deflink.mgmt_queue = mvm->probe_queue; +  	if (iwl_mvm_has_new_tx_api(mvm))  		return; -	WARN_ON(!(mvmvif->bcast_sta.tfd_queue_msk & BIT(queue))); -	mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(queue); +	WARN_ON(!(mvmvif->deflink.bcast_sta.tfd_queue_msk & BIT(queue))); +	mvmvif->deflink.bcast_sta.tfd_queue_msk &= ~BIT(queue);  }  /* Send the FW a request to remove the station from it's internal data @@ -2296,7 +2446,7 @@ int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	iwl_mvm_free_bcast_sta_queues(mvm, vif); -	ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); +	ret = iwl_mvm_rm_sta_common(mvm, mvmvif->deflink.bcast_sta.sta_id);  	if (ret)  		IWL_WARN(mvm, "Failed sending remove station\n");  	return ret; @@ -2308,7 +2458,7 @@ int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	lockdep_assert_held(&mvm->mutex); -	return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, 0, +	return iwl_mvm_allocate_int_sta(mvm, &mvmvif->deflink.bcast_sta, 0,  					ieee80211_vif_type_p2p(vif),  					IWL_STA_GENERAL_PURPOSE);  } @@ -2323,7 +2473,7 @@ int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  int iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; +	struct iwl_mvm_int_sta *bsta = &mvmvif->deflink.bcast_sta;  	int ret;  	lockdep_assert_held(&mvm->mutex); @@ -2344,7 +2494,7 @@ void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); +	iwl_mvm_dealloc_int_sta(mvm, &mvmvif->deflink.bcast_sta);  }  /* @@ -2375,7 +2525,7 @@ int iwl_mvm_rm_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	struct iwl_mvm_int_sta *msta = &mvmvif->mcast_sta; +	struct iwl_mvm_int_sta *msta = &mvmvif->deflink.mcast_sta;  	static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00};  	const u8 *maddr = _maddr;  	struct iwl_trans_txq_scd_cfg cfg = { @@ -2402,7 +2552,7 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	 * changes in mac80211 layer.  	 */  	if (vif->type == NL80211_IFTYPE_ADHOC) -		mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; +		mvmvif->deflink.cab_queue = IWL_MVM_DQA_GCAST_QUEUE;  	/*  	 * While in previous FWs we had to exclude cab queue from TFD queue @@ -2410,9 +2560,10 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	 */  	if (!iwl_mvm_has_new_tx_api(mvm) &&  	    fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) { -		iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg, +		iwl_mvm_enable_txq(mvm, NULL, mvmvif->deflink.cab_queue, 0, +				   &cfg,  				   timeout); -		msta->tfd_queue_msk |= BIT(mvmvif->cab_queue); +		msta->tfd_queue_msk |= BIT(mvmvif->deflink.cab_queue);  	}  	ret = iwl_mvm_add_int_sta_common(mvm, msta, maddr,  					 mvmvif->id, mvmvif->color); @@ -2427,17 +2578,17 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	 * tfd_queue_mask.  	 */  	if (iwl_mvm_has_new_tx_api(mvm)) { -		int queue = iwl_mvm_tvqm_enable_txq(mvm, msta->sta_id, -						    0, -						    timeout); +		int queue = iwl_mvm_tvqm_enable_txq(mvm, NULL, msta->sta_id, +						    0, timeout);  		if (queue < 0) {  			ret = queue;  			goto err;  		} -		mvmvif->cab_queue = queue; +		mvmvif->deflink.cab_queue = queue;  	} else if (!fw_has_api(&mvm->fw->ucode_capa,  			       IWL_UCODE_TLV_API_STA_TYPE)) -		iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg, +		iwl_mvm_enable_txq(mvm, NULL, mvmvif->deflink.cab_queue, 0, +				   &cfg,  				   timeout);  	return 0; @@ -2510,12 +2661,12 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	lockdep_assert_held(&mvm->mutex); -	iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true); +	iwl_mvm_flush_sta(mvm, &mvmvif->deflink.mcast_sta, true); -	iwl_mvm_disable_txq(mvm, NULL, mvmvif->mcast_sta.sta_id, -			    &mvmvif->cab_queue, 0); +	iwl_mvm_disable_txq(mvm, NULL, mvmvif->deflink.mcast_sta.sta_id, +			    &mvmvif->deflink.cab_queue, 0); -	ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); +	ret = iwl_mvm_rm_sta_common(mvm, mvmvif->deflink.mcast_sta.sta_id);  	if (ret)  		IWL_WARN(mvm, "Failed sending remove station\n"); @@ -2604,13 +2755,14 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm,  }  static int iwl_mvm_fw_baid_op_sta(struct iwl_mvm *mvm, -				  struct iwl_mvm_sta *mvm_sta, +				  struct ieee80211_sta *sta,  				  bool start, int tid, u16 ssn,  				  u16 buf_size)  { +	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd cmd = {  		.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), -		.sta_id = mvm_sta->sta_id, +		.sta_id = mvm_sta->deflink.sta_id,  		.add_modify = STA_MODE_MODIFY,  	};  	u32 status; @@ -2652,7 +2804,7 @@ static int iwl_mvm_fw_baid_op_sta(struct iwl_mvm *mvm,  }  static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, -				  struct iwl_mvm_sta *mvm_sta, +				  struct ieee80211_sta *sta,  				  bool start, int tid, u16 ssn,  				  u16 buf_size, int baid)  { @@ -2666,7 +2818,8 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm,  	BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));  	if (start) { -		cmd.alloc.sta_id_mask = cpu_to_le32(BIT(mvm_sta->sta_id)); +		cmd.alloc.sta_id_mask = +			cpu_to_le32(iwl_mvm_sta_fw_id_mask(mvm, sta, -1));  		cmd.alloc.tid = tid;  		cmd.alloc.ssn = cpu_to_le16(ssn);  		cmd.alloc.win_size = cpu_to_le16(buf_size); @@ -2675,7 +2828,8 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm,  		cmd.remove_v1.baid = cpu_to_le32(baid);  		BUILD_BUG_ON(sizeof(cmd.remove_v1) > sizeof(cmd.remove));  	} else { -		cmd.remove.sta_id_mask = cpu_to_le32(BIT(mvm_sta->sta_id)); +		cmd.remove.sta_id_mask = +			cpu_to_le32(iwl_mvm_sta_fw_id_mask(mvm, sta, -1));  		cmd.remove.tid = cpu_to_le32(tid);  	} @@ -2698,16 +2852,16 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm,  	return baid;  } -static int iwl_mvm_fw_baid_op(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta, +static int iwl_mvm_fw_baid_op(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			      bool start, int tid, u16 ssn, u16 buf_size,  			      int baid)  {  	if (fw_has_capa(&mvm->fw->ucode_capa,  			IWL_UCODE_TLV_CAPA_BAID_ML_SUPPORT)) -		return iwl_mvm_fw_baid_op_cmd(mvm, mvm_sta, start, +		return iwl_mvm_fw_baid_op_cmd(mvm, sta, start,  					      tid, ssn, buf_size, baid); -	return iwl_mvm_fw_baid_op_sta(mvm, mvm_sta, start, +	return iwl_mvm_fw_baid_op_sta(mvm, sta, start,  				      tid, ssn, buf_size);  } @@ -2777,7 +2931,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	/* Don't send command to remove (start=0) BAID during restart */  	if (start || !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) -		baid = iwl_mvm_fw_baid_op(mvm, mvm_sta, start, tid, ssn, buf_size, +		baid = iwl_mvm_fw_baid_op(mvm, sta, start, tid, ssn, buf_size,  					  baid);  	if (baid < 0) { @@ -2799,7 +2953,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			    iwl_mvm_rx_agg_session_expired, 0);  		baid_data->mvm = mvm;  		baid_data->tid = tid; -		baid_data->sta_id = mvm_sta->sta_id; +		baid_data->sta_mask = iwl_mvm_sta_fw_id_mask(mvm, sta, -1);  		mvm_sta->tid_to_baid[tid] = baid;  		if (timeout) @@ -2814,7 +2968,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  		 * RX is being processed in parallel  		 */  		IWL_DEBUG_HT(mvm, "Sta %d(%d) is assigned to BAID %d\n", -			     mvm_sta->sta_id, tid, baid); +			     mvm_sta->deflink.sta_id, tid, baid);  		WARN_ON(rcu_access_pointer(mvm->baid_map[baid]));  		rcu_assign_pointer(mvm->baid_map[baid], baid_data);  	} else  { @@ -2876,7 +3030,7 @@ int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	}  	cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); -	cmd.sta_id = mvm_sta->sta_id; +	cmd.sta_id = mvm_sta->deflink.sta_id;  	cmd.add_modify = STA_MODE_MODIFY;  	if (!iwl_mvm_has_new_tx_api(mvm))  		cmd.modify_mask = STA_MODIFY_QUEUES; @@ -2968,7 +3122,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	 */  	txq_id = mvmsta->tid_data[tid].txq_id;  	if (txq_id == IWL_MVM_INVALID_QUEUE) { -		ret = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, +		ret = iwl_mvm_find_free_queue(mvm, mvmsta->deflink.sta_id,  					      IWL_MVM_DQA_MIN_DATA_QUEUE,  					      IWL_MVM_DQA_MAX_DATA_QUEUE);  		if (ret < 0) { @@ -3006,7 +3160,8 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	IWL_DEBUG_TX_QUEUES(mvm,  			    "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n", -			    mvmsta->sta_id, tid, txq_id, tid_data->ssn, +			    mvmsta->deflink.sta_id, tid, txq_id, +			    tid_data->ssn,  			    tid_data->next_reclaimed);  	/* @@ -3045,7 +3200,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	u16 ssn;  	struct iwl_trans_txq_scd_cfg cfg = { -		.sta_id = mvmsta->sta_id, +		.sta_id = mvmsta->deflink.sta_id,  		.tid = tid,  		.frame_limit = buf_size,  		.aggregate = true, @@ -3117,7 +3272,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		}  		ret = iwl_mvm_reconfig_scd(mvm, queue, cfg.fifo, -					   mvmsta->sta_id, tid, +					   mvmsta->deflink.sta_id, tid,  					   buf_size, ssn);  		if (ret) {  			IWL_ERR(mvm, @@ -3148,14 +3303,16 @@ out:  	 * for each station. Therefore, use the minimum of all the  	 * aggregation sessions and our default value.  	 */ -	mvmsta->max_agg_bufsize = -		min(mvmsta->max_agg_bufsize, buf_size); -	mvmsta->lq_sta.rs_drv.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; +	mvmsta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize = +		min(mvmsta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize, +		    buf_size); +	mvmsta->deflink.lq_sta.rs_drv.lq.agg_frame_cnt_limit = +		mvmsta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize;  	IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",  		     sta->addr, tid); -	return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq); +	return iwl_mvm_send_lq_cmd(mvm, &mvmsta->deflink.lq_sta.rs_drv.lq);  }  static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm, @@ -3204,7 +3361,8 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	txq_id = tid_data->txq_id;  	IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n", -			    mvmsta->sta_id, tid, txq_id, tid_data->state); +			    mvmsta->deflink.sta_id, tid, txq_id, +			    tid_data->state);  	mvmsta->agg_tids &= ~BIT(tid); @@ -3243,7 +3401,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	default:  		IWL_ERR(mvm,  			"Stopping AGG while state not ON or starting for %d on %d (%d)\n", -			mvmsta->sta_id, tid, tid_data->state); +			mvmsta->deflink.sta_id, tid, tid_data->state);  		IWL_ERR(mvm,  			"\ttid_data->txq_id = %d\n", tid_data->txq_id);  		err = -EINVAL; @@ -3269,7 +3427,8 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	spin_lock_bh(&mvmsta->lock);  	txq_id = tid_data->txq_id;  	IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n", -			    mvmsta->sta_id, tid, txq_id, tid_data->state); +			    mvmsta->deflink.sta_id, tid, txq_id, +			    tid_data->state);  	old_state = tid_data->state;  	tid_data->state = IWL_AGG_OFF;  	mvmsta->agg_tids &= ~BIT(tid); @@ -3281,7 +3440,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		iwl_mvm_drain_sta(mvm, mvmsta, true);  		if (iwl_mvm_has_new_tx_api(mvm)) { -			if (iwl_mvm_flush_sta_tids(mvm, mvmsta->sta_id, +			if (iwl_mvm_flush_sta_tids(mvm, mvmsta->deflink.sta_id,  						   BIT(tid)))  				IWL_ERR(mvm, "Couldn't flush the AGG queue\n");  			iwl_trans_wait_txq_empty(mvm->trans, txq_id); @@ -3341,8 +3500,8 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm,  	 * station ID, then use AP's station ID.  	 */  	if (vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { -		u8 sta_id = mvmvif->ap_sta_id; +	    mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { +		u8 sta_id = mvmvif->deflink.ap_sta_id;  		sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id],  					    lockdep_is_held(&mvm->mutex)); @@ -3619,10 +3778,13 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,  		return sta->addr;  	if (vif->type == NL80211_IFTYPE_STATION && -	    mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { -		u8 sta_id = mvmvif->ap_sta_id; +	    mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { +		u8 sta_id = mvmvif->deflink.ap_sta_id;  		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],  						lockdep_is_held(&mvm->mutex)); +		if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) +			return NULL; +  		return sta->addr;  	} @@ -3646,13 +3808,13 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	if (sta) {  		struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); -		sta_id = mvm_sta->sta_id; +		sta_id = mvm_sta->deflink.sta_id;  		mfp = sta->mfp;  	} else if (vif->type == NL80211_IFTYPE_AP &&  		   !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {  		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -		sta_id = mvmvif->mcast_sta.sta_id; +		sta_id = mvmvif->deflink.mcast_sta.sta_id;  	} else {  		IWL_ERR(mvm, "Failed to find station id\n");  		return -EINVAL; @@ -3660,6 +3822,11 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	if (keyconf->cipher == WLAN_CIPHER_SUITE_TKIP) {  		addr = iwl_mvm_get_mac_addr(mvm, vif, sta); +		if (!addr) { +			IWL_ERR(mvm, "Failed to find mac address\n"); +			return -EINVAL; +		} +  		/* get phase 1 key from mac80211 */  		ieee80211_get_key_rx_seq(keyconf, 0, &seq);  		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); @@ -3695,7 +3862,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  			IWL_ERR(mvm, "Failed to find station\n");  			return -EINVAL;  		} -		sta_id = mvm_sta->sta_id; +		sta_id = mvm_sta->deflink.sta_id;  		/*  		 * It is possible that the 'sta' parameter is NULL, and thus @@ -3717,7 +3884,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  	} else {  		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -		sta_id = mvmvif->mcast_sta.sta_id; +		sta_id = mvmvif->deflink.mcast_sta.sta_id;  	}  	if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || @@ -3790,9 +3957,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,  	/* Get the station from the mvm local station table */  	mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);  	if (mvm_sta) -		sta_id = mvm_sta->sta_id; +		sta_id = mvm_sta->deflink.sta_id;  	else if (!sta && vif->type == NL80211_IFTYPE_AP && mcast) -		sta_id = iwl_mvm_vif_from_mac80211(vif)->mcast_sta.sta_id; +		sta_id = iwl_mvm_vif_from_mac80211(vif)->deflink.mcast_sta.sta_id;  	IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", @@ -3848,7 +4015,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,  	mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);  	if (WARN_ON_ONCE(!mvm_sta))  		goto unlock; -	iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast, +	iwl_mvm_send_sta_key(mvm, mvm_sta->deflink.sta_id, keyconf, mcast,  			     iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx,  			     mfp); @@ -3862,7 +4029,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd cmd = {  		.add_modify = STA_MODE_MODIFY, -		.sta_id = mvmsta->sta_id, +		.sta_id = mvmsta->deflink.sta_id,  		.station_flags_msk = cpu_to_le32(STA_FLG_PS),  		.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),  	}; @@ -3883,7 +4050,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd cmd = {  		.add_modify = STA_MODE_MODIFY, -		.sta_id = mvmsta->sta_id, +		.sta_id = mvmsta->deflink.sta_id,  		.modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,  		.sleep_tx_count = cpu_to_le16(cnt),  		.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), @@ -3976,17 +4143,23 @@ void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,  }  void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, -				   struct iwl_mvm_sta *mvmsta, bool disable) +				   struct iwl_mvm_sta *mvmsta, +				   bool disable)  {  	struct iwl_mvm_add_sta_cmd cmd = {  		.add_modify = STA_MODE_MODIFY, -		.sta_id = mvmsta->sta_id, +		.sta_id = mvmsta->deflink.sta_id,  		.station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0,  		.station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX),  		.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),  	};  	int ret; +	if (mvm->mld_api_is_used) { +		iwl_mvm_mld_sta_modify_disable_tx(mvm, mvmsta, disable); +		return; +	} +  	ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC,  				   iwl_mvm_add_sta_cmd_size(mvm), &cmd);  	if (ret) @@ -3999,6 +4172,11 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,  {  	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); +	if (mvm->mld_api_is_used) { +		iwl_mvm_mld_sta_modify_disable_tx_ap(mvm, sta, disable); +		return; +	} +  	spin_lock_bh(&mvm_sta->lock);  	if (mvm_sta->disable_tx == disable) { @@ -4049,6 +4227,11 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,  	struct iwl_mvm_sta *mvm_sta;  	int i; +	if (mvm->mld_api_is_used) { +		iwl_mvm_mld_modify_all_sta_disable_tx(mvm, mvmvif, disable); +		return; +	} +  	rcu_read_lock();  	/* Block/unblock all the stations of the given mvmvif */ @@ -4071,17 +4254,19 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,  		return;  	/* Need to block/unblock also multicast station */ -	if (mvmvif->mcast_sta.sta_id != IWL_MVM_INVALID_STA) +	if (mvmvif->deflink.mcast_sta.sta_id != IWL_MVM_INVALID_STA)  		iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, -						  &mvmvif->mcast_sta, disable); +						  &mvmvif->deflink.mcast_sta, +						  disable);  	/*  	 * Only unblock the broadcast station (FW blocks it for immediate  	 * quiet, not the driver)  	 */ -	if (!disable && mvmvif->bcast_sta.sta_id != IWL_MVM_INVALID_STA) +	if (!disable && mvmvif->deflink.bcast_sta.sta_id != IWL_MVM_INVALID_STA)  		iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, -						  &mvmvif->bcast_sta, disable); +						  &mvmvif->deflink.bcast_sta, +						  disable);  }  void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -4091,7 +4276,7 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)  	rcu_read_lock(); -	mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); +	mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->deflink.ap_sta_id);  	if (mvmsta)  		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index f1a4fc3e4038..a61d4f88125f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -1,6 +1,6 @@  /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */  /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation   * Copyright (C) 2013-2014 Intel Mobile Communications GmbH   * Copyright (C) 2015-2016 Intel Deutschland GmbH   */ @@ -331,14 +331,32 @@ struct iwl_mvm_rxq_dup_data {  } ____cacheline_aligned_in_smp;  /** + * struct iwl_mvm_link_sta - link specific parameters of a station + * @rcu_head: used for freeing the data + * @sta_id: the index of the station in the fw + * @lq_sta: holds rate scaling data, either for the case when RS is done in + *	the driver - %rs_drv or in the FW - %rs_fw. + * @avg_energy: energy as reported by FW statistics notification + */ +struct iwl_mvm_link_sta { +	struct rcu_head rcu_head; +	u32 sta_id; +	union { +		struct iwl_lq_sta_rs_fw rs_fw; +		struct iwl_lq_sta rs_drv; +	} lq_sta; + +	u8 avg_energy; +}; + +/**   * struct iwl_mvm_sta - representation of a station in the driver - * @sta_id: the index of the station in the fw (will be replaced by id_n_color)   * @tfd_queue_msk: the tfd queues used by the station   * @mac_id_n_color: the MAC context this station is linked to   * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for   *	tid. - * @max_agg_bufsize: the maximal size of the AGG buffer for this station   * @sta_type: station type + * @authorized: indicates station is authorized   * @sta_state: station state according to enum %ieee80211_sta_state   * @bt_reduced_txpower: is reduced tx power enabled for this station   * @next_status_eosp: the next reclaimed packet is a PS-Poll response and @@ -347,8 +365,6 @@ struct iwl_mvm_rxq_dup_data {   * and from Tx response flow, it needs a spinlock.   * @tid_data: per tid data + mgmt. Look at %iwl_mvm_tid_data.   * @tid_to_baid: a simple map of TID to baid - * @lq_sta: holds rate scaling data, either for the case when RS is done in - *	the driver - %rs_drv or in the FW - %rs_fw.   * @reserved_queue: the queue reserved for this STA for DQA purposes   *	Every STA has is given one reserved queue to allow it to operate. If no   *	such queue can be guaranteed, the STA addition will fail. @@ -374,6 +390,12 @@ struct iwl_mvm_rxq_dup_data {   *	used during connection establishment (e.g. for the 4 way handshake   *	exchange).   * @pairwise_cipher: used to feed iwlmei upon authorization + * @deflink: the default link station, for non-MLO STA, all link specific data + *	is accessed via deflink (or link[0]). For MLO, it will hold data of the + *	first added link STA. + * @link: per link sta entries. For non-MLO only link[0] holds data. For MLO, + *	link[0] points to deflink and link[link_id] is allocated when new link + *	sta is added.   *   * When mac80211 creates a station it reserves some space (hw->sta_data_size)   * in the structure for use by driver. This structure is placed in that @@ -381,22 +403,17 @@ struct iwl_mvm_rxq_dup_data {   *   */  struct iwl_mvm_sta { -	u32 sta_id;  	u32 tfd_queue_msk;  	u32 mac_id_n_color;  	u16 tid_disable_agg; -	u16 max_agg_bufsize; -	enum iwl_sta_type sta_type; +	u8 sta_type;  	enum ieee80211_sta_state sta_state;  	bool bt_reduced_txpower;  	bool next_status_eosp; +	bool authorized;  	spinlock_t lock;  	struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT + 1];  	u8 tid_to_baid[IWL_MAX_TID_COUNT]; -	union { -		struct iwl_lq_sta_rs_fw rs_fw; -		struct iwl_lq_sta rs_drv; -	} lq_sta;  	struct ieee80211_vif *vif;  	struct iwl_mvm_key_pn __rcu *ptk_pn[4];  	struct iwl_mvm_rxq_dup_data *dup_data; @@ -414,9 +431,11 @@ struct iwl_mvm_sta {  	bool sleeping;  	u8 agg_tids;  	u8 sleep_tx_count; -	u8 avg_energy;  	u8 tx_ant;  	u32 pairwise_cipher; + +	struct iwl_mvm_link_sta deflink; +	struct iwl_mvm_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];  };  u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data); @@ -436,7 +455,7 @@ iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta)   */  struct iwl_mvm_int_sta {  	u32 sta_id; -	enum iwl_sta_type type; +	u8 type;  	u32 tfd_queue_msk;  }; @@ -452,6 +471,9 @@ struct iwl_mvm_int_sta {   */  int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  			   bool update, unsigned int flags); +int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, enum nl80211_iftype iftype); +int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_sta *sta, int sta_id, u8 sta_type);  int iwl_mvm_add_sta(struct iwl_mvm *mvm,  		    struct ieee80211_vif *vif,  		    struct ieee80211_sta *sta); @@ -463,8 +485,13 @@ static inline int iwl_mvm_update_sta(struct iwl_mvm *mvm,  	return iwl_mvm_sta_send_to_fw(mvm, sta, true, 0);  } +void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, +					  struct ieee80211_sta *sta);  int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm,  				  struct iwl_mvm_sta *mvm_sta); +bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		     struct ieee80211_sta *sta, +		     struct ieee80211_link_sta *link_sta, int *ret);  int iwl_mvm_rm_sta(struct iwl_mvm *mvm,  		   struct ieee80211_vif *vif,  		   struct ieee80211_sta *sta); @@ -510,6 +537,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id);  int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm);  int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, +				   struct ieee80211_vif *vif);  int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -519,7 +548,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,  			     struct iwl_mvm_int_sta *sta,  				    u32 qmask, enum nl80211_iftype iftype, -				    enum iwl_sta_type type); +				    u8 type);  void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta);  int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -543,6 +572,7 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,  void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,  				       struct iwl_mvm_vif *mvmvif,  				       bool disable); +  void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);  void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk);  int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -551,4 +581,80 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,  				   struct ieee80211_vif *vif,  				   u32 mac_id); +/* Queues */ +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, +			    struct ieee80211_sta *sta, +			    u8 sta_id, u8 tid, unsigned int timeout); + +/* Sta state */ +/** + * struct iwl_mvm_sta_state_ops - callbacks for the sta_state() ops + * + * Since the only difference between both MLD and + * non-MLD versions of sta_state() is these function calls, + * each version will send its specific function calls to + * %iwl_mvm_mac_sta_state_common(). + * + * @add_sta: pointer to the function that adds a new sta + * @update_sta: pointer to the function that updates a sta + * @rm_sta: pointer to the functions that removes a sta + * @mac_ctxt_changed: pointer to the function that handles a change in mac ctxt + */ +struct iwl_mvm_sta_state_ops { +	int (*add_sta)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		       struct ieee80211_sta *sta); +	int (*update_sta)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			  struct ieee80211_sta *sta); +	int (*rm_sta)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		      struct ieee80211_sta *sta); +	int (*mac_ctxt_changed)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +				bool force_assoc_off); +}; + +int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, +				 struct ieee80211_vif *vif, +				 struct ieee80211_sta *sta, +				 enum ieee80211_sta_state old_state, +				 enum ieee80211_sta_state new_state, +				 struct iwl_mvm_sta_state_ops *callbacks); + +/* New MLD STA related APIs */ +/* STA */ +int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf); +int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf); +int iwl_mvm_mld_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			      struct ieee80211_bss_conf *link_conf); +int iwl_mvm_mld_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id); +int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf); +int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			     struct ieee80211_bss_conf *link_conf); +int iwl_mvm_mld_rm_aux_sta(struct iwl_mvm *mvm); +int iwl_mvm_mld_add_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			struct ieee80211_sta *sta); +int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			   struct ieee80211_sta *sta); +int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +		       struct ieee80211_sta *sta); +int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id); +int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, +				 struct ieee80211_vif *vif, +				 struct ieee80211_sta *sta, +				 u16 old_links, u16 new_links); +u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, +			   int filter_link_id); + +/* Queues */ +void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm, +					   struct iwl_mvm_vif *mvmvif, +					   bool disable); +void iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm *mvm, +				       struct iwl_mvm_sta *mvm_sta, +				       bool disable); +void iwl_mvm_mld_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, +					  struct ieee80211_sta *sta, +					  bool disable);  #endif /* __sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index 674dd137fb9f..dae6f2a1aad9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -369,7 +369,7 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,  		goto out;  	}  	mvmsta = iwl_mvm_sta_from_mac80211(sta); -	cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id); +	cmd.peer_sta_id = cpu_to_le32(mvmsta->deflink.sta_id);  	if (!chandef) {  		if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && @@ -414,7 +414,7 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,  	}  	iwl_mvm_set_tx_cmd(mvm, skb, &tail->frame.tx_cmd, info, -			   mvmsta->sta_id); +			   mvmsta->deflink.sta_id);  	iwl_mvm_set_tx_cmd_rate(mvm, &tail->frame.tx_cmd, info, sta,  				hdr->frame_control); @@ -431,7 +431,7 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,  	/* channel switch has started, update state */  	if (type != TDLS_MOVE_CH) { -		mvm->tdls_cs.cur_sta_id = mvmsta->sta_id; +		mvm->tdls_cs.cur_sta_id = mvmsta->deflink.sta_id;  		iwl_mvm_tdls_update_cs_state(mvm,  					     type == TDLS_SEND_CHAN_SW_REQ ?  					     IWL_MVM_TDLS_SW_REQ_SENT : @@ -541,7 +541,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,  	}  	mvmsta = iwl_mvm_sta_from_mac80211(sta); -	mvm->tdls_cs.peer.sta_id = mvmsta->sta_id; +	mvm->tdls_cs.peer.sta_id = mvmsta->deflink.sta_id;  	mvm->tdls_cs.peer.chandef = *chandef;  	mvm->tdls_cs.peer.initiator = sta->tdls_initiator;  	mvm->tdls_cs.peer.op_class = oper_class; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index e403a240a82f..6b7b6250f1bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -79,7 +79,8 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)  		if (!WARN_ON(!mvm->p2p_device_vif)) {  			mvmvif = iwl_mvm_vif_from_mac80211(mvm->p2p_device_vif); -			iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true); +			iwl_mvm_flush_sta(mvm, &mvmvif->deflink.bcast_sta, +					  true);  		}  	} @@ -94,6 +95,11 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)  		/* do the same in case of hot spot 2.0 */  		iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true); +		if (mvm->mld_api_is_used) { +			iwl_mvm_mld_rm_aux_sta(mvm); +			goto out_unlock; +		} +  		/* In newer version of this command an aux station is added only  		 * in cases of dedicated tx queue and need to be removed in end  		 * of use */ @@ -101,6 +107,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)  			iwl_mvm_rm_aux_sta(mvm);  	} +out_unlock:  	mutex_unlock(&mvm->mutex);  } @@ -170,7 +177,8 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,  		struct iwl_mvm_sta *mvmsta;  		rcu_read_lock(); -		mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); +		mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, +						    mvmvif->deflink.ap_sta_id);  		if (!WARN_ON(!mvmsta))  			iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);  		rcu_read_unlock(); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c new file mode 100644 index 000000000000..edae3e24192b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ + +#include "mvm.h" +#include "time-sync.h" +#include <linux/ieee80211.h> + +void iwl_mvm_init_time_sync(struct iwl_time_sync_data *data) +{ +	skb_queue_head_init(&data->frame_list); +} + +static bool iwl_mvm_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token) +{ +	struct ieee80211_mgmt *mgmt = (void *)skb->data; +	u8 skb_dialog_token; + +	if (ieee80211_is_timing_measurement(skb)) +		skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token; +	else +		skb_dialog_token = mgmt->u.action.u.ftm.dialog_token; + +	if ((ether_addr_equal(mgmt->sa, addr) || +	     ether_addr_equal(mgmt->da, addr)) && +	    skb_dialog_token == dialog_token) +		return true; + +	return false; +} + +static struct sk_buff *iwl_mvm_time_sync_find_skb(struct iwl_mvm *mvm, u8 *addr, +						  u8 dialog_token) +{ +	struct sk_buff *skb; + +	/* The queue is expected to have only one SKB. If there are other SKBs +	 * in the queue, they did not get a time sync notification and are +	 * probably obsolete by now, so drop them. +	 */ +	while ((skb = skb_dequeue(&mvm->time_sync.frame_list))) { +		if (iwl_mvm_is_skb_match(skb, addr, dialog_token)) +			break; + +		kfree_skb(skb); +		skb = NULL; +	} + +	return skb; +} + +static u64 iwl_mvm_get_64_bit(__le32 high, __le32 low) +{ +	return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low); +} + +void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm, +				  struct iwl_rx_cmd_buffer *rxb) +{ +	struct iwl_rx_packet *pkt = rxb_addr(rxb); +	struct iwl_time_msmt_notify *notif = (void *)pkt->data; +	struct ieee80211_rx_status *rx_status; +	struct skb_shared_hwtstamps *shwt; +	u64 ts_10ns; +	struct sk_buff *skb = +		iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, +					   le32_to_cpu(notif->dialog_token)); +	u64 adj_time; + +	if (!skb) { +		IWL_DEBUG_INFO(mvm, "Time sync event but no pending skb\n"); +		return; +	} + +	ts_10ns = iwl_mvm_get_64_bit(notif->t2_hi, notif->t2_lo); +	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); +	shwt = skb_hwtstamps(skb); +	shwt->hwtstamp = ktime_set(0, adj_time); + +	ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo); +	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); +	rx_status = IEEE80211_SKB_RXCB(skb); +	rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time); + +	IWL_DEBUG_INFO(mvm, +		       "Time sync: RX event - report frame t2=%llu t3=%llu\n", +		       ktime_to_ns(shwt->hwtstamp), +		       ktime_to_ns(rx_status->ack_tx_hwtstamp)); +	ieee80211_rx_napi(mvm->hw, NULL, skb, NULL); +} + +void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, +					  struct iwl_rx_cmd_buffer *rxb) +{ +	struct iwl_rx_packet *pkt = rxb_addr(rxb); +	struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data; +	struct ieee80211_tx_status status = {}; +	struct skb_shared_hwtstamps *shwt; +	u64 ts_10ns, adj_time; + +	status.skb = +		iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, +					   le32_to_cpu(notif->dialog_token)); + +	if (!status.skb) { +		IWL_DEBUG_INFO(mvm, "Time sync confirm but no pending skb\n"); +		return; +	} + +	ts_10ns = iwl_mvm_get_64_bit(notif->t1_hi, notif->t1_lo); +	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); +	shwt = skb_hwtstamps(status.skb); +	shwt->hwtstamp = ktime_set(0, adj_time); + +	ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo); +	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); +	status.info = IEEE80211_SKB_CB(status.skb); +	status.ack_hwtstamp = ktime_set(0, adj_time); + +	IWL_DEBUG_INFO(mvm, +		       "Time sync: TX event - report frame t1=%llu t4=%llu\n", +		       ktime_to_ns(shwt->hwtstamp), +		       ktime_to_ns(status.ack_hwtstamp)); +	ieee80211_tx_status_ext(mvm->hw, &status); +} + +int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, u32 protocols) +{ +	struct iwl_time_sync_cfg_cmd cmd = {}; +	int err; + +	lockdep_assert_held(&mvm->mutex); + +	if (!fw_has_capa(&mvm->fw->ucode_capa, +			 IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM)) +		return -EINVAL; + +	/* The fw only supports one peer. We do allow reconfiguration of the +	 * same peer for cases of fw reset etc. +	 */ +	if (mvm->time_sync.active && +	    !ether_addr_equal(addr, mvm->time_sync.peer_addr)) { +		IWL_DEBUG_INFO(mvm, "Time sync: reject config for peer: %pM\n", +			       addr); +		return -ENOBUFS; +	} + +	if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM | +			  IWL_TIME_SYNC_PROTOCOL_FTM)) +		return -EINVAL; + +	cmd.protocols = cpu_to_le32(protocols); + +	ether_addr_copy(cmd.peer_addr, addr); + +	err = iwl_mvm_send_cmd_pdu(mvm, +				   WIDE_ID(DATA_PATH_GROUP, +					   WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), +				   0, sizeof(cmd), &cmd); +	if (err) { +		IWL_ERR(mvm, "Failed to send time sync cfg cmd: %d\n", err); +	} else { +		mvm->time_sync.active = protocols != 0; +		ether_addr_copy(mvm->time_sync.peer_addr, addr); +		IWL_DEBUG_INFO(mvm, "Time sync: set peer addr=%pM\n", addr); +	} + +	if (!mvm->time_sync.active) +		skb_queue_purge(&mvm->time_sync.frame_list); + +	return err; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h new file mode 100644 index 000000000000..2cfd0fb5e781 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2022 Intel Corporation + */ +#ifndef __TIME_SYNC_H__ +#define __TIME_SYNC_H__ + +#include "mvm.h" +#include <linux/ieee80211.h> + +void iwl_mvm_init_time_sync(struct iwl_time_sync_data *data); +void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm, +				  struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, +					  struct iwl_rx_cmd_buffer *rxb); +int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, +			     u32 protocols); + +static inline +bool iwl_mvm_time_sync_frame(struct iwl_mvm *mvm, struct sk_buff *skb, u8 *addr) +{ +	if (ether_addr_equal(mvm->time_sync.peer_addr, addr) && +	    (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) { +		skb_queue_tail(&mvm->time_sync.frame_list, skb); +		return true; +	} + +	return false; +} +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 232c200af38f..157e96fa23c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2012-2014, 2019-2021 Intel Corporation + * Copyright (C) 2012-2014, 2019-2022 Intel Corporation   * Copyright (C) 2013-2014 Intel Mobile Communications GmbH   * Copyright (C) 2015-2016 Intel Deutschland GmbH   */ @@ -334,7 +334,7 @@ static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,  	if (vif->type != NL80211_IFTYPE_STATION)  		return; -	iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); +	iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode, 0);  }  static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) @@ -615,7 +615,7 @@ send:  static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,  				  int *temperature)  { -	struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata; +	struct iwl_mvm *mvm = thermal_zone_device_priv(device);  	int ret;  	int temp; @@ -641,7 +641,7 @@ out:  static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,  				       int trip, int temp)  { -	struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata; +	struct iwl_mvm *mvm = thermal_zone_device_priv(device);  	struct iwl_mvm_thermal_device *tzone;  	int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 9813d7fa1800..00719e130438 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -14,6 +14,7 @@  #include "iwl-eeprom-parse.h"  #include "mvm.h"  #include "sta.h" +#include "time-sync.h"  static void  iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, @@ -183,10 +184,7 @@ static u32 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,  			   struct ieee80211_tx_info *info,  			   bool amsdu)  { -	if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ || -	    (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ && -	     CSR_HW_REV_TYPE(mvm->trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && -	     mvm->trans->hw_rev_step == SILICON_A_STEP)) +	if (!iwl_mvm_has_new_tx_csum(mvm))  		return iwl_mvm_tx_csum_pre_bz(mvm, skb, info, amsdu);  	return iwl_mvm_tx_csum_bz(mvm, skb, amsdu);  } @@ -331,22 +329,23 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,  			  sta ? iwl_mvm_sta_from_mac80211(sta)->sta_state : -1);  		rate_idx = info->control.rates[0].idx; + +		/* For non 2 GHZ band, remap mac80211 rate indices into driver +		 * indices. +		 */ +		if (info->band != NL80211_BAND_2GHZ || +		    (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) +			rate_idx += IWL_FIRST_OFDM_RATE; + +		/* For 2.4 GHZ band, check that there is no need to remap */ +		BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);  	}  	/* if the rate isn't a well known legacy rate, take the lowest one */  	if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY) -		rate_idx = rate_lowest_index( -				&mvm->nvm_data->bands[info->band], sta); - -	/* -	 * For non 2 GHZ band, remap mac80211 rate -	 * indices into driver indices -	 */ -	if (info->band != NL80211_BAND_2GHZ) -		rate_idx += IWL_FIRST_OFDM_RATE; - -	/* For 2.4 GHZ band, check that there is no need to remap */ -	BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); +		rate_idx = iwl_mvm_mac_ctxt_get_lowest_rate(mvm, +							    info, +							    info->control.vif);  	/* Get PLCP rate for tx_cmd->rate_n_flags */  	rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate_idx); @@ -603,11 +602,11 @@ static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,  }  static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm, +				      struct iwl_mvm_vif_link_info *link,  				      struct ieee80211_tx_info *info, -				      struct ieee80211_hdr *hdr) +				      struct sk_buff *skb)  { -	struct iwl_mvm_vif *mvmvif = -		iwl_mvm_vif_from_mac80211(info->control.vif); +	struct ieee80211_hdr *hdr = (void *)skb->data;  	__le16 fc = hdr->frame_control;  	switch (info->control.vif->type) { @@ -624,17 +623,17 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,  		 * reason 7 ("Class 3 frame received from nonassociated STA").  		 */  		if (ieee80211_is_mgmt(fc) && -		    (!ieee80211_is_bufferable_mmpdu(fc) || +		    (!ieee80211_is_bufferable_mmpdu(skb) ||  		     ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc))) -			return mvm->probe_queue; +			return link->mgmt_queue;  		if (!ieee80211_has_order(fc) && !ieee80211_is_probe_req(fc) &&  		    is_multicast_ether_addr(hdr->addr1)) -			return mvmvif->cab_queue; +			return link->cab_queue;  		WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC,  			  "fc=0x%02x", le16_to_cpu(fc)); -		return mvm->probe_queue; +		return link->mgmt_queue;  	case NL80211_IFTYPE_P2P_DEVICE:  		if (ieee80211_is_mgmt(fc))  			return mvm->p2p_dev_queue; @@ -667,7 +666,7 @@ static void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm,  	rcu_read_lock(); -	resp_data = rcu_dereference(mvmvif->probe_resp_data); +	resp_data = rcu_dereference(mvmvif->deflink.probe_resp_data);  	if (!resp_data)  		goto out; @@ -738,12 +737,26 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)  		if (info.control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||  		    info.control.vif->type == NL80211_IFTYPE_AP ||  		    info.control.vif->type == NL80211_IFTYPE_ADHOC) { +			u32 link_id = u32_get_bits(info.control.flags, +						   IEEE80211_TX_CTRL_MLO_LINK); +			struct iwl_mvm_vif_link_info *link; + +			if (link_id == IEEE80211_LINK_UNSPECIFIED) { +				if (info.control.vif->active_links) +					link_id = ffs(info.control.vif->active_links) - 1; +				else +					link_id = 0; +			} + +			link = mvmvif->link[link_id]; +  			if (!ieee80211_is_data(hdr->frame_control)) -				sta_id = mvmvif->bcast_sta.sta_id; +				sta_id = link->bcast_sta.sta_id;  			else -				sta_id = mvmvif->mcast_sta.sta_id; +				sta_id = link->mcast_sta.sta_id; -			queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info, hdr); +			queue = iwl_mvm_get_ctrl_vif_queue(mvm, link, &info, +							   skb);  		} else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) {  			queue = mvm->snif_queue;  			sta_id = mvm->snif_sta.sta_id; @@ -791,10 +804,11 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,  				    struct ieee80211_sta *sta, unsigned int tid)  {  	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); -	enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band;  	u8 ac = tid_to_mac80211_ac[tid]; +	enum nl80211_band band;  	unsigned int txf; -	int lmac = iwl_mvm_get_lmac_id(mvm->fw, band); +	unsigned int val; +	int lmac;  	/* For HE redirect to trigger based fifos */  	if (sta->deflink.he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm))) @@ -808,7 +822,37 @@ unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,  	 * We also want to have the start of the next packet inside the  	 * fifo to be able to send bursts.  	 */ -	return min_t(unsigned int, mvmsta->max_amsdu_len, +	val = mvmsta->max_amsdu_len; + +	if (hweight16(sta->valid_links) <= 1) { +		if (sta->valid_links) { +			struct ieee80211_bss_conf *link_conf; +			unsigned int link = ffs(sta->valid_links) - 1; + +			rcu_read_lock(); +			link_conf = rcu_dereference(mvmsta->vif->link_conf[link]); +			if (WARN_ON(!link_conf)) +				band = NL80211_BAND_2GHZ; +			else +				band = link_conf->chandef.chan->band; +			rcu_read_unlock(); +		} else { +			band = mvmsta->vif->bss_conf.chandef.chan->band; +		} + +		lmac = iwl_mvm_get_lmac_id(mvm->fw, band); +	} else if (fw_has_capa(&mvm->fw->ucode_capa, +			       IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) { +		/* for real MLO restrict to both LMACs if they exist */ +		lmac = IWL_LMAC_5G_INDEX; +		val = min_t(unsigned int, val, +			    mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); +		lmac = IWL_LMAC_24G_INDEX; +	} else { +		lmac = IWL_LMAC_24G_INDEX; +	} + +	return min_t(unsigned int, val,  		     mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);  } @@ -1083,7 +1127,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,  	if (WARN_ON_ONCE(!mvmsta))  		return -1; -	if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) +	if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA))  		return -1;  	if (unlikely(ieee80211_is_any_nullfunc(fc)) && sta->deflink.he_cap.has_he) @@ -1093,7 +1137,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,  		iwl_mvm_probe_resp_set_noa(mvm, skb);  	dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, -					sta, mvmsta->sta_id); +					sta, mvmsta->deflink.sta_id);  	if (!dev_cmd)  		goto drop; @@ -1169,7 +1213,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,  	}  	IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x len %d\n", -		     mvmsta->sta_id, tid, txq_id, +		     mvmsta->deflink.sta_id, tid, txq_id,  		     IEEE80211_SEQ_TO_SN(seq_number), skb->len);  	/* From now on, we cannot access info->control */ @@ -1204,7 +1248,8 @@ drop_unlock_sta:  	iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);  	spin_unlock(&mvmsta->lock);  drop: -	IWL_DEBUG_TX(mvm, "TX to [%d|%d] dropped\n", mvmsta->sta_id, tid); +	IWL_DEBUG_TX(mvm, "TX to [%d|%d] dropped\n", mvmsta->deflink.sta_id, +		     tid);  	return -1;  } @@ -1221,7 +1266,7 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,  	if (WARN_ON_ONCE(!mvmsta))  		return -1; -	if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) +	if (WARN_ON_ONCE(mvmsta->deflink.sta_id == IWL_MVM_INVALID_STA))  		return -1;  	memcpy(&info, skb->cb, sizeof(info)); @@ -1241,8 +1286,7 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,  	if (ret)  		return ret; -	if (WARN_ON(skb_queue_empty(&mpdus_skbs))) -		return ret; +	WARN_ON(skb_queue_empty(&mpdus_skbs));  	while (!skb_queue_empty(&mpdus_skbs)) {  		skb = __skb_dequeue(&mpdus_skbs); @@ -1396,8 +1440,8 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,  		r->idx = rate;  	} else if (format ==  RATE_MCS_VHT_MSK) {  		ieee80211_rate_set_vht(r, rate, -				       ((rate_n_flags & RATE_MCS_NSS_MSK) >> -					RATE_MCS_NSS_POS) + 1); +				       FIELD_GET(RATE_MCS_NSS_MSK, +						 rate_n_flags) + 1);  		r->flags |= IEEE80211_TX_RC_VHT_MCS;  	} else if (format == RATE_MCS_HE_MSK) {  		/* mac80211 cannot do this without ieee80211_tx_status_ext() @@ -1428,8 +1472,7 @@ void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags,  	} else if (rate_n_flags & RATE_MCS_VHT_MSK_V1) {  		ieee80211_rate_set_vht(  			r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, -			((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> -						RATE_VHT_MCS_NSS_POS) + 1); +			FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1);  		r->flags |= IEEE80211_TX_RC_VHT_MCS;  	} else {  		r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, @@ -1644,7 +1687,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,  		info->status.status_driver_data[0] =  			RS_DRV_DATA_PACK(lq_color, tx_resp->reduced_tpc); -		ieee80211_tx_status(mvm->hw, skb); +		if (likely(!iwl_mvm_time_sync_frame(mvm, skb, hdr->addr1))) +			ieee80211_tx_status(mvm->hw, skb);  	}  	/* This is an aggregation queue or might become one, so we use @@ -1831,7 +1875,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,  	mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id);  	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); -	if (WARN_ON_ONCE(!sta || !sta->wme)) { +	if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta) || !sta->wme)) {  		rcu_read_unlock();  		return;  	} @@ -1974,9 +2018,11 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,  	 * possible (i.e. first MPDU in the aggregation wasn't acked)  	 * Still it's important to update RS about sent vs. acked.  	 */ -	if (!is_flush && skb_queue_empty(&reclaimed_skbs)) { +	if (!is_flush && skb_queue_empty(&reclaimed_skbs) && +	    !iwl_mvm_has_tlc_offload(mvm)) {  		struct ieee80211_chanctx_conf *chanctx_conf = NULL; +		/* no TLC offload, so non-MLD mode */  		if (mvmsta->vif)  			chanctx_conf =  				rcu_dereference(mvmsta->vif->bss_conf.chanctx_conf); @@ -1987,11 +2033,8 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,  		tx_info->band = chanctx_conf->def.chan->band;  		iwl_mvm_hwrate_to_tx_status(mvm->fw, rate, tx_info); -		if (!iwl_mvm_has_tlc_offload(mvm)) { -			IWL_DEBUG_TX_REPLY(mvm, -					   "No reclaim. Update rs directly\n"); -			iwl_mvm_rs_tx_status(mvm, sta, tid, tx_info, false); -		} +		IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); +		iwl_mvm_rs_tx_status(mvm, sta, tid, tx_info, false);  	}  out: @@ -2229,17 +2272,22 @@ free_rsp:  int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal)  { -	struct iwl_mvm_int_sta *int_sta = sta; -	struct iwl_mvm_sta *mvm_sta = sta; +	u32 sta_id, tfd_queue_msk; -	BUILD_BUG_ON(offsetof(struct iwl_mvm_int_sta, sta_id) != -		     offsetof(struct iwl_mvm_sta, sta_id)); +	if (internal) { +		struct iwl_mvm_int_sta *int_sta = sta; -	if (iwl_mvm_has_new_tx_api(mvm)) -		return iwl_mvm_flush_sta_tids(mvm, mvm_sta->sta_id, 0xffff); +		sta_id = int_sta->sta_id; +		tfd_queue_msk = int_sta->tfd_queue_msk; +	} else { +		struct iwl_mvm_sta *mvm_sta = sta; -	if (internal) -		return iwl_mvm_flush_tx_path(mvm, int_sta->tfd_queue_msk); +		sta_id = mvm_sta->deflink.sta_id; +		tfd_queue_msk = mvm_sta->tfd_queue_msk; +	} + +	if (iwl_mvm_has_new_tx_api(mvm)) +		return iwl_mvm_flush_sta_tids(mvm, sta_id, 0xffff); -	return iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk); +	return iwl_mvm_flush_tx_path(mvm, tfd_queue_msk);  } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 14b2de65bd84..af31b09c3966 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -272,13 +272,15 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq)   * @vif: Pointer to the ieee80211_vif structure   * @req_type: The part of the driver who call for a change.   * @smps_request: The request to change the SMPS mode. + * @link_id: for MLO link_id, otherwise 0 (deflink)   *   * Get a requst to change the SMPS mode,   * and change it according to all other requests in the driver.   */  void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			 enum iwl_mvm_smps_type_request req_type, -			 enum ieee80211_smps_mode smps_request) +			 enum ieee80211_smps_mode smps_request, +			 unsigned int link_id)  {  	struct iwl_mvm_vif *mvmvif;  	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; @@ -294,17 +296,38 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  		return;  	mvmvif = iwl_mvm_vif_from_mac80211(vif); -	mvmvif->smps_requests[req_type] = smps_request; + +	if (WARN_ON_ONCE(!mvmvif->link[link_id])) +		return; + +	mvmvif->link[link_id]->smps_requests[req_type] = smps_request;  	for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { -		if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { +		if (mvmvif->link[link_id]->smps_requests[i] == +		    IEEE80211_SMPS_STATIC) {  			smps_mode = IEEE80211_SMPS_STATIC;  			break;  		} -		if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) +		if (mvmvif->link[link_id]->smps_requests[i] == +		    IEEE80211_SMPS_DYNAMIC)  			smps_mode = IEEE80211_SMPS_DYNAMIC;  	} -	ieee80211_request_smps(vif, 0, smps_mode); +	ieee80211_request_smps(vif, link_id, smps_mode); +} + +void iwl_mvm_update_smps_on_active_links(struct iwl_mvm *mvm, +					 struct ieee80211_vif *vif, +					 enum iwl_mvm_smps_type_request req_type, +					 enum ieee80211_smps_mode smps_request) +{ +	struct ieee80211_bss_conf *link_conf; +	unsigned int link_id; + +	rcu_read_lock(); +	for_each_vif_active_link(vif, link_conf, link_id) +		iwl_mvm_update_smps(mvm, vif, req_type, smps_request, +				    link_id); +	rcu_read_unlock();  }  static bool iwl_wait_stats_complete(struct iwl_notif_wait_data *notif_wait, @@ -392,12 +415,12 @@ static void iwl_mvm_diversity_iter(void *_data, u8 *mac,  	struct iwl_mvm_diversity_iter_data *data = _data;  	int i; -	if (mvmvif->phy_ctxt != data->ctxt) +	if (mvmvif->deflink.phy_ctxt != data->ctxt)  		return;  	for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { -		if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || -		    mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) { +		if (mvmvif->deflink.smps_requests[i] == IEEE80211_SMPS_STATIC || +		    mvmvif->deflink.smps_requests[i] == IEEE80211_SMPS_DYNAMIC) {  			data->result = false;  			break;  		} @@ -495,10 +518,10 @@ static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)  	if (iwl_mvm_vif_low_latency(mvmvif)) {  		result->result = true; -		if (!mvmvif->phy_ctxt) +		if (!mvmvif->deflink.phy_ctxt)  			return; -		band = mvmvif->phy_ctxt->channel->band; +		band = mvmvif->deflink.phy_ctxt->channel->band;  		result->result_per_band[band] = true;  	}  } @@ -819,10 +842,10 @@ static void iwl_mvm_uapsd_agg_disconnect(struct iwl_mvm *mvm,  	if (!vif->cfg.assoc)  		return; -	if (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && -	    !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && -	    !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && -	    !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) +	if (!mvmvif->deflink.queue_params[IEEE80211_AC_VO].uapsd && +	    !mvmvif->deflink.queue_params[IEEE80211_AC_VI].uapsd && +	    !mvmvif->deflink.queue_params[IEEE80211_AC_BE].uapsd && +	    !mvmvif->deflink.queue_params[IEEE80211_AC_BK].uapsd)  		return;  	if (mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected) @@ -831,7 +854,8 @@ static void iwl_mvm_uapsd_agg_disconnect(struct iwl_mvm *mvm,  	mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected = true;  	IWL_INFO(mvm,  		 "detected AP should do aggregation but isn't, likely due to U-APSD\n"); -	schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, 15 * HZ); +	schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, +			      15 * HZ);  }  static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm, @@ -883,10 +907,10 @@ static void iwl_mvm_tcm_iterator(void *_data, u8 *mac,  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);  	u32 *band = _data; -	if (!mvmvif->phy_ctxt) +	if (!mvmvif->deflink.phy_ctxt)  		return; -	band[mvmvif->id] = mvmvif->phy_ctxt->channel->band; +	band[mvmvif->id] = mvmvif->deflink.phy_ctxt->channel->band;  }  static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm, @@ -1137,3 +1161,36 @@ void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type,  		iwl_mvm_power_update_device(mvm);  	}  } + +/* Find if at least two links from different vifs use same channel + * FIXME: consider having a refcount array in struct iwl_mvm_vif for + * used phy_ctxt ids. + */ +bool iwl_mvm_have_links_same_channel(struct iwl_mvm_vif *vif1, +				     struct iwl_mvm_vif *vif2) +{ +	unsigned int i, j; + +	for_each_mvm_vif_valid_link(vif1, i) { +		for_each_mvm_vif_valid_link(vif2, j) { +			if (vif1->link[i]->phy_ctxt == vif2->link[j]->phy_ctxt) +				return true; +		} +	} + +	return false; +} + +bool iwl_mvm_vif_is_active(struct iwl_mvm_vif *mvmvif) +{ +	unsigned int i; + +	/* FIXME: can it fail when phy_ctxt is assigned? */ +	for_each_mvm_vif_valid_link(mvmvif, i) { +		if (mvmvif->link[i]->phy_ctxt && +		    mvmvif->link[i]->phy_ctxt->id < NUM_PHY_CTX) +			return true; +	} + +	return false; +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 99768d6a6032..79115eb1c285 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -504,6 +504,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {  /* Bz devices */  	{IWL_PCI_DEVICE(0x2727, PCI_ANY_ID, iwl_bz_trans_cfg)}, +	{IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_bz_trans_cfg)},  	{IWL_PCI_DEVICE(0xA840, PCI_ANY_ID, iwl_bz_trans_cfg)},  	{IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)},  #endif /* CONFIG_IWLMVM */ @@ -513,16 +514,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {  MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);  #define _IWL_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \ -		      _rf_id, _no_160, _cores, _cdb, _jacket, _cfg, _name) \ -	{ .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg),  \ -	  .name = _name, .mac_type = _mac_type, .rf_type = _rf_type,	   \ -	  .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id,		   \ +		      _rf_id, _rf_step, _no_160, _cores, _cdb, _jacket, _cfg, \ +		      _name) \ +	{ .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg), \ +	  .name = _name, .mac_type = _mac_type, .rf_type = _rf_type, .rf_step = _rf_step, \ +	  .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id, \  	  .mac_step = _mac_step, .cdb = _cdb, .jacket = _jacket }  #define IWL_DEV_INFO(_device, _subdevice, _cfg, _name) \ -	_IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY,	   \ -		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY,  \ -		      IWL_CFG_ANY, IWL_CFG_ANY, _cfg, _name) +	_IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY, \ +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, _cfg, _name)  static const struct iwl_dev_info iwl_dev_info_table[] = {  #if IS_ENABLED(CONFIG_IWLMVM) @@ -546,6 +548,8 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  	IWL_DEV_INFO(0x54F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),  	IWL_DEV_INFO(0x7A70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name),  	IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), +	IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), +	IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name),  	IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name),  	IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma_a0_gf4_a0, iwl_ax411_killer_1690s_name), @@ -565,7 +569,6 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  	IWL_DEV_INFO(0x43F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650i_name),  	IWL_DEV_INFO(0x43F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL),  	IWL_DEV_INFO(0x43F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), -	IWL_DEV_INFO(0x43F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650s_name),  	IWL_DEV_INFO(0xA0F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL),  	IWL_DEV_INFO(0xA0F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL),  	IWL_DEV_INFO(0xA0F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), @@ -694,87 +697,87 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_2ac_cfg_soc, iwl9560_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9461_160_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9461_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9462_160_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_PNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9462_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9270_160_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9270_name),  	_IWL_DEV_INFO(0x271B, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9162_160_name),  	_IWL_DEV_INFO(0x271B, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9162_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9260_160_name),  	_IWL_DEV_INFO(0x2526, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9260_2ac_cfg, iwl9260_name), @@ -782,176 +785,176 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  	/* Qu B step */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9560_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550s_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550i_name),  	/* Qu C step */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9560_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550s_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550i_name),  	/* QuZ */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9560_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550s_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550i_name),  	/* QnJ */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9560_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1551,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9560_killer_1550s_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, 0x1552,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl9560_qnj_b0_jf_b0_cfg, iwl9560_killer_1550i_name), @@ -959,367 +962,408 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  	/* Qu B step */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qu_b0_hr1_b0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qu_b0_hr_b0, iwl_ax203_name),  	/* Qu C step */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qu_c0_hr1_b0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qu_c0_hr_b0, iwl_ax203_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qu_c0_hr_b0, iwl_ax201_name),  	/* QuZ */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_quz_a0_hr1_b0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_quz_a0_hr_b0, iwl_ax203_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_quz_a0_hr_b0, iwl_ax201_name),  /* QnJ with Hr */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_QNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_qnj_b0_hr_b0_cfg, iwl_ax201_name),  /* SnJ with Jf */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9462_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_jf_b0, iwl9560_name),  /* SnJ with Hr */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_hr_b0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_hr_b0, iwl_ax201_name),  /* Ma */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_hr_b0, iwl_ax201_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_gf_a0, iwl_ax211_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_gf4_a0, iwl_ax211_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_mr_a0, iwl_ax221_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_fm_a0, iwl_ax231_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_mr_a0, iwl_ax221_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_ma_b0_hr_b0, iwl_ax201_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_ma_b0_gf_a0, iwl_ax211_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_ANY, +		      iwl_cfg_ma_b0_gf4_a0, iwl_ax211_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_ma_b0_mr_a0, iwl_ax221_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_MA, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_ma_b0_fm_a0, iwl_ax231_name),  /* So with Hr */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax203_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, -		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax201_name),  /* So-F with Hr */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax203_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, -		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax101_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_hr_a0, iwl_ax201_name),  /* So-F with Gf */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_ANY,  		      iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name),  /* Bz */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_bz_a0_hr_a0, iwl_bz_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_a0_hr_b0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_a0_gf_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_a0_gf4_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MR, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_a0_mr_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_BZ, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_A_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_a0_fm_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_A_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_NO_JACKET,  		      iwl_cfg_bz_a0_fm4_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_B_STEP, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET, +		      iwl_cfg_bz_a0_fm_b0, iwl_bz_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_B_STEP, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, +		      iwl_cfg_bz_a0_fm4_b0, iwl_bz_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_A_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_NO_JACKET,  		      iwl_cfg_gl_a0_fm_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_B_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_NO_JACKET,  		      iwl_cfg_gl_b0_fm_b0, iwl_bz_name),  /* BZ Z step */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_BZ, SILICON_Z_STEP, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_bz_z0_gf_a0, iwl_bz_name),  /* BNJ */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_A_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET,  		      iwl_cfg_bnj_a0_fm_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_B_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET,  		      iwl_cfg_bnj_b0_fm_b0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_A_STEP,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET,  		      iwl_cfg_bnj_a0_fm4_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, SILICON_B_STEP, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, +		      iwl_cfg_bnj_b0_fm4_b0, iwl_bz_name), +	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET,  		      iwl_cfg_bnj_a0_gf_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, -		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, -		      iwl_cfg_bnj_a0_gf4_a0, iwl_bz_name), -	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, +		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET, -		      iwl_cfg_bnj_a0_hr_b0, iwl_bz_name), - -/* SoF with JF2 */ +		      iwl_cfg_bnj_b0_gf_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, -		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), +		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, +		      iwl_cfg_bnj_a0_gf4_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, -		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), - -/* SoF with JF */ +		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, +		      iwl_cfg_bnj_b0_gf4_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, -		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), +		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_bnj_a0_hr_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, -		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), +		      IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_bnj_a0_hr_b0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, -		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), +		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_bnj_b0_hr_a0, iwl_bz_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, -		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, -		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY, -		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), +		      IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, +		      IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, +		      IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY, +		      iwl_cfg_bnj_b0_hr_b0, iwl_bz_name),  /* SoF with JF2 */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_name),  /* SoF with JF */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_name),  /* So with GF */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_ANY,  		      iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name),  /* So with JF2 */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, +		      IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9560_name),  /* So with JF */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9461_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, +		      IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY,  		      IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), @@ -1327,22 +1371,22 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {  /* For now we use the same FW as MR, but this will change in the future. */  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_ms_a0, iwl_ax204_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_so_a0_ms_a0, iwl_ax204_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_ma_a0_ms_a0, iwl_ax204_name),  	_IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_MAC_TYPE_SNJ, IWL_CFG_ANY, -		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, +		      IWL_CFG_RF_TYPE_MS, IWL_CFG_ANY, IWL_CFG_ANY,  		      IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_ANY,  		      iwl_cfg_snj_a0_ms_a0, iwl_ax204_name) @@ -1377,8 +1421,16 @@ static int get_crf_id(struct iwl_trans *iwl_trans)  	/* Read crf info */  	iwl_trans->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr); +	/* Read cnv info */ +	iwl_trans->hw_cnv_id = +		iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP); +  	/* Read cdb info (also contains the jacket info if needed in the future */ -	iwl_trans->hw_cdb_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); +	iwl_trans->hw_wfpm_id = +		iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); +	IWL_INFO(iwl_trans, "Detected crf-id 0x%x, cnv-id 0x%x wfpm id 0x%x\n", +		 iwl_trans->hw_crf_id, iwl_trans->hw_cnv_id, +		 iwl_trans->hw_wfpm_id);  	iwl_trans_release_nic_access(iwl_trans); @@ -1394,7 +1446,11 @@ static int map_crf_id(struct iwl_trans *iwl_trans)  {  	int ret = 0;  	u32 val = iwl_trans->hw_crf_id; -	u32 cdb = iwl_trans->hw_cdb_id; +	u32 step_id = REG_CRF_ID_STEP(val); +	u32 slave_id = REG_CRF_ID_SLAVE(val); +	u32 jacket_id_cnv  = REG_CRF_ID_SLAVE(iwl_trans->hw_cnv_id); +	u32 jacket_id_wfpm  = WFPM_OTP_CFG1_IS_JACKET(iwl_trans->hw_wfpm_id); +	u32 cdb_id_wfpm  = WFPM_OTP_CFG1_IS_CDB(iwl_trans->hw_wfpm_id);  	/* Map between crf id to rf id */  	switch (REG_CRF_ID_TYPE(val)) { @@ -1404,9 +1460,12 @@ static int map_crf_id(struct iwl_trans *iwl_trans)  	case REG_CRF_ID_TYPE_JF_2:  		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_JF2 << 12);  		break; -	case REG_CRF_ID_TYPE_HR_NONE_CDB: +	case REG_CRF_ID_TYPE_HR_NONE_CDB_1X1:  		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR1 << 12);  		break; +	case REG_CRF_ID_TYPE_HR_NONE_CDB: +		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12); +		break;  	case REG_CRF_ID_TYPE_HR_CDB:  		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12);  		break; @@ -1416,27 +1475,43 @@ static int map_crf_id(struct iwl_trans *iwl_trans)  	case REG_CRF_ID_TYPE_MR:  		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_MR << 12);  		break; -		case REG_CRF_ID_TYPE_FM: -			iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12); -			break; +	case REG_CRF_ID_TYPE_FM: +	case REG_CRF_ID_TYPE_FMI: +	case REG_CRF_ID_TYPE_FMR: +		iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12); +		break;  	default:  		ret = -EIO;  		IWL_ERR(iwl_trans, -			"Can find a correct rfid for crf id 0x%x\n", +			"Can't find a correct rfid for crf id 0x%x\n",  			REG_CRF_ID_TYPE(val));  		goto out;  	} +	/* Set Step-id */ +	iwl_trans->hw_rf_id |= (step_id << 8); +  	/* Set CDB capabilities */ -	if (cdb & BIT(4)) { +	if (cdb_id_wfpm || slave_id) {  		iwl_trans->hw_rf_id += BIT(28);  		IWL_INFO(iwl_trans, "Adding cdb to rf id\n");  	} -	IWL_INFO(iwl_trans, "Detected RF 0x%x from crf id 0x%x\n", -		 iwl_trans->hw_rf_id, REG_CRF_ID_TYPE(val)); +	/* Set Jacket capabilities */ +	if (jacket_id_wfpm || jacket_id_cnv) { +		iwl_trans->hw_rf_id += BIT(29); +		IWL_INFO(iwl_trans, "Adding jacket to rf id\n"); +	} +	IWL_INFO(iwl_trans, +		 "Detected rf-type 0x%x step-id 0x%x slave-id 0x%x from crf id 0x%x\n", +		 REG_CRF_ID_TYPE(val), step_id, slave_id, iwl_trans->hw_rf_id); +	IWL_INFO(iwl_trans, +		 "Detected cdb-id 0x%x jacket-id 0x%x from wfpm id 0x%x\n", +		 cdb_id_wfpm, jacket_id_wfpm, iwl_trans->hw_wfpm_id); +	IWL_INFO(iwl_trans, "Detected jacket-id 0x%x from cnvi id 0x%x\n", +		 jacket_id_cnv, iwl_trans->hw_cnv_id);  out:  	return ret; @@ -1447,8 +1522,8 @@ out:  static const struct iwl_dev_info *  iwl_pci_find_dev_info(u16 device, u16 subsystem_device, -		      u16 mac_type, u8 mac_step, -		      u16 rf_type, u8 cdb, u8 jacket, u8 rf_id, u8 no_160, u8 cores) +		      u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, +		      u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step)  {  	int num_devices = ARRAY_SIZE(iwl_dev_info_table);  	int i; @@ -1499,6 +1574,10 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device,  		    dev_info->cores != cores)  			continue; +		if (dev_info->rf_step != (u8)IWL_CFG_ANY && +		    dev_info->rf_step != rf_step) +			continue; +  		return dev_info;  	} @@ -1570,6 +1649,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  		goto out_free_trans;  	} +	IWL_INFO(iwl_trans, "PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n", +		 pdev->device, pdev->subsystem_device, +		 iwl_trans->hw_rev, iwl_trans->hw_rf_id); +  	dev_info = iwl_pci_find_dev_info(pdev->device, pdev->subsystem_device,  					 CSR_HW_REV_TYPE(iwl_trans->hw_rev),  					 iwl_trans->hw_rev_step, @@ -1578,8 +1661,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  					 CSR_HW_RFID_IS_JACKET(iwl_trans->hw_rf_id),  					 IWL_SUBDEVICE_RF_ID(pdev->subsystem_device),  					 IWL_SUBDEVICE_NO_160(pdev->subsystem_device), -					 IWL_SUBDEVICE_CORES(pdev->subsystem_device)); - +					 IWL_SUBDEVICE_CORES(pdev->subsystem_device), +					 CSR_HW_RFID_STEP(iwl_trans->hw_rf_id));  	if (dev_info) {  		iwl_trans->cfg = dev_info->cfg;  		iwl_trans->name = dev_info->name; @@ -1699,6 +1782,9 @@ static void iwl_pci_remove(struct pci_dev *pdev)  {  	struct iwl_trans *trans = pci_get_drvdata(pdev); +	if (!trans) +		return; +  	iwl_drv_stop(trans->drv);  	iwl_trans_pcie_free(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index f7e4f868363d..69b95ad5993b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -497,6 +497,7 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans);  void iwl_pcie_rx_free(struct iwl_trans *trans);  void iwl_pcie_free_rbs_pool(struct iwl_trans *trans);  void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq); +void iwl_pcie_rx_napi_sync(struct iwl_trans *trans);  void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority,  			    struct iwl_rxq *rxq); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 9c9f87fe8377..0d7890f99a5f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /* - * Copyright (C) 2003-2014, 2018-2022 Intel Corporation + * Copyright (C) 2003-2014, 2018-2023 Intel Corporation   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH   * Copyright (C) 2016-2017 Intel Deutschland GmbH   */ @@ -1053,6 +1053,22 @@ static int iwl_pcie_napi_poll_msix(struct napi_struct *napi, int budget)  	return ret;  } +void iwl_pcie_rx_napi_sync(struct iwl_trans *trans) +{ +	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +	int i; + +	if (unlikely(!trans_pcie->rxq)) +		return; + +	for (i = 0; i < trans->num_rx_queues; i++) { +		struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + +		if (rxq && rxq->napi.poll) +			napi_synchronize(&rxq->napi); +	} +} +  static int _iwl_pcie_rx_init(struct iwl_trans *trans)  {  	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 94f40c4d2421..73b395841ca8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -1,7 +1,7 @@  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause  /*   * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation   */  #include "iwl-trans.h"  #include "iwl-prph.h" @@ -156,6 +156,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)  	if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {  		IWL_DEBUG_INFO(trans,  			       "DEVICE_ENABLED bit was set and is now cleared\n"); +		iwl_pcie_rx_napi_sync(trans);  		iwl_txq_gen2_tx_free(trans);  		iwl_pcie_rx_stop(trans);  	} @@ -277,6 +278,9 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans)  	case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB):  		pos = scnprintf(buf, buflen, "HRCDB");  		break; +	case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_MS): +		pos = scnprintf(buf, buflen, "MS"); +		break;  	default:  		return;  	} @@ -347,7 +351,7 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr)  	mutex_unlock(&trans_pcie->mutex);  } -static void iwl_pcie_set_ltr(struct iwl_trans *trans) +static bool iwl_pcie_set_ltr(struct iwl_trans *trans)  {  	u32 ltr_val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ |  		      u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC, @@ -368,18 +372,77 @@ static void iwl_pcie_set_ltr(struct iwl_trans *trans)  	     trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) &&  	    !trans->trans_cfg->integrated) {  		iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val); -	} else if (trans->trans_cfg->integrated && -		   trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) { +		return true; +	} + +	if (trans->trans_cfg->integrated && +	    trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) {  		iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL);  		iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val); +		return true;  	} + +	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { +		/* First clear the interrupt, just in case */ +		iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD, +			    MSIX_HW_INT_CAUSES_REG_IML); +		/* In this case, unfortunately the same ROM bug exists in the +		 * device (not setting LTR correctly), but we don't have control +		 * over the settings from the host due to some hardware security +		 * features. The only workaround we've been able to come up with +		 * so far is to try to keep the CPU and device busy by polling +		 * it and the IML (image loader) completed interrupt. +		 */ +		return false; +	} + +	/* nothing needs to be done on other devices */ +	return true; +} + +static void iwl_pcie_spin_for_iml(struct iwl_trans *trans) +{ +/* in practice, this seems to complete in around 20-30ms at most, wait 100 */ +#define IML_WAIT_TIMEOUT	(HZ / 10) +	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +	unsigned long end_time = jiffies + IML_WAIT_TIMEOUT; +	u32 value, loops = 0; +	bool irq = false; + +	if (WARN_ON(!trans_pcie->iml)) +		return; + +	value = iwl_read32(trans, CSR_LTR_LAST_MSG); +	IWL_DEBUG_INFO(trans, "Polling for IML load - CSR_LTR_LAST_MSG=0x%x\n", +		       value); + +	while (time_before(jiffies, end_time)) { +		if (iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD) & +				MSIX_HW_INT_CAUSES_REG_IML) { +			irq = true; +			break; +		} +		/* Keep the CPU and device busy. */ +		value = iwl_read32(trans, CSR_LTR_LAST_MSG); +		loops++; +	} + +	IWL_DEBUG_INFO(trans, +		       "Polled for IML load: irq=%d, loops=%d, CSR_LTR_LAST_MSG=0x%x\n", +		       irq, loops, value); + +	/* We don't fail here even if we timed out - maybe we get lucky and the +	 * interrupt comes in later (and we get alive from firmware) and then +	 * we're all happy - but if not we'll fail on alive timeout or get some +	 * other error out. +	 */  }  int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,  				 const struct fw_img *fw, bool run_in_rfkill)  {  	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); -	bool hw_rfkill; +	bool hw_rfkill, keep_ram_busy;  	int ret;  	/* This may fail if AMT took ownership of the device */ @@ -440,7 +503,7 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,  	if (ret)  		goto out; -	iwl_pcie_set_ltr(trans); +	keep_ram_busy = !iwl_pcie_set_ltr(trans);  	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {  		iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE); @@ -452,6 +515,9 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,  		iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1);  	} +	if (keep_ram_busy) +		iwl_pcie_spin_for_iml(trans); +  	/* re-check RF-Kill state since we may have missed the interrupt */  	hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);  	if (hw_rfkill && !run_in_rfkill) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 0a9af1ad1f20..b281850fbf7a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -599,7 +599,6 @@ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans)  int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)  {  	int ret; -	int t = 0;  	int iter;  	IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); @@ -616,6 +615,8 @@ int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)  	usleep_range(1000, 2000);  	for (iter = 0; iter < 10; iter++) { +		int t = 0; +  		/* If HW is not ready, prepare the conditions to check again */  		iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,  			    CSR_HW_IF_CONFIG_REG_PREPARE); @@ -1260,6 +1261,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)  	if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {  		IWL_DEBUG_INFO(trans,  			       "DEVICE_ENABLED bit was set and is now cleared\n"); +		iwl_pcie_rx_napi_sync(trans);  		iwl_pcie_tx_stop(trans);  		iwl_pcie_rx_stop(trans); @@ -1522,19 +1524,16 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)  	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	int ret; -	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { +	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210)  		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,  				    suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND :  					      UREG_DOORBELL_TO_ISR6_RESUME); -	} else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { +	else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)  		iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,  			    suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :  				      CSR_IPC_SLEEP_CONTROL_RESUME); -		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, -				    UREG_DOORBELL_TO_ISR6_SLEEP_CTRL); -	} else { +	else  		return 0; -	}  	ret = wait_event_timeout(trans_pcie->sx_waitq,  				 trans_pcie->sx_complete, 2 * HZ); @@ -2863,7 +2862,7 @@ static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,  				  void *buf, ssize_t *size,  				  ssize_t *bytes_copied)  { -	int buf_size_left = count - *bytes_copied; +	ssize_t buf_size_left = count - *bytes_copied;  	buf_size_left = buf_size_left - (buf_size_left % sizeof(u32));  	if (*size > buf_size_left) diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index 726185d6fab8..d1c39c214f95 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -1554,14 +1554,18 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn,  		     struct sk_buff_head *skbs)  {  	struct iwl_txq *txq = trans->txqs.txq[txq_id]; -	int tfd_num = iwl_txq_get_cmd_index(txq, ssn); -	int read_ptr = iwl_txq_get_cmd_index(txq, txq->read_ptr); -	int last_to_free; +	int tfd_num, read_ptr, last_to_free;  	/* This function is not meant to release cmd queue*/  	if (WARN_ON(txq_id == trans->txqs.cmd.q_id))  		return; +	if (WARN_ON(!txq)) +		return; + +	tfd_num = iwl_txq_get_cmd_index(txq, ssn); +	read_ptr = iwl_txq_get_cmd_index(txq, txq->read_ptr); +  	spin_lock_bh(&txq->lock);  	if (!test_bit(txq_id, trans->txqs.queue_used)) {  |