diff options
Diffstat (limited to 'drivers/net/wireless/intel')
64 files changed, 2039 insertions, 1026 deletions
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 0812db8936f1..b6636002c7d2 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -317,8 +317,6 @@ static int ipw2100_get_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, size_t max); -static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, - size_t max); static void ipw2100_release_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); static int ipw2100_ucode_download(struct ipw2100_priv *priv, @@ -5894,17 +5892,14 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct ipw2100_priv *priv = libipw_priv(dev); - char fw_ver[64], ucode_ver[64]; + char fw_ver[64]; strscpy(info->driver, DRV_NAME, sizeof(info->driver)); strscpy(info->version, DRV_VERSION, sizeof(info->version)); ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver)); - ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver)); - - snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s", - fw_ver, priv->eeprom_version, ucode_ver); + strscpy(info->fw_version, fw_ver, sizeof(info->fw_version)); strscpy(info->bus_info, pci_name(priv->pci_dev), sizeof(info->bus_info)); } @@ -8406,17 +8401,6 @@ static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, return tmp; } -static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, - size_t max) -{ - u32 ver; - u32 len = sizeof(ver); - /* microcode version is a 32 bit integer */ - if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len)) - return -EIO; - return snprintf(buf, max, "%08X", ver); -} - /* * On exit, the firmware will have been freed from the fw list */ diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 820100cac491..eed9ef17bc29 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -9656,31 +9656,30 @@ static int ipw_wx_get_wireless_mode(struct net_device *dev, mutex_lock(&priv->mutex); switch (priv->ieee->mode) { case IEEE_A: - strncpy(extra, "802.11a (1)", MAX_WX_STRING); + strscpy_pad(extra, "802.11a (1)", MAX_WX_STRING); break; case IEEE_B: - strncpy(extra, "802.11b (2)", MAX_WX_STRING); + strscpy_pad(extra, "802.11b (2)", MAX_WX_STRING); break; case IEEE_A | IEEE_B: - strncpy(extra, "802.11ab (3)", MAX_WX_STRING); + strscpy_pad(extra, "802.11ab (3)", MAX_WX_STRING); break; case IEEE_G: - strncpy(extra, "802.11g (4)", MAX_WX_STRING); + strscpy_pad(extra, "802.11g (4)", MAX_WX_STRING); break; case IEEE_A | IEEE_G: - strncpy(extra, "802.11ag (5)", MAX_WX_STRING); + strscpy_pad(extra, "802.11ag (5)", MAX_WX_STRING); break; case IEEE_B | IEEE_G: - strncpy(extra, "802.11bg (6)", MAX_WX_STRING); + strscpy_pad(extra, "802.11bg (6)", MAX_WX_STRING); break; case IEEE_A | IEEE_B | IEEE_G: - strncpy(extra, "802.11abg (7)", MAX_WX_STRING); + strscpy_pad(extra, "802.11abg (7)", MAX_WX_STRING); break; default: - strncpy(extra, "unknown", MAX_WX_STRING); + strscpy_pad(extra, "unknown", MAX_WX_STRING); break; } - extra[MAX_WX_STRING - 1] = '\0'; IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra); @@ -10378,7 +10377,6 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, { struct ipw_priv *p = libipw_priv(dev); char vers[64]; - char date[32]; u32 len; strscpy(info->driver, DRV_NAME, sizeof(info->driver)); @@ -10386,11 +10384,8 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, len = sizeof(vers); ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len); - len = sizeof(date); - ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len); - snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)", - vers, date); + strscpy(info->fw_version, vers, sizeof(info->fw_version)); strscpy(info->bus_info, pci_name(p->pci_dev), sizeof(info->bus_info)); } diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index 8d5f9dce71d5..134635c70ce8 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_AX210_UCODE_API_MAX 83 +#define IWL_AX210_UCODE_API_MAX 86 /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 59 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 3d223014cfe6..82da957adcf6 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 83 +#define IWL_BZ_UCODE_API_MAX 86 /* Lowest firmware API version supported */ #define IWL_BZ_UCODE_API_MIN 80 @@ -158,7 +158,7 @@ const struct iwl_cfg iwl_cfg_bz = { .fw_name_mac = "bz", .uhb_supported = true, IWL_DEVICE_BZ, - .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_BZ_EHT, }; @@ -166,7 +166,7 @@ const struct iwl_cfg iwl_cfg_gl = { .fw_name_mac = "gl", .uhb_supported = true, IWL_DEVICE_BZ, - .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_BZ_EHT, }; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index d6243025993e..80eb9b499538 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 83 +#define IWL_SC_UCODE_API_MAX 86 /* Lowest firmware API version supported */ #define IWL_SC_UCODE_API_MIN 82 @@ -151,7 +151,7 @@ const struct iwl_cfg iwl_cfg_sc = { .fw_name_mac = "sc", .uhb_supported = true, IWL_DEVICE_SC, - .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .num_rbds = IWL_NUM_RBDS_SC_EHT, }; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index a873be109f43..8774dd7b921e 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1464,7 +1464,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, snprintf(priv->hw->wiphy->fw_version, sizeof(priv->hw->wiphy->fw_version), - "%s", fw->fw_version); + "%.31s", fw->fw_version); priv->new_scan_threshold_behaviour = !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index 60a7b61d59aa..111ed1873006 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -3,6 +3,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2023 Intel Corporation *****************************************************************************/ #include <linux/kernel.h> @@ -1169,7 +1170,7 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) iwlagn_check_ratid_empty(priv, sta_id, tid); } - iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); + iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs, false); freed = 0; @@ -1247,7 +1248,7 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); - ieee80211_tx_status(priv->hw, skb); + ieee80211_tx_status_skb(priv->hw, skb); } } @@ -1315,7 +1316,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, * block-ack window (we assume that they've been successfully * transmitted ... if not, it's too late anyway). */ iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn, - &reclaimed_skbs); + &reclaimed_skbs, false); IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", @@ -1384,6 +1385,6 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(priv->hw, skb); + ieee80211_tx_status_skb(priv->hw, skb); } } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index e83ce797a68b..b96f30d11644 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -1015,15 +1015,25 @@ __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) __le32 config_bitmap = 0; /* - ** Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2' + * Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'. + * Setting config_bitmap Indonesia bit is valid only for HR/JF. */ - ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, - DSM_FUNC_ENABLE_INDONESIA_5G2, - &iwl_guid, &value); - - if (!ret && value == DSM_VALUE_INDONESIA_ENABLE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); + switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { + case IWL_CFG_RF_TYPE_HR1: + case IWL_CFG_RF_TYPE_HR2: + case IWL_CFG_RF_TYPE_JF1: + case IWL_CFG_RF_TYPE_JF2: + ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, + DSM_FUNC_ENABLE_INDONESIA_5G2, + &iwl_guid, &value); + + if (!ret && value == DSM_VALUE_INDONESIA_ENABLE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); + break; + default: + break; + } /* ** Evaluate func 'DSM_FUNC_DISABLE_SRD' diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index d129fc66d8bb..e9277f6f3582 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -138,7 +138,8 @@ enum iwl_dsm_funcs_rev_0 { DSM_FUNC_11AX_ENABLEMENT = 6, DSM_FUNC_ENABLE_UNII4_CHAN = 7, DSM_FUNC_ACTIVATE_CHANNEL = 8, - DSM_FUNC_FORCE_DISABLE_CHANNELS = 9 + DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, + DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, }; enum iwl_dsm_values_srd { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 13cb0d53a1a3..7544c4cb1a30 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -30,6 +30,8 @@ * @REGULATORY_AND_NVM_GROUP: regulatory/NVM group, uses command IDs from * &enum iwl_regulatory_and_nvm_subcmd_ids * @DEBUG_GROUP: Debug group, uses command IDs from &enum iwl_debug_cmds + * @STATISTICS_GROUP: Statistics group, uses command IDs from + * &enum iwl_statistics_subcmd_ids */ enum iwl_mvm_command_groups { LEGACY_GROUP = 0x0, @@ -44,6 +46,7 @@ enum iwl_mvm_command_groups { PROT_OFFLOAD_GROUP = 0xb, REGULATORY_AND_NVM_GROUP = 0xc, DEBUG_GROUP = 0xf, + STATISTICS_GROUP = 0x10, }; /** @@ -617,9 +620,36 @@ enum iwl_system_subcmd_ids { SYSTEM_FEATURES_CONTROL_CMD = 0xd, /** + * @SYSTEM_STATISTICS_CMD: &struct iwl_system_statistics_cmd + */ + SYSTEM_STATISTICS_CMD = 0xf, + + /** + * @SYSTEM_STATISTICS_END_NOTIF: &struct iwl_system_statistics_end_notif + */ + SYSTEM_STATISTICS_END_NOTIF = 0xfd, + + /** * @RFI_DEACTIVATE_NOTIF: &struct iwl_rfi_deactivate_notif */ RFI_DEACTIVATE_NOTIF = 0xff, }; +/** + * enum iwl_statistics_subcmd_ids - Statistics group command IDs + */ +enum iwl_statistics_subcmd_ids { + /** + * @STATISTICS_OPER_NOTIF: Notification about operational + * statistics &struct iwl_system_statistics_notif_oper + */ + STATISTICS_OPER_NOTIF = 0x0, + + /** + * @STATISTICS_OPER_PART1_NOTIF: Notification about operational part1 + * statistics &struct iwl_system_statistics_part1_notif_oper + */ + STATISTICS_OPER_PART1_NOTIF = 0x1, +}; + #endif /* __iwl_fw_api_commands_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 39bee9c00e07..394747deb269 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -43,6 +43,30 @@ struct iwl_fw_ini_header { } __packed; /* FW_TLV_DEBUG_HEADER_S_VER_1 */ /** + * struct iwl_fw_ini_addr_size - Base address and size that defines + * a chunk of memory + * + * @addr: the base address (fixed size - 4 bytes) + * @size: the size to read + */ +struct iwl_fw_ini_addr_size { + __le32 addr; + __le32 size; +} __packed; /* FW_TLV_DEBUG_ADDR_SIZE_VER_1 */ + +/** + * struct iwl_fw_ini_region_dev_addr_range - Configuration to read + * device address range + * + * @offset: offset to add to the base address of each chunk + * The addrs[] array will be treated as an array of &iwl_fw_ini_addr_size - + * an array of (addr, size) pairs. + */ +struct iwl_fw_ini_region_dev_addr_range { + __le32 offset; +} __packed; /* FW_TLV_DEBUG_DEVICE_ADDR_RANGE_API_S_VER_1 */ + +/** * struct iwl_fw_ini_region_dev_addr - Configuration to read device addresses * * @size: size of each memory chunk @@ -135,6 +159,10 @@ struct iwl_fw_ini_region_internal_buffer { * &IWL_FW_INI_REGION_PAGING, &IWL_FW_INI_REGION_CSR, * &IWL_FW_INI_REGION_DRAM_IMR and &IWL_FW_INI_REGION_PCI_IOSF_CONFIG * &IWL_FW_INI_REGION_DBGI_SRAM, &FW_TLV_DEBUG_REGION_TYPE_DBGI_SRAM, + * &IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP, + * @dev_addr_range: device address range configuration. Used by + * &IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE and + * &IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE * @fifos: fifos configuration. Used by &IWL_FW_INI_REGION_TXF and * &IWL_FW_INI_REGION_RXF * @err_table: error table configuration. Used by @@ -157,6 +185,7 @@ struct iwl_fw_ini_region_tlv { u8 name[IWL_FW_INI_MAX_NAME]; union { struct iwl_fw_ini_region_dev_addr dev_addr; + struct iwl_fw_ini_region_dev_addr_range dev_addr_range; struct iwl_fw_ini_region_fifos fifos; struct iwl_fw_ini_region_err_table err_table; struct iwl_fw_ini_region_internal_buffer internal_buffer; @@ -362,6 +391,9 @@ enum iwl_fw_ini_buffer_location { * @IWL_FW_INI_REGION_PCI_IOSF_CONFIG: PCI/IOSF config * @IWL_FW_INI_REGION_SPECIAL_DEVICE_MEMORY: special device memory * @IWL_FW_INI_REGION_DBGI_SRAM: periphery registers of DBGI SRAM + * @IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE: a range of periphery registers of MAC + * @IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE: a range of periphery registers of PHY + * @IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP: periphery registers of SNPS DPHYIP * @IWL_FW_INI_REGION_NUM: number of region types */ enum iwl_fw_ini_region_type { @@ -384,6 +416,9 @@ enum iwl_fw_ini_region_type { IWL_FW_INI_REGION_PCI_IOSF_CONFIG, IWL_FW_INI_REGION_SPECIAL_DEVICE_MEMORY, IWL_FW_INI_REGION_DBGI_SRAM, + IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE, + IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE, + IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP, IWL_FW_INI_REGION_NUM }; /* FW_TLV_DEBUG_REGION_TYPE_API_E */ 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 184db5a6f06f..f15e6d64c298 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -58,6 +58,14 @@ enum iwl_mac_conf_subcmd_ids { */ STA_DISABLE_TX_CMD = 0xD, /** + * @ROC_CMD: &struct iwl_roc_req + */ + ROC_CMD = 0xE, + /** + * @ROC_NOTIF: &struct iwl_roc_notif + */ + ROC_NOTIF = 0xF8, + /** * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif */ SESSION_PROTECTION_NOTIF = 0xFB, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index c4577219c501..dfe0bebabc81 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -21,8 +21,9 @@ enum iwl_regulatory_and_nvm_subcmd_ids { * &struct iwl_lari_config_change_cmd_v2, * &struct iwl_lari_config_change_cmd_v3, * &struct iwl_lari_config_change_cmd_v4, - * &struct iwl_lari_config_change_cmd_v5 or - * &struct iwl_lari_config_change_cmd_v6 + * &struct iwl_lari_config_change_cmd_v5, + * &struct iwl_lari_config_change_cmd_v6 or + * &struct iwl_lari_config_change_cmd_v7 */ LARI_CONFIG_CHANGE = 0x1, @@ -44,6 +45,11 @@ enum iwl_regulatory_and_nvm_subcmd_ids { SAR_OFFSET_MAPPING_TABLE_CMD = 0x4, /** + * @UATS_TABLE_CMD: &struct iwl_uats_table_cmd + */ + UATS_TABLE_CMD = 0x5, + + /** * @PNVM_INIT_COMPLETE_NTFY: &struct iwl_pnvm_init_complete_ntfy */ PNVM_INIT_COMPLETE_NTFY = 0xFE, @@ -603,6 +609,45 @@ struct iwl_lari_config_change_cmd_v6 { } __packed; /* LARI_CHANGE_CONF_CMD_S_VER_6 */ /** + * struct iwl_lari_config_change_cmd_v7 - change LARI configuration + * This structure is used also for lari cmd version 8. + * @config_bitmap: Bitmap of the config commands. Each bit will trigger a + * different predefined FW config operation. + * @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets. + * @oem_11ax_allow_bitmap: Bitmap of 11ax allowed MCCs. There are two bits + * per country, one to indicate whether to override and the other to + * indicate the value to use. + * @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits + * per country, one to indicate whether to override and the other to + * indicate allow/disallow unii4 channels. + * @chan_state_active_bitmap: Bitmap to enable different bands per country + * or region. + * Each bit represents a country or region, and a band to activate + * according to the BIOS definitions. + * For LARI cmd version 7 - bits 0:3 are supported. + * For LARI cmd version 8 - bits 0:4 are supported. + * @force_disable_channels_bitmap: Bitmap of disabled bands/channels. + * Each bit represents a set of channels in a specific band that should be + * disabled + * @edt_bitmap: Bitmap of energy detection threshold table. + * Disable/enable the EDT optimization method for different band. + */ +struct iwl_lari_config_change_cmd_v7 { + __le32 config_bitmap; + __le32 oem_uhb_allow_bitmap; + __le32 oem_11ax_allow_bitmap; + __le32 oem_unii4_allow_bitmap; + __le32 chan_state_active_bitmap; + __le32 force_disable_channels_bitmap; + __le32 edt_bitmap; +} __packed; +/* LARI_CHANGE_CONF_CMD_S_VER_7 */ +/* LARI_CHANGE_CONF_CMD_S_VER_8 */ + +/* Activate UNII-1 (5.2GHz) for World Wide */ +#define ACTIVATE_5G2_IN_WW_MASK BIT(4) + +/** * struct iwl_pnvm_init_complete_ntfy - PNVM initialization complete * @status: PNVM image loading status */ @@ -610,4 +655,17 @@ struct iwl_pnvm_init_complete_ntfy { __le32 status; } __packed; /* PNVM_INIT_COMPLETE_NTFY_S_VER_1 */ +#define UATS_TABLE_ROW_SIZE 26 +#define UATS_TABLE_COL_SIZE 13 + +/** + * struct iwl_uats_table_cmd - struct for UATS_TABLE_CMD + * @offset_map: mapping a mcc to UHB AP type support (UATS) allowed + * @reserved: reserved + */ +struct iwl_uats_table_cmd { + u8 offset_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE]; + __le16 reserved; +} __packed; /* UATS_TABLE_CMD_S_VER_1 */ + #endif /* __iwl_fw_api_nvm_reg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index 8fe42cff1102..306ed88de463 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -25,8 +25,8 @@ * For legacy set bit means upper channel, otherwise lower. * For VHT - bit-2 marks if the control is lower/upper relative to center-freq * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. - * center_freq * For EHT - bit-3 is used for extended distance + * center_freq * | * 40Mhz |____|____| * 80Mhz |____|____|____|____| diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h index 898e62326e6c..2271b19213fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020 - 2021 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020 - 2021, 2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_stats_h__ #define __iwl_fw_api_stats_h__ #include "mac.h" +#include "mac-cfg.h" struct mvm_statistics_dbg { __le32 burst_check; @@ -412,6 +413,49 @@ struct iwl_statistics_cmd { #define MAX_BCAST_FILTER_NUM 8 /** + * enum iwl_statistics_notify_type_id - type_id used in system statistics + * command + * @IWL_STATS_NTFY_TYPE_ID_OPER: request legacy statistics + * @IWL_STATS_NTFY_TYPE_ID_OPER_PART1: request operational part1 statistics + * @IWL_STATS_NTFY_TYPE_ID_OPER_PART2: request operational part2 statistics + * @IWL_STATS_NTFY_TYPE_ID_OPER_PART3: request operational part3 statistics + * @IWL_STATS_NTFY_TYPE_ID_OPER_PART4: request operational part4 statistics + */ +enum iwl_statistics_notify_type_id { + IWL_STATS_NTFY_TYPE_ID_OPER = BIT(0), + IWL_STATS_NTFY_TYPE_ID_OPER_PART1 = BIT(1), + IWL_STATS_NTFY_TYPE_ID_OPER_PART2 = BIT(2), + IWL_STATS_NTFY_TYPE_ID_OPER_PART3 = BIT(3), + IWL_STATS_NTFY_TYPE_ID_OPER_PART4 = BIT(4), +}; + +/** + * enum iwl_statistics_cfg_flags - cfg_mask used in system statistics command + * @IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK: 0 for enable, 1 for disable + * @IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK: 0 for periodic, 1 for on-demand + * @IWL_STATS_CFG_FLG_RESET_MSK: 0 for reset statistics after + * sending the notification, 1 for do not reset statistics after sending + * the notification + */ +enum iwl_statistics_cfg_flags { + IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK = BIT(0), + IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK = BIT(1), + IWL_STATS_CFG_FLG_RESET_MSK = BIT(2), +}; + +/** + * struct iwl_system_statistics_cmd - system statistics command + * @cfg_mask: configuration mask, &enum iwl_statistics_cfg_flags + * @config_time_sec: time in sec for periodic notification + * @type_id_mask: type_id masks, &enum iwl_statistics_notify_type_id + */ +struct iwl_system_statistics_cmd { + __le32 cfg_mask; + __le32 config_time_sec; + __le32 type_id_mask; +} __packed; /* STATISTICS_FW_CMD_API_S_VER_1 */ + +/** * enum iwl_fw_statistics_type * * @FW_STATISTICS_OPERATIONAL: operational statistics @@ -447,7 +491,49 @@ struct iwl_statistics_ntfy_hdr { }; /* STATISTICS_NTFY_HDR_API_S_VER_1 */ /** - * struct iwl_statistics_ntfy_per_mac + * struct iwl_stats_ntfy_per_link + * + * @beacon_filter_average_energy: Average energy [-dBm] of the 2 + * antennas. + * @air_time: air time + * @beacon_counter: all beacons (both filtered and not filtered) + * @beacon_average_energy: Average energy [-dBm] of all beacons + * (both filtered and not filtered) + * @beacon_rssi_a: beacon RSSI on antenna A + * @beacon_rssi_b: beacon RSSI on antenna B + * @rx_bytes: RX byte count + */ +struct iwl_stats_ntfy_per_link { + __le32 beacon_filter_average_energy; + __le32 air_time; + __le32 beacon_counter; + __le32 beacon_average_energy; + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 rx_bytes; +} __packed; /* STATISTICS_NTFY_PER_LINK_API_S_VER_1 */ + +/** + * struct iwl_stats_ntfy_part1_per_link + * + * @rx_time: rx time + * @tx_time: tx time + * @rx_action: action frames handled by FW + * @tx_action: action frames generated and transmitted by FW + * @cca_defers: cca defer count + * @beacon_filtered: filtered out beacons + */ +struct iwl_stats_ntfy_part1_per_link { + __le64 rx_time; + __le64 tx_time; + __le32 rx_action; + __le32 tx_action; + __le32 cca_defers; + __le32 beacon_filtered; +} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_PER_LINK_API_S_VER_1 */ + +/** + * struct iwl_stats_ntfy_per_mac * * @beacon_filter_average_energy: Average energy [-dBm] of the 2 * antennas. @@ -459,7 +545,7 @@ struct iwl_statistics_ntfy_hdr { * @beacon_rssi_b: beacon RSSI on antenna B * @rx_bytes: RX byte count */ -struct iwl_statistics_ntfy_per_mac { +struct iwl_stats_ntfy_per_mac { __le32 beacon_filter_average_energy; __le32 air_time; __le32 beacon_counter; @@ -470,7 +556,7 @@ struct iwl_statistics_ntfy_per_mac { } __packed; /* STATISTICS_NTFY_PER_MAC_API_S_VER_1 */ #define IWL_STATS_MAX_BW_INDEX 5 -/** struct iwl_statistics_ntfy_per_phy +/** struct iwl_stats_ntfy_per_phy * @channel_load: channel load * @channel_load_by_us: device contribution to MCLM * @channel_load_not_by_us: other devices' contribution to MCLM @@ -485,7 +571,7 @@ struct iwl_statistics_ntfy_per_mac { * per channel BW. note BACK counted as 1 * @last_tx_ch_width_indx: last txed frame channel width index */ -struct iwl_statistics_ntfy_per_phy { +struct iwl_stats_ntfy_per_phy { __le32 channel_load; __le32 channel_load_by_us; __le32 channel_load_not_by_us; @@ -499,23 +585,62 @@ struct iwl_statistics_ntfy_per_phy { } __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */ /** - * struct iwl_statistics_ntfy_per_sta + * struct iwl_stats_ntfy_per_sta * * @average_energy: in fact it is minus the energy.. */ -struct iwl_statistics_ntfy_per_sta { +struct iwl_stats_ntfy_per_sta { __le32 average_energy; } __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */ -#define IWL_STATS_MAX_PHY_OPERTINAL 3 +#define IWL_STATS_MAX_PHY_OPERATIONAL 3 +#define IWL_STATS_MAX_FW_LINKS (IWL_MVM_FW_MAX_LINK_ID + 1) + +/** + * struct iwl_system_statistics_notif_oper + * + * @time_stamp: time when the notification is sent from firmware + * @per_link: per link statistics, &struct iwl_stats_ntfy_per_link + * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy + * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta + */ +struct iwl_system_statistics_notif_oper { + __le32 time_stamp; + struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS]; + struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; + struct iwl_stats_ntfy_per_sta per_sta[IWL_MVM_STATION_COUNT_MAX]; +} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ + +/** + * struct iwl_system_statistics_part1_notif_oper + * + * @time_stamp: time when the notification is sent from firmware + * @per_link: per link statistics &struct iwl_stats_ntfy_part1_per_link + * @per_phy_crc_error_stats: per phy crc error statistics + */ +struct iwl_system_statistics_part1_notif_oper { + __le32 time_stamp; + struct iwl_stats_ntfy_part1_per_link per_link[IWL_STATS_MAX_FW_LINKS]; + __le32 per_phy_crc_error_stats[IWL_STATS_MAX_PHY_OPERATIONAL]; +} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */ + +/** + * struct iwl_system_statistics_end_notif + * + * @time_stamp: time when the notification is sent from firmware + */ +struct iwl_system_statistics_end_notif { + __le32 time_stamp; +} __packed; /* STATISTICS_FW_NTFY_END_API_S_VER_1 */ + /** * struct iwl_statistics_operational_ntfy * * @hdr: general statistics header * @flags: bitmap of possible notification structures - * @per_mac_stats: per mac statistics, &struct iwl_statistics_ntfy_per_mac - * @per_phy_stats: per phy statistics, &struct iwl_statistics_ntfy_per_phy - * @per_sta_stats: per sta statistics, &struct iwl_statistics_ntfy_per_sta + * @per_mac: per mac statistics, &struct iwl_stats_ntfy_per_mac + * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy + * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta * @rx_time: rx time * @tx_time: usec the radio is transmitting. * @on_time_rf: The total time in usec the RF is awake. @@ -524,9 +649,9 @@ struct iwl_statistics_ntfy_per_sta { struct iwl_statistics_operational_ntfy { struct iwl_statistics_ntfy_hdr hdr; __le32 flags; - struct iwl_statistics_ntfy_per_mac per_mac_stats[MAC_INDEX_AUX]; - struct iwl_statistics_ntfy_per_phy per_phy_stats[IWL_STATS_MAX_PHY_OPERTINAL]; - struct iwl_statistics_ntfy_per_sta per_sta_stats[IWL_MVM_STATION_COUNT_MAX]; + struct iwl_stats_ntfy_per_mac per_mac[MAC_INDEX_AUX]; + struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; + struct iwl_stats_ntfy_per_sta per_sta[IWL_MVM_STATION_COUNT_MAX]; __le64 rx_time; __le64 tx_time; __le64 on_time_rf; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h index 7cc706731d70..2e15be71c957 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2020, 2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2020, 2022-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -335,6 +335,63 @@ struct iwl_hs20_roc_res { __le32 status; } __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ +/* + * Activity types for the ROC command + * @ROC_ACTIVITY_HOTSPOT: ROC for hs20 activity + * @ROC_ACTIVITY_P2P_DISC: ROC for p2p discoverability activity + * @ROC_ACTIVITY_P2P_TXRX: ROC for p2p action frames activity + */ +enum iwl_roc_activity { + ROC_ACTIVITY_HOTSPOT, + ROC_ACTIVITY_P2P_DISC, + ROC_ACTIVITY_P2P_TXRX, + ROC_NUM_ACTIVITIES +}; /* ROC_ACTIVITY_API_E_VER_1 */ + +/* + * ROC command + * + * Command requests the firmware to remain on a channel for a certain duration. + * + * ( MAC_CONF_GROUP 0x3, ROC_CMD 0xE ) + * + * @action: action to perform, see &enum iwl_ctxt_action + * @activity: type of activity, see &enum iwl_roc_activity + * @sta_id: station id, resumed during "Remain On Channel" activity. + * @channel_info: &struct iwl_fw_channel_info + * @node_addr: node MAC address for Rx filtering + * @reserved: align to a dword + * @max_delay: max delay the ROC can start in TU + * @duration: remain on channel duration in TU + */ +struct iwl_roc_req { + __le32 action; + __le32 activity; + __le32 sta_id; + struct iwl_fw_channel_info channel_info; + u8 node_addr[ETH_ALEN]; + __le16 reserved; + __le32 max_delay; + __le32 duration; +} __packed; /* ROC_CMD_API_S_VER_3 */ + +/* + * ROC notification + * + * Notification when ROC startes and when ROC ended. + * + * ( MAC_CONF_GROUP 0x3, ROC_NOTIF 0xf8 ) + * + * @status: true if ROC succeeded to start + * @start_end: true if ROC started, false if ROC ended + * @activity: notification to which activity - &enum iwl_roc_activity + */ +struct iwl_roc_notif { + __le32 success; + __le32 started; + __le32 activity; +} __packed; /* ROC_NOTIF_API_S_VER_1 */ + /** * enum iwl_mvm_session_prot_conf_id - session protection's configurations * @SESSION_PROTECT_CONF_ASSOC: Start a session protection for association. @@ -375,8 +432,8 @@ enum iwl_mvm_session_prot_conf_id { /** * struct iwl_mvm_session_prot_cmd - configure a session protection - * @id_and_color: the id and color of the mac for which this session protection - * is sent + * @id_and_color: the id and color of the link (or mac, for command version 1) + * for which this session protection is sent * @action: can be either FW_CTXT_ACTION_ADD or FW_CTXT_ACTION_REMOVE, * see &enum iwl_ctxt_action * @conf_id: see &enum iwl_mvm_session_prot_conf_id @@ -397,11 +454,15 @@ struct iwl_mvm_session_prot_cmd { __le32 duration_tu; __le32 repetition_count; __le32 interval; -} __packed; /* SESSION_PROTECTION_CMD_API_S_VER_1 */ +} __packed; +/* SESSION_PROTECTION_CMD_API_S_VER_1 and + * SESSION_PROTECTION_CMD_API_S_VER_2 + */ /** * struct iwl_mvm_session_prot_notif - session protection started / ended - * @mac_id: the mac id for which the session protection started / ended + * @mac_link_id: the mac id (or link id, for notif ver > 2) for which the + * session protection started / ended * @status: 1 means success, 0 means failure * @start: 1 means the session protection started, 0 means it ended * @conf_id: see &enum iwl_mvm_session_prot_conf_id @@ -410,10 +471,13 @@ struct iwl_mvm_session_prot_cmd { * and end even the firmware could not schedule it. */ struct iwl_mvm_session_prot_notif { - __le32 mac_id; + __le32 mac_link_id; __le32 status; __le32 start; __le32 conf_id; -} __packed; /* SESSION_PROTECTION_NOTIFICATION_API_S_VER_2 */ +} __packed; +/* SESSION_PROTECTION_NOTIFICATION_API_S_VER_2 and + * SESSION_PROTECTION_NOTIFICATION_API_S_VER_3 + */ #endif /* __iwl_fw_api_time_event_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e236c1d95e8e..3975a53a9f20 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1021,22 +1021,18 @@ struct iwl_dump_ini_region_data { struct iwl_fwrt_dump_data *dump_data; }; -static int -iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, - struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) +static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt, + void *range_ptr, u32 addr, + __le32 size) { - struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_error_dump_range *range = range_ptr; __le32 *val = range->data; u32 prph_val; - u32 addr = le32_to_cpu(reg->addrs[idx]) + - le32_to_cpu(reg->dev_addr.offset); int i; range->internal_base_addr = cpu_to_le32(addr); - range->range_data_size = reg->dev_addr.size; - for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { + range->range_data_size = size; + for (i = 0; i < le32_to_cpu(size); i += 4) { prph_val = iwl_read_prph(fwrt->trans, addr + i); if (iwl_trans_is_hw_error_value(prph_val)) return -EBUSY; @@ -1047,38 +1043,61 @@ iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, } static int -iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, +iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, void *range_ptr, u32 range_len, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 addr = le32_to_cpu(reg->addrs[idx]) + + le32_to_cpu(reg->dev_addr.offset); + + return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr, + reg->dev_addr.size); +} + +static int +iwl_dump_ini_prph_mac_block_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs; + u32 addr = le32_to_cpu(reg->dev_addr_range.offset) + + le32_to_cpu(pairs[idx].addr); + + return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr, + pairs[idx].size); +} + +static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, + void *range_ptr, u32 addr, + __le32 size, __le32 offset) +{ struct iwl_fw_ini_error_dump_range *range = range_ptr; __le32 *val = range->data; u32 indirect_wr_addr = WMAL_INDRCT_RD_CMD1; u32 indirect_rd_addr = WMAL_MRSPF_1; u32 prph_val; - u32 addr = le32_to_cpu(reg->addrs[idx]); u32 dphy_state; u32 dphy_addr; int i; range->internal_base_addr = cpu_to_le32(addr); - range->range_data_size = reg->dev_addr.size; + range->range_data_size = size; if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) indirect_wr_addr = WMAL_INDRCT_CMD1; - indirect_wr_addr += le32_to_cpu(reg->dev_addr.offset); - indirect_rd_addr += le32_to_cpu(reg->dev_addr.offset); + indirect_wr_addr += le32_to_cpu(offset); + indirect_rd_addr += le32_to_cpu(offset); if (!iwl_trans_grab_nic_access(fwrt->trans)) return -EBUSY; - dphy_addr = (reg->dev_addr.offset) ? WFPM_LMAC2_PS_CTL_RW : - WFPM_LMAC1_PS_CTL_RW; + dphy_addr = (offset) ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr); - for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { + for (i = 0; i < le32_to_cpu(size); i += 4) { if (dphy_state == HBUS_TIMEOUT || (dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) != WFPM_PHYRF_STATE_ON) { @@ -1097,6 +1116,33 @@ iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, return sizeof(*range) + le32_to_cpu(range->range_data_size); } +static int +iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + u32 addr = le32_to_cpu(reg->addrs[idx]); + + return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr, + reg->dev_addr.size, + reg->dev_addr.offset); +} + +static int +iwl_dump_ini_prph_phy_block_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs; + u32 addr = le32_to_cpu(pairs[idx].addr); + + return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr, + pairs[idx].size, + reg->dev_addr_range.offset); +} + static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, void *range_ptr, u32 range_len, int idx) @@ -1370,6 +1416,53 @@ out: return sizeof(*range) + le32_to_cpu(range->range_data_size); } +static int +iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data, + void *range_ptr, u32 range_len, int idx) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + struct iwl_fw_ini_error_dump_range *range = range_ptr; + __le32 *val = range->data; + __le32 offset = reg->dev_addr.offset; + u32 indirect_rd_wr_addr = DPHYIP_INDIRECT; + u32 addr = le32_to_cpu(reg->addrs[idx]); + u32 dphy_state, dphy_addr, prph_val; + int i; + + range->internal_base_addr = cpu_to_le32(addr); + range->range_data_size = reg->dev_addr.size; + + if (!iwl_trans_grab_nic_access(fwrt->trans)) + return -EBUSY; + + indirect_rd_wr_addr += le32_to_cpu(offset); + + dphy_addr = offset ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; + dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr); + + for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { + if (dphy_state == HBUS_TIMEOUT || + (dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) != + WFPM_PHYRF_STATE_ON) { + *val++ = cpu_to_le32(WFPM_DPHY_OFF); + continue; + } + + iwl_write_prph_no_grab(fwrt->trans, indirect_rd_wr_addr, + addr + i); + /* wait a bit for value to be ready in register */ + udelay(1); + prph_val = iwl_read_prph_no_grab(fwrt->trans, + indirect_rd_wr_addr); + *val++ = cpu_to_le32((prph_val & DPHYIP_INDIRECT_RD_MSK) >> + DPHYIP_INDIRECT_RD_SHIFT); + } + + iwl_trans_release_nic_access(fwrt->trans); + return sizeof(*range) + le32_to_cpu(range->range_data_size); +} + struct iwl_ini_rxf_data { u32 fifo_num; u32 size; @@ -1781,6 +1874,16 @@ static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, return iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); } +static u32 +iwl_dump_ini_mem_block_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + size_t size = sizeof(struct iwl_fw_ini_addr_size); + + return iwl_tlv_array_len_with_size(reg_data->reg_tlv, reg, size); +} + static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -1867,6 +1970,25 @@ static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, } static u32 +iwl_dump_ini_mem_block_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_dump_ini_region_data *reg_data) +{ + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs; + u32 ranges = iwl_dump_ini_mem_block_ranges(fwrt, reg_data); + u32 size = sizeof(struct iwl_fw_ini_error_dump); + int range; + + if (!ranges) + return 0; + + for (range = 0; range < ranges; range++) + size += le32_to_cpu(pairs[range].size); + + return size + ranges * sizeof(struct iwl_fw_ini_error_dump_range); +} + +static u32 iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { @@ -2413,6 +2535,18 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_phy_iter, }, + [IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE] = { + .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, + .get_size = iwl_dump_ini_mem_block_get_size, + .fill_mem_hdr = iwl_dump_ini_mem_fill_header, + .fill_range = iwl_dump_ini_prph_mac_block_iter, + }, + [IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE] = { + .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, + .get_size = iwl_dump_ini_mem_block_get_size, + .fill_mem_hdr = iwl_dump_ini_mem_fill_header, + .fill_range = iwl_dump_ini_prph_phy_block_iter, + }, [IWL_FW_INI_REGION_PERIPHERY_AUX] = {}, [IWL_FW_INI_REGION_PAGING] = { .fill_mem_hdr = iwl_dump_ini_mem_fill_header, @@ -2450,6 +2584,12 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .fill_mem_hdr = iwl_dump_ini_mon_dbgi_fill_header, .fill_range = iwl_dump_ini_dbgi_sram_iter, }, + [IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP] = { + .get_num_of_ranges = iwl_dump_ini_mem_ranges, + .get_size = iwl_dump_ini_mem_get_size, + .fill_mem_hdr = iwl_dump_ini_mem_fill_header, + .fill_range = iwl_dump_ini_prph_snps_dphyip_iter, + }, }; static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, @@ -2492,7 +2632,9 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops)) continue; - if (reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY && + if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY || + reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE || + reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) && tp_id != IWL_FW_INI_TIME_POINT_FW_ASSERT) { IWL_WARN(fwrt, "WRT: trying to collect phy prph at time point: %d, skipping\n", diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index b8d4a4d571e7..751a125a1566 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -141,7 +141,11 @@ static int iwl_dbgfs_enabled_severities_write(struct iwl_fw_runtime *fwrt, event_cfg.enabled_severities = cpu_to_le32(enabled_severities); - ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); + if (fwrt->ops && fwrt->ops->send_hcmd) + ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd); + else + ret = -EPERM; + IWL_INFO(fwrt, "sent host event cfg with enabled_severities: %u, ret: %d\n", enabled_severities, ret); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 7e0894ea1005..03f6e520145f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -975,4 +975,6 @@ static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv, _iwl_tlv_array_len((_tlv_ptr), sizeof(*(_struct_ptr)), \ sizeof(_struct_ptr->_memb[0])) +#define iwl_tlv_array_len_with_size(_tlv_ptr, _struct_ptr, _size) \ + _iwl_tlv_array_len((_tlv_ptr), sizeof(*(_struct_ptr)), _size) #endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 8d0d58d61892..96bda80632f3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -198,7 +198,7 @@ struct iwl_dump_exclude { struct iwl_fw { u32 ucode_ver; - char fw_version[64]; + char fw_version[128]; /* ucode images */ struct fw_img img[IWL_UCODE_TYPE_MAX]; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 702586945533..357727774db9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -98,6 +98,8 @@ struct iwl_txf_iter_data { * @cur_fw_img: current firmware image, must be maintained by * the driver by calling &iwl_fw_set_current_image() * @dump: debug dump data + * @uats_enabled: VLP or AFC AP is enabled + * @uats_table: AP type table */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -171,6 +173,8 @@ struct iwl_fw_runtime { struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; u8 reduced_power_flags; + bool uats_enabled; + struct iwl_uats_table_cmd uats_table; #endif }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 9877988db0d2..2964c5fb11e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -388,4 +388,54 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, kfree(data); } IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table); + +static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data, + struct iwl_fw_runtime *fwrt) +{ + if (uats_data->revision != 1) + return -EINVAL; + + memcpy(fwrt->uats_table.offset_map, uats_data->offset_map, + sizeof(fwrt->uats_table.offset_map)); + return 0; +} + +int iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_wlan_uats_data *data; + unsigned long package_size; + int ret; + + data = iwl_uefi_get_variable(IWL_UEFI_UATS_NAME, &IWL_EFI_VAR_GUID, + &package_size); + if (IS_ERR(data)) { + IWL_DEBUG_FW(trans, + "UATS UEFI variable not found 0x%lx\n", + PTR_ERR(data)); + return -EINVAL; + } + + if (package_size < sizeof(*data)) { + IWL_DEBUG_FW(trans, + "Invalid UATS table UEFI variable len (%lu)\n", + package_size); + kfree(data); + return -EINVAL; + } + + IWL_DEBUG_FW(trans, "Read UATS from UEFI with size %lu\n", + package_size); + + ret = iwl_uefi_uats_parse(data, fwrt); + if (ret < 0) { + IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n"); + kfree(data); + return ret; + } + + kfree(data); + return 0; +} +IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); #endif /* CONFIG_ACPI */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 1369cc4855c3..bf61a8df1225 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -9,8 +9,10 @@ #define IWL_UEFI_REDUCED_POWER_NAME L"UefiCnvWlanReducedPower" #define IWL_UEFI_SGOM_NAME L"UefiCnvWlanSarGeoOffsetMapping" #define IWL_UEFI_STEP_NAME L"UefiCnvCommonSTEP" +#define IWL_UEFI_UATS_NAME L"CnvUefiWlanUATS" #define IWL_SGOM_MAP_SIZE 339 +#define IWL_UATS_MAP_SIZE 339 struct pnvm_sku_package { u8 rev; @@ -25,6 +27,11 @@ struct uefi_cnv_wlan_sgom_data { u8 offset_map[IWL_SGOM_MAP_SIZE - 1]; } __packed; +struct uefi_cnv_wlan_uats_data { + u8 revision; + u8 offset_map[IWL_UATS_MAP_SIZE - 1]; +} __packed; + struct uefi_cnv_common_step_data { u8 revision; u8 step_mode; @@ -82,10 +89,20 @@ iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, #if defined(CONFIG_EFI) && defined(CONFIG_ACPI) void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt); #else static inline void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { } + +static inline +int iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt) +{ + return 0; +} + #endif #endif /* __iwl_fw_uefi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 90bebdf85c06..02ded22295c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -86,10 +86,7 @@ enum iwl_nvm_type { #define IWL_DEFAULT_MAX_TX_POWER 22 #define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\ NETIF_F_TSO | NETIF_F_TSO6) -#define IWL_TX_CSUM_NETIF_FLAGS_BZ (NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6) -#define IWL_CSUM_NETIF_FLAGS_MASK (IWL_TX_CSUM_NETIF_FLAGS | \ - IWL_TX_CSUM_NETIF_FLAGS_BZ | \ - NETIF_F_RXCSUM) +#define IWL_CSUM_NETIF_FLAGS_MASK (IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM) /* Antenna presence definitions */ #define ANT_NONE 0x0 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 3653a9fd9d8c..a4df67ff21ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2022 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -313,6 +313,7 @@ enum { SILICON_C_STEP, SILICON_D_STEP, SILICON_E_STEP, + SILICON_TC_STEP = 0xe, SILICON_Z_STEP = 0xf, }; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index 1455b578358b..01fb7b900a6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -3,17 +3,19 @@ * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. * Copyright(C) 2016 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018, 2023 Intel Corporation *****************************************************************************/ #ifndef __IWLWIFI_DEVICE_TRACE #include <linux/skbuff.h> #include <linux/ieee80211.h> #include <net/cfg80211.h> +#include <net/mac80211.h> #include "iwl-trans.h" #if !defined(__IWLWIFI_DEVICE_TRACE) static inline bool iwl_trace_data(struct sk_buff *skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; __le16 fc = hdr->frame_control; int offs = 24; /* start with normal header length */ @@ -21,6 +23,10 @@ static inline bool iwl_trace_data(struct sk_buff *skb) if (!ieee80211_is_data(fc)) return false; + /* If upper layers wanted TX status it's an important frame */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) + return false; + /* Try to determine if the frame is EAPOL. This might have false * positives (if there's no RFC 1042 header and we compare to some * payload instead) but since we're only doing tracing that's not diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index fb5e254757e7..ffe2670720c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -162,6 +162,8 @@ static inline char iwl_drv_get_step(int step) { if (step == SILICON_Z_STEP) return 'z'; + if (step == SILICON_TC_STEP) + return 'a'; return 'a' + step; } @@ -178,6 +180,8 @@ const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) mac_step = iwl_drv_get_step(trans->hw_rev_step); + rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->hw_rf_id)); + switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { case IWL_CFG_RF_TYPE_HR1: case IWL_CFG_RF_TYPE_HR2: @@ -196,7 +200,13 @@ const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) rf = "fm"; break; case IWL_CFG_RF_TYPE_WH: - rf = "wh"; + if (SILICON_Z_STEP == + CSR_HW_RFID_STEP(trans->hw_rf_id)) { + rf = "whtc"; + rf_step = 'a'; + } else { + rf = "wh"; + } break; default: return "unknown-rf"; @@ -204,8 +214,6 @@ const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) cdb = CSR_HW_RFID_IS_CDB(trans->hw_rf_id) ? "4" : ""; - rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->hw_rf_id)); - scnprintf(buf, FW_NAME_PRE_BUFSIZE, "iwlwifi-%s-%c0-%s%s-%c0", trans->cfg->fw_name_mac, mac_step, @@ -1303,10 +1311,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, 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); + if (!drv->trans->dbg.pc_data) + return -ENOMEM; + drv->trans->dbg.num_pc = + tlv_len / sizeof(struct iwl_pc_data); break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); @@ -1415,6 +1425,9 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) struct iwl_op_mode *op_mode = NULL; int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY; + /* also protects start/stop from racing against each other */ + lockdep_assert_held(&iwlwifi_opmode_table_mtx); + for (retry = 0; retry <= max_retry; retry++) { #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1429,6 +1442,9 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) if (op_mode) return op_mode; + if (test_bit(STATUS_TRANS_DEAD, &drv->trans->status)) + break; + IWL_ERR(drv, "retry init count %d\n", retry); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1442,6 +1458,9 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) static void _iwl_op_mode_stop(struct iwl_drv *drv) { + /* also protects start/stop from racing against each other */ + lockdep_assert_held(&iwlwifi_opmode_table_mtx); + /* op_mode can be NULL if its start failed */ if (drv->op_mode) { iwl_op_mode_stop(drv->op_mode); @@ -1725,11 +1744,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) } mutex_unlock(&iwlwifi_opmode_table_mtx); - /* - * Complete the firmware request last so that - * a driver unbind (stop) doesn't run while we - * are doing the start() above. - */ complete(&drv->request_firmware_complete); /* @@ -1834,11 +1848,12 @@ void iwl_drv_stop(struct iwl_drv *drv) { wait_for_completion(&drv->request_firmware_complete); + mutex_lock(&iwlwifi_opmode_table_mtx); + _iwl_op_mode_stop(drv); iwl_dealloc_ucode(drv); - mutex_lock(&iwlwifi_opmode_table_mtx); /* * List is empty (this item wasn't added) * when firmware loading failed -- in that diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 512af3605a2c..6015e1255d2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -671,7 +671,8 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 | + IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC, .phy_cap_info[0] = IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 6fad5b65a836..dd32c287b983 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -348,8 +348,8 @@ #define RFIC_REG_RD 0xAD0470 #define WFPM_CTRL_REG 0xA03030 #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_CFG1_IS_JACKET_BIT BIT(5) +#define WFPM_OTP_CFG1_IS_CDB_BIT BIT(4) #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) @@ -365,7 +365,6 @@ #define DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK 0x00000FFF enum { - ENABLE_WFPM = BIT(31), WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, }; @@ -383,7 +382,7 @@ enum { #define PREG_PRPH_WPROT_22000 0xA04D00 #define SB_MODIFY_CFG_FLAG 0xA03088 -#define SB_CFG_RESIDES_IN_OTP_MASK 0x10 +#define SB_CFG_RESIDES_IN_ROM 0x80 #define SB_CPU_1_STATUS 0xA01E30 #define SB_CPU_2_STATUS 0xA01E34 #define UMAG_SB_CPU_1_STATUS 0xA038C0 @@ -424,14 +423,14 @@ enum { * reserved: bits 12-18 * slave_exist: bit 19 * dash: bits 20-23 - * step: bits 24-26 - * flavor: bits 27-31 + * step: bits 24-27 + * flavor: bits 28-31 */ #define REG_CRF_ID_TYPE(val) (((val) & 0x00000FFF) >> 0) #define REG_CRF_ID_SLAVE(val) (((val) & 0x00080000) >> 19) #define REG_CRF_ID_DASH(val) (((val) & 0x00F00000) >> 20) -#define REG_CRF_ID_STEP(val) (((val) & 0x07000000) >> 24) -#define REG_CRF_ID_FLAVOR(val) (((val) & 0xF8000000) >> 27) +#define REG_CRF_ID_STEP(val) (((val) & 0x0F000000) >> 24) +#define REG_CRF_ID_FLAVOR(val) (((val) & 0xF0000000) >> 28) #define UREG_CHICK (0xA05C00) #define UREG_CHICK_MSI_ENABLE BIT(24) @@ -517,4 +516,8 @@ enum { #define WFPM_LMAC2_PD_NOTIFICATION 0xA033CC #define WFPM_LMAC2_PD_RE_READ BIT(31) +#define DPHYIP_INDIRECT 0xA2D800 +#define DPHYIP_INDIRECT_RD_MSK 0xFF000000 +#define DPHYIP_INDIRECT_RD_SHIFT 24 + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3d58a2b9518c..05e72a2125b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -279,7 +279,7 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) #define IWL_MGMT_TID 15 #define IWL_FRAME_LIMIT 64 #define IWL_MAX_RX_HW_QUEUES 16 -#define IWL_9000_MAX_RX_HW_QUEUES 6 +#define IWL_9000_MAX_RX_HW_QUEUES 1 /** * enum iwl_wowlan_status - WoWLAN image/device status @@ -589,7 +589,7 @@ struct iwl_trans_ops { int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_tx_cmd *dev_cmd, int queue); void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, - struct sk_buff_head *skbs); + struct sk_buff_head *skbs, bool is_flush); void (*set_q_ptrs)(struct iwl_trans *trans, int queue, int ptr); @@ -1274,14 +1274,15 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, } static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, - int ssn, struct sk_buff_head *skbs) + int ssn, struct sk_buff_head *skbs, + bool is_flush) { if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return; } - trans->ops->reclaim(trans, queue, ssn, skbs); + trans->ops->reclaim(trans, queue, ssn, skbs, is_flush); } static inline void iwl_trans_set_q_ptrs(struct iwl_trans *trans, int queue, diff --git a/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h b/drivers/net/wireless/intel/iwlwifi/mei/iwl-mei.h index 655d95d3a068..1f3c885aeb65 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 - 2022 Intel Corporation + * Copyright (C) 2021-2023 Intel Corporation */ #ifndef __iwl_mei_h__ @@ -493,7 +493,7 @@ static inline void iwl_mei_set_power_limit(__le16 *power_limit) static inline int iwl_mei_register(void *priv, const struct iwl_mei_ops *ops) -{ return 0; } +{ return -EOPNOTSUPP; } static inline void iwl_mei_start_unregister(void) {} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 59df2bf6327c..c832068b5718 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -119,5 +119,6 @@ #define IWL_MVM_DISABLE_AP_FILS false #define IWL_MVM_6GHZ_PASSIVE_SCAN_TIMEOUT 3000 /* in seconds */ #define IWL_MVM_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT 60 /* in seconds */ +#define IWL_MVM_AUTO_EML_ENABLE true #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index ffb1fdd7ee32..92c45571bd69 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -818,7 +818,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); - if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm)) + if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm, false)) IWL_ERR(mvm, "Failed to initialize D3 LAR information\n"); return 0; @@ -2031,6 +2031,16 @@ iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status, if (IS_ERR(key_config)) return false; ieee80211_set_key_rx_seq(key_config, 0, &seq); + + if (key_config->keyidx == 4 || key_config->keyidx == 5) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct iwl_mvm_vif_link_info *mvm_link = + mvmvif->link[link_id]; + + mvm_link->igtk = key_config; + } + return true; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index cb4ecad6103f..e8b881596baf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -699,19 +699,11 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32); MVM_DEBUGFS_READ_FILE_OPS(os_device_timediff); - -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +void iwl_mvm_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct dentry *dbgfs_dir = vif->debugfs_dir; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[100]; - - /* - * Check if debugfs directory already exist before creating it. - * This may happen when, for example, resetting hw or suspend-resume - */ - if (!dbgfs_dir || mvmvif->dbgfs_dir) - return; mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); if (IS_ERR_OR_NULL(mvmvif->dbgfs_dir)) { @@ -737,6 +729,17 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && mvmvif == mvm->bf_allowed_vif) MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 0600); +} + +void iwl_mvm_vif_dbgfs_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct dentry *dbgfs_dir = vif->debugfs_dir; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[100]; + + /* this will happen in monitor mode */ + if (!dbgfs_dir) + return; /* * Create symlink for convenience pointing to interface specific @@ -745,21 +748,62 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * find * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ */ - snprintf(buf, 100, "../../../%pd3/%pd", - dbgfs_dir, - mvmvif->dbgfs_dir); + snprintf(buf, 100, "../../../%pd3/iwlmvm", dbgfs_dir); mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, mvm->debugfs_dir, buf); } -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +void iwl_mvm_vif_dbgfs_rm_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); debugfs_remove(mvmvif->dbgfs_slink); mvmvif->dbgfs_slink = NULL; +} + +#define MVM_DEBUGFS_WRITE_LINK_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, \ + struct ieee80211_bss_conf) +#define MVM_DEBUGFS_READ_WRITE_LINK_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(link_##name, bufsz, \ + struct ieee80211_bss_conf) +#define MVM_DEBUGFS_ADD_LINK_FILE(name, parent, mode) \ + debugfs_create_file(#name, mode, parent, link_conf, \ + &iwl_dbgfs_link_##name##_ops) + +static void iwl_mvm_debugfs_add_link_files(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *mvm_dir) +{ + /* Add per-link files here*/ +} + +void iwl_mvm_link_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + unsigned int link_id = link_conf->link_id; + struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id]; + struct dentry *mvm_dir; + + if (WARN_ON(!link_info) || !dir) + return; + + if (dir == vif->debugfs_dir) { + WARN_ON(!mvmvif->dbgfs_dir); + mvm_dir = mvmvif->dbgfs_dir; + } else { + mvm_dir = debugfs_create_dir("iwlmvm", dir); + if (IS_ERR_OR_NULL(mvm_dir)) { + IWL_ERR(mvm, "Failed to create debugfs directory under %pd\n", + dir); + return; + } + } - debugfs_remove_recursive(mvmvif->dbgfs_dir); - mvmvif->dbgfs_dir = NULL; + iwl_mvm_debugfs_add_link_files(vif, link_conf, mvm_dir); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index cf27f106d4d5..329c545f65fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -50,8 +50,18 @@ static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { int ret; + bool force; - if (!iwl_mvm_is_ctdp_supported(mvm)) + if (!kstrtobool(buf, &force)) + IWL_DEBUG_INFO(mvm, + "force start is %d [0=disabled, 1=enabled]\n", + force); + + /* we allow skipping cap support check and force stop ctdp + * statistics collection and with guerantee that it is + * safe to use. + */ + if (!force && !iwl_mvm_is_ctdp_supported(mvm)) return -EOPNOTSUPP; if (!iwl_mvm_firmware_running(mvm) || @@ -65,6 +75,36 @@ static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, return ret ?: count; } +static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int ret; + bool force; + + if (!kstrtobool(buf, &force)) + IWL_DEBUG_INFO(mvm, + "force start is %d [0=disabled, 1=enabled]\n", + force); + + /* we allow skipping cap support check and force enable ctdp + * for statistics collection and with guerantee that it is + * safe to use. + */ + if (!force && !iwl_mvm_is_ctdp_supported(mvm)) + return -EOPNOTSUPP; + + if (!iwl_mvm_firmware_running(mvm) || + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) + return -EIO; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, 0); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + static ssize_t iwl_dbgfs_force_ctkill_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { @@ -965,6 +1005,13 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, char *buf; int ret; size_t bufsz; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(SYSTEM_GROUP, + SYSTEM_STATISTICS_CMD), + IWL_FW_CMD_VER_UNKNOWN); + + if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) + return -EOPNOTSUPP; if (iwl_mvm_has_new_rx_stats_api(mvm)) bufsz = ((sizeof(struct mvm_statistics_rx) / @@ -1144,6 +1191,101 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, } #undef PRINT_STAT_LE32 +static ssize_t iwl_dbgfs_fw_system_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buff, *pos, *endpos; + int ret; + size_t bufsz; + int i; + struct iwl_mvm_vif *mvmvif; + struct ieee80211_vif *vif; + struct iwl_mvm *mvm = file->private_data; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(SYSTEM_GROUP, + SYSTEM_STATISTICS_CMD), + IWL_FW_CMD_VER_UNKNOWN); + + /* in case of a wrong cmd version, allocate buffer only for error msg */ + bufsz = (cmd_ver == 1) ? 4096 : 64; + + buff = kzalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + pos = buff; + endpos = pos + bufsz; + + if (cmd_ver != 1) { + pos += scnprintf(pos, endpos - pos, + "System stats not supported:%d\n", cmd_ver); + goto send_out; + } + + mutex_lock(&mvm->mutex); + if (iwl_mvm_firmware_running(mvm)) + iwl_mvm_request_statistics(mvm, false); + + for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) { + vif = iwl_mvm_rcu_dereference_vif_id(mvm, i, false); + if (!vif) + continue; + + if (vif->type == NL80211_IFTYPE_STATION) + break; + } + + if (i == NUM_MAC_INDEX_DRIVER || !vif) { + pos += scnprintf(pos, endpos - pos, "vif is NULL\n"); + goto release_send_out; + } + + mvmvif = iwl_mvm_vif_from_mac80211(vif); + if (!mvmvif) { + pos += scnprintf(pos, endpos - pos, "mvmvif is NULL\n"); + goto release_send_out; + } + + for_each_mvm_vif_valid_link(mvmvif, i) { + struct iwl_mvm_vif_link_info *link_info = mvmvif->link[i]; + + pos += scnprintf(pos, endpos - pos, + "link_id %d", i); + pos += scnprintf(pos, endpos - pos, + " num_beacons %d", + link_info->beacon_stats.num_beacons); + pos += scnprintf(pos, endpos - pos, + " accu_num_beacons %d", + link_info->beacon_stats.accu_num_beacons); + pos += scnprintf(pos, endpos - pos, + " avg_signal %d\n", + link_info->beacon_stats.avg_signal); + } + + pos += scnprintf(pos, endpos - pos, + "radio_stats.rx_time %lld\n", + mvm->radio_stats.rx_time); + pos += scnprintf(pos, endpos - pos, + "radio_stats.tx_time %lld\n", + mvm->radio_stats.tx_time); + pos += scnprintf(pos, endpos - pos, + "accu_radio_stats.rx_time %lld\n", + mvm->accu_radio_stats.rx_time); + pos += scnprintf(pos, endpos - pos, + "accu_radio_stats.tx_time %lld\n", + mvm->accu_radio_stats.tx_time); + +release_send_out: + mutex_unlock(&mvm->mutex); + +send_out: + ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); + kfree(buff); + + return ret; +} + static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, char __user *user_buf, size_t count, loff_t *ppos, @@ -1998,6 +2140,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget); MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(start_ctdp, 8); MVM_DEBUGFS_WRITE_FILE_OPS(force_ctkill, 8); MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); @@ -2012,6 +2155,7 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); +MVM_DEBUGFS_READ_FILE_OPS(fw_system_stats); MVM_DEBUGFS_READ_FILE_OPS(fw_ver); MVM_DEBUGFS_READ_FILE_OPS(phy_integration_ver); MVM_DEBUGFS_READ_FILE_OPS(tas_get_status); @@ -2210,6 +2354,7 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm) MVM_DEBUGFS_ADD_FILE(nic_temp, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(ctdp_budget, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(stop_ctdp, mvm->debugfs_dir, 0200); + MVM_DEBUGFS_ADD_FILE(start_ctdp, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(force_ctkill, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(stations, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(bt_notif, mvm->debugfs_dir, 0400); @@ -2218,6 +2363,7 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm) MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, 0400); + MVM_DEBUGFS_ADD_FILE(fw_system_stats, mvm->debugfs_dir, 0400); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, 0200); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h index 0711ab689c48..cc2c45b45ddc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* + * Copyright (C) 2023 Intel Corporation * Copyright (C) 2012-2014 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index b49781d1a07a..10b9219b3bfd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #include <net/cfg80211.h> #include <linux/etherdevice.h> @@ -302,7 +302,12 @@ static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm, struct iwl_mvm_pasn_sta *sta) { list_del(&sta->list); - iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id); + + if (iwl_mvm_has_mld_api(mvm->fw)) + iwl_mvm_mld_rm_sta_id(mvm, sta->int_sta.sta_id); + else + iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id); + iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta); kfree(sta); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index a5348b015310..403bd17b8b7a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -15,6 +15,7 @@ #include "iwl-prph.h" #include "fw/acpi.h" #include "fw/pnvm.h" +#include "fw/uefi.h" #include "mvm.h" #include "fw/dbg.h" @@ -23,12 +24,15 @@ #include "iwl-nvm-parse.h" #include "time-sync.h" -#define MVM_UCODE_ALIVE_TIMEOUT (HZ) +#define MVM_UCODE_ALIVE_TIMEOUT (2 * HZ) #define MVM_UCODE_CALIB_TIMEOUT (2 * HZ) #define IWL_TAS_US_MCC 0x5553 #define IWL_TAS_CANADA_MCC 0x4341 +#define IWL_UATS_VLP_AP_SUPPORTED BIT(29) +#define IWL_UATS_AFC_AP_SUPPORTED BIT(30) + struct iwl_mvm_alive_data { bool valid; u32 scd_base_addr; @@ -487,6 +491,52 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, } #if defined(CONFIG_ACPI) && defined(CONFIG_EFI) +static void iwl_mvm_uats_init(struct iwl_mvm *mvm) +{ + u8 cmd_ver; + int ret; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + UATS_TABLE_CMD), + .flags = 0, + .data[0] = &mvm->fwrt.uats_table, + .len[0] = sizeof(mvm->fwrt.uats_table), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!(mvm->trans->trans_cfg->device_family >= + IWL_DEVICE_FAMILY_AX210)) { + IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n"); + return; + } + + if (!mvm->fwrt.uats_enabled) { + IWL_DEBUG_RADIO(mvm, "UATS feature is disabled\n"); + return; + } + + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, + IWL_FW_CMD_VER_UNKNOWN); + if (cmd_ver != 1) { + IWL_DEBUG_RADIO(mvm, + "UATS_TABLE_CMD ver %d not supported\n", + cmd_ver); + return; + } + + ret = iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt); + if (ret < 0) { + IWL_ERR(mvm, "failed to read UATS table (%d)\n", ret); + return; + } + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret < 0) + IWL_ERR(mvm, "failed to send UATS_TABLE_CMD (%d)\n", ret); + else + IWL_DEBUG_RADIO(mvm, "UATS_TABLE_CMD sent to FW\n"); +} + static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) { u8 cmd_ver; @@ -526,6 +576,10 @@ static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) { return 0; } + +static void iwl_mvm_uats_init(struct iwl_mvm *mvm) +{ +} #endif static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) @@ -596,7 +650,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); /* if needed, we'll reset this on our way out later */ - mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK); + mvm->pldr_sync = sb_cfg == SB_CFG_RESIDES_IN_ROM; if (mvm->pldr_sync && iwl_mei_pldr_req()) return -EBUSY; } @@ -1101,6 +1155,12 @@ static const struct dmi_system_id dmi_tas_approved_list[] = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), }, }, + { .ident = "GOOGLE-HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + }, + }, { .ident = "MSI", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), @@ -1226,7 +1286,10 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { int ret; u32 value; - struct iwl_lari_config_change_cmd_v6 cmd = {}; + struct iwl_lari_config_change_cmd_v7 cmd = {}; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), 1); cmd.config_bitmap = iwl_acpi_get_lari_config_bitmap(&mvm->fwrt); @@ -1244,8 +1307,11 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, DSM_FUNC_ACTIVATE_CHANNEL, &iwl_guid, &value); - if (!ret) + if (!ret) { + if (cmd_ver < 8) + value &= ~ACTIVATE_5G2_IN_WW_MASK; cmd.chan_state_active_bitmap = cpu_to_le32(value); + } ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, DSM_FUNC_ENABLE_6E, @@ -1259,18 +1325,26 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) if (!ret) cmd.force_disable_channels_bitmap = cpu_to_le32(value); + ret = iwl_acpi_get_dsm_u32(mvm->fwrt.dev, 0, + DSM_FUNC_ENERGY_DETECTION_THRESHOLD, + &iwl_guid, &value); + if (!ret) + cmd.edt_bitmap = cpu_to_le32(value); + if (cmd.config_bitmap || cmd.oem_uhb_allow_bitmap || cmd.oem_11ax_allow_bitmap || cmd.oem_unii4_allow_bitmap || cmd.chan_state_active_bitmap || - cmd.force_disable_channels_bitmap) { + cmd.force_disable_channels_bitmap || + cmd.edt_bitmap) { size_t cmd_size; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - WIDE_ID(REGULATORY_AND_NVM_GROUP, - LARI_CONFIG_CHANGE), - 1); + switch (cmd_ver) { + case 8: + case 7: + cmd_size = sizeof(struct iwl_lari_config_change_cmd_v7); + break; case 6: cmd_size = sizeof(struct iwl_lari_config_change_cmd_v6); break; @@ -1304,6 +1378,9 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", le32_to_cpu(cmd.oem_uhb_allow_bitmap), le32_to_cpu(cmd.force_disable_channels_bitmap)); + IWL_DEBUG_RADIO(mvm, + "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x\n", + le32_to_cpu(cmd.edt_bitmap)); ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, LARI_CONFIG_CHANGE), @@ -1313,6 +1390,10 @@ static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) "Failed to send LARI_CONFIG_CHANGE (%d)\n", ret); } + + if (le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_VLP_AP_SUPPORTED || + le32_to_cpu(cmd.oem_uhb_allow_bitmap) & IWL_UATS_AFC_AP_SUPPORTED) + mvm->fwrt.uats_enabled = TRUE; } void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) @@ -1516,8 +1597,6 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; struct ieee80211_supported_band *sband = NULL; lockdep_assert_held(&mvm->mutex); @@ -1642,21 +1721,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } - chan = &sband->channels[0]; - - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - for (i = 0; i < NUM_PHY_CTX; i++) { - /* - * The channel used here isn't relevant as it's - * going to be overwritten in the other flows. - * For now use the first channel we have. - */ - ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], - &chandef, 1, 1); - if (ret) - goto error; - } - if (iwl_mvm_is_tt_in_fw(mvm)) { /* in order to give the responsibility of ct-kill and * TX backoff to FW we need to send empty temperature reporting @@ -1739,6 +1803,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_mvm_tas_init(mvm); iwl_mvm_leds_sync(mvm); + iwl_mvm_uats_init(mvm); if (iwl_rfi_supported(mvm)) { if (iwl_mvm_eval_dsm_rfi(mvm) == DSM_VALUE_RFI_ENABLE) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c index ace82e2c5bd9..be48b0fc9cb6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -53,7 +53,6 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *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; @@ -61,7 +60,7 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 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) + if (link_info->fw_link_id >= ARRAY_SIZE(mvm->link_id_to_link_conf)) return -EINVAL; rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id], @@ -77,12 +76,8 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd.link_id = cpu_to_le32(link_info->fw_link_id); cmd.mac_id = cpu_to_le32(mvmvif->id); cmd.spec_link_id = link_conf->link_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); + WARN_ON_ONCE(link_info->phy_ctxt); + cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN); @@ -194,11 +189,14 @@ int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 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; + if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) { + if (iwlwifi_mod_params.disable_11be || + !link_conf->eht_support) + changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + else + cmd.puncture_mask = + cpu_to_le16(link_conf->eht_puncturing); + } cmd.bss_color = link_conf->he_bss_color.color; @@ -244,9 +242,10 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_link_config_cmd cmd = {}; int ret; + /* mac80211 thought we have the link, but it was never configured */ if (WARN_ON(!link_info || - link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) - return -EINVAL; + link_info->fw_link_id >= ARRAY_SIZE(mvm->link_id_to_link_conf))) + return 0; RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id], NULL); @@ -254,6 +253,7 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_release_fw_link_id(mvm, link_info->fw_link_id); link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; cmd.spec_link_id = link_conf->link_id; + cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID); ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 06bbd6212df0..c4f96125cf33 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -286,6 +286,10 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) INIT_LIST_HEAD(&mvmvif->time_event_data.list); mvmvif->time_event_data.id = TE_MAX; + 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; + /* No need to allocate data queues to P2P Device MAC and NAN.*/ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return 0; @@ -300,10 +304,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->deflink.cab_queue = IWL_MVM_DQA_GCAST_QUEUE; } - 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->deflink.smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 6fc5b3f22746..a64600f0ed9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -186,7 +186,7 @@ struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, MCC_SOURCE_OLD_FW, changed); } -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm, bool force_regd_sync) { enum iwl_mcc_source used_src; struct ieee80211_regdomain *regd; @@ -213,8 +213,10 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) if (IS_ERR_OR_NULL(regd)) return -EIO; - /* update cfg80211 if the regdomain was changed */ - if (changed) + /* update cfg80211 if the regdomain was changed or the caller explicitly + * asked to update regdomain + */ + if (changed || force_regd_sync) ret = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd); else ret = 0; @@ -376,7 +378,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, HAS_RATE_CONTROL); } - if (iwl_mvm_has_new_rx_api(mvm)) + /* We want to use the mac80211's reorder buffer for 9000 */ + if (iwl_mvm_has_new_rx_api(mvm) && + mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_9000) ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); if (fw_has_capa(&mvm->fw->ucode_capa, @@ -1535,6 +1539,7 @@ static int iwl_mvm_mac_add_interface(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; + int i; mutex_lock(&mvm->mutex); @@ -1551,8 +1556,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, /* 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; + for_each_mvm_vif_valid_link(mvmvif, i) + mvmvif->link[i]->beacon_stats.accu_num_beacons += + mvmvif->link[i]->beacon_stats.num_beacons; /* Allocate resources for the MAC context, and add it to the fw */ ret = iwl_mvm_mac_ctxt_init(mvm, vif); @@ -1580,7 +1586,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { - iwl_mvm_vif_dbgfs_register(mvm, vif); + iwl_mvm_vif_dbgfs_add_link(mvm, vif); ret = 0; goto out; } @@ -1607,32 +1613,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, 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_binding_add_vif(mvm, vif); - if (ret) - goto out_unref_phy; - - ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif); - if (ret) - goto out_unbind; - - /* 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 */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) mvm->p2p_device_vif = vif; - } iwl_mvm_tcm_add_vif(mvm, vif); INIT_DELAYED_WORK(&mvmvif->csa_work, @@ -1644,7 +1626,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chandef); } - iwl_mvm_vif_dbgfs_register(mvm, vif); + iwl_mvm_vif_dbgfs_add_link(mvm, vif); if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && vif->type == NL80211_IFTYPE_STATION && !vif->p2p && @@ -1661,16 +1643,6 @@ out: goto out_unlock; - out_unbind: - iwl_mvm_binding_remove_vif(mvm, vif); - 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; iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -1732,7 +1704,7 @@ static bool iwl_mvm_mac_remove_interface_common(struct ieee80211_hw *hw, if (vif->bss_conf.ftm_responder) memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats)); - iwl_mvm_vif_dbgfs_clean(mvm, vif); + iwl_mvm_vif_dbgfs_rm_link(mvm, vif); /* * For AP/GO interface, the tear down of the resources allocated to the @@ -1762,12 +1734,17 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, if (iwl_mvm_mac_remove_interface_common(hw, vif)) goto out; + /* Before the interface removal, mac80211 would cancel the ROC, and the + * ROC worker would be scheduled if needed. The worker would be flushed + * in iwl_mvm_prepare_mac_removal() and thus at this point there is no + * binding etc. so nothing needs to be done here. + */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + if (mvmvif->deflink.phy_ctxt) { + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + mvmvif->deflink.phy_ctxt = NULL; + } 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->deflink.phy_ctxt); - mvmvif->deflink.phy_ctxt = NULL; } iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -2491,7 +2468,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, } void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 duration_override) + u32 duration_override, unsigned int link_id) { u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS; @@ -2511,7 +2488,8 @@ void iwl_mvm_protect_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) iwl_mvm_schedule_session_protection(mvm, vif, 900, - min_duration, false); + min_duration, false, + link_id); else iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); @@ -2605,6 +2583,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; + int i; /* * Re-calculate the tsf id, as the leader-follower relations depend @@ -2651,8 +2630,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (vif->cfg.assoc) { /* clear statistics to get clean beacon counter */ iwl_mvm_request_statistics(mvm, true); - memset(&mvmvif->deflink.beacon_stats, 0, - sizeof(mvmvif->deflink.beacon_stats)); + for_each_mvm_vif_valid_link(mvmvif, i) + memset(&mvmvif->link[i]->beacon_stats, 0, + sizeof(mvmvif->link[i]->beacon_stats)); /* add quota for this interface */ ret = iwl_mvm_update_quotas(mvm, true, NULL); @@ -2699,7 +2679,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, * time could be small without us having heard * a beacon yet. */ - iwl_mvm_protect_assoc(mvm, vif, 0); + iwl_mvm_protect_assoc(mvm, vif, 0, 0); } iwl_mvm_sf_update(mvm, vif, false); @@ -3066,22 +3046,6 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *bss_conf, u64 changes) { - static const 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, - const struct iwl_mvm_bss_info_changed_ops *callbacks, - u64 changes) -{ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); @@ -3091,12 +3055,11 @@ iwl_mvm_bss_info_changed_common(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_STATION: - callbacks->bss_info_changed_sta(mvm, vif, bss_conf, changes); + iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: - callbacks->bss_info_changed_ap_ibss(mvm, vif, bss_conf, - changes); + iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); break; case NL80211_IFTYPE_MONITOR: if (changes & BSS_CHANGED_MU_GROUPS) @@ -3803,12 +3766,25 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm, callbacks->mac_ctxt_changed(mvm, vif, false); iwl_mvm_mei_host_associated(mvm, vif, mvm_sta); + + /* when client is authorized (AP station marked as such), + * try to enable more links + */ + if (vif->type == NL80211_IFTYPE_STATION && + !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_mld_select_links(mvm, vif, false); } mvm_sta->authorized = true; iwl_mvm_rs_rate_init_all_links(mvm, vif, sta); + /* MFP is set by default before the station is authorized. + * Clear it here in case it's not used. + */ + if (!sta->mfp) + return callbacks->update_sta(mvm, vif, sta); + return 0; } @@ -4042,7 +4018,7 @@ void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); - iwl_mvm_protect_assoc(mvm, vif, info->duration); + iwl_mvm_protect_assoc(mvm, vif, info->duration, info->link_id); mutex_unlock(&mvm->mutex); } @@ -4179,12 +4155,21 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, * GTK on AP interface is a TX-only key, return 0; * 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. + * CMAC/GMAC in AP/IBSS modes must be done in software + * on older NICs. * * Except, of course, beacon protection - it must be - * offloaded since we just set a beacon template. + * offloaded since we just set a beacon template, and + * then we must also offload the IGTK (not just BIGTK) + * for firmware reasons. + * + * So just check for beacon protection - if we don't + * have it we cannot get here with keyidx >= 6, and + * if we do have it we need to send the key to FW in + * all cases (CMAC/GMAC). */ - if (keyidx < 6 && + if (!wiphy_ext_feature_isset(hw->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION) && (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)) { @@ -4413,6 +4398,39 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, #define AUX_ROC_MAX_DELAY MSEC_TO_TU(600) #define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20) #define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10) + +static void iwl_mvm_roc_duration_and_delay(struct ieee80211_vif *vif, + u32 duration_ms, + u32 *duration_tu, + u32 *delay) +{ + u32 dtim_interval = vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int; + + *delay = AUX_ROC_MIN_DELAY; + *duration_tu = MSEC_TO_TU(duration_ms); + + /* + * If we are associated we want the delay time to be at least one + * dtim interval so that the FW can wait until after the DTIM and + * then start the time event, this will potentially allow us to + * remain off-channel for the max duration. + * Since we want to use almost a whole dtim interval we would also + * like the delay to be for 2-3 dtim intervals, in case there are + * other time events with higher priority. + */ + if (vif->cfg.assoc) { + *delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY); + /* We cannot remain off-channel longer than the DTIM interval */ + if (dtim_interval <= *duration_tu) { + *duration_tu = dtim_interval - AUX_ROC_SAFETY_BUFFER; + if (*duration_tu <= AUX_ROC_MIN_DURATION) + *duration_tu = dtim_interval - + AUX_ROC_MIN_SAFETY_BUFFER; + } + } +} + static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, struct ieee80211_channel *channel, struct ieee80211_vif *vif, @@ -4423,8 +4441,6 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; static const u16 time_event_response[] = { HOT_SPOT_CMD }; struct iwl_notification_wait wait_time_event; - u32 dtim_interval = vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int; u32 req_dur, delay; struct iwl_hs20_roc_req aux_roc_req = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), @@ -4445,29 +4461,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, /* Set the time and duration */ tail->apply_time = cpu_to_le32(iwl_mvm_get_systime(mvm)); - delay = AUX_ROC_MIN_DELAY; - req_dur = MSEC_TO_TU(duration); - - /* - * If we are associated we want the delay time to be at least one - * dtim interval so that the FW can wait until after the DTIM and - * then start the time event, this will potentially allow us to - * remain off-channel for the max duration. - * Since we want to use almost a whole dtim interval we would also - * like the delay to be for 2-3 dtim intervals, in case there are - * other time events with higher priority. - */ - if (vif->cfg.assoc) { - delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY); - /* We cannot remain off-channel longer than the DTIM interval */ - if (dtim_interval <= req_dur) { - req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER; - if (req_dur <= AUX_ROC_MIN_DURATION) - req_dur = dtim_interval - - AUX_ROC_MIN_SAFETY_BUFFER; - } - } - + iwl_mvm_roc_duration_and_delay(vif, duration, &req_dur, &delay); tail->duration = cpu_to_le32(req_dur); tail->apply_time_max_delay = cpu_to_le32(delay); @@ -4475,8 +4469,8 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, "ROC: Requesting to remain on channel %u for %ums\n", channel->hw_value, req_dur); IWL_DEBUG_TE(mvm, - "\t(requested = %ums, max_delay = %ums, dtim_interval = %ums)\n", - duration, delay, dtim_interval); + "\t(requested = %ums, max_delay = %ums)\n", + duration, delay); /* Set the node address */ memcpy(tail->node_addr, vif->addr, ETH_ALEN); @@ -4534,6 +4528,48 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, return res; } +static int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, + struct ieee80211_channel *channel, + struct ieee80211_vif *vif, + int duration, u32 activity) +{ + int res; + u32 duration_tu, delay; + struct iwl_roc_req roc_req = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .activity = cpu_to_le32(activity), + .sta_id = cpu_to_le32(mvm->aux_sta.sta_id), + }; + + lockdep_assert_held(&mvm->mutex); + + /* Set the channel info data */ + iwl_mvm_set_chan_info(mvm, &roc_req.channel_info, + channel->hw_value, + iwl_mvm_phy_band_from_nl80211(channel->band), + IWL_PHY_CHANNEL_MODE20, 0); + + iwl_mvm_roc_duration_and_delay(vif, duration, &duration_tu, + &delay); + roc_req.duration = cpu_to_le32(duration_tu); + roc_req.max_delay = cpu_to_le32(delay); + + IWL_DEBUG_TE(mvm, + "\t(requested = %ums, max_delay = %ums)\n", + duration, delay); + IWL_DEBUG_TE(mvm, + "Requesting to remain on channel %u for %utu\n", + channel->hw_value, duration_tu); + + /* Set the node address */ + memcpy(roc_req.node_addr, vif->addr, ETH_ALEN); + + res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + 0, sizeof(roc_req), &roc_req); + + return res; +} + static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) { int ret = 0; @@ -4554,30 +4590,20 @@ static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) 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) +static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret = 0; + int ret; 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")) + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding 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; + /* The station and queue allocation must be done only after the binding + * is done, as otherwise the FW might incorrectly configure its state. + */ + return iwl_mvm_add_p2p_bcast_sta(mvm, vif); } static int iwl_mvm_roc(struct ieee80211_hw *hw, @@ -4588,12 +4614,81 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, { static const 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, + .link = iwl_mvm_roc_link, }; return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); } +static int iwl_mvm_roc_station(struct iwl_mvm *mvm, + struct ieee80211_channel *channel, + struct ieee80211_vif *vif, + int duration) +{ + int ret; + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, ROC_CMD); + u8 fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + if (fw_ver == IWL_FW_CMD_VER_UNKNOWN) { + ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, vif, duration); + } else if (fw_ver == 3) { + ret = iwl_mvm_roc_add_cmd(mvm, channel, vif, duration, + ROC_ACTIVITY_HOTSPOT); + } else { + ret = -EOPNOTSUPP; + IWL_ERR(mvm, "ROC command version %d mismatch!\n", fw_ver); + } + + return ret; +} + +static int iwl_mvm_p2p_find_phy_ctxt(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_channel *channel) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct cfg80211_chan_def chandef; + int i; + + lockdep_assert_held(&mvm->mutex); + + if (mvmvif->deflink.phy_ctxt && + channel == mvmvif->deflink.phy_ctxt->channel) + return 0; + + /* Try using a PHY context that is already in use */ + for (i = 0; i < NUM_PHY_CTX; i++) { + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[i]; + + if (!phy_ctxt->ref || mvmvif->deflink.phy_ctxt == phy_ctxt) + continue; + + if (channel == phy_ctxt->channel) { + if (mvmvif->deflink.phy_ctxt) + iwl_mvm_phy_ctxt_unref(mvm, + mvmvif->deflink.phy_ctxt); + + mvmvif->deflink.phy_ctxt = phy_ctxt; + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt); + return 0; + } + } + + /* We already have a phy_ctxt, but it's not on the right channel */ + if (mvmvif->deflink.phy_ctxt) + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + + mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->deflink.phy_ctxt) + return -ENOSPC; + + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); + + return iwl_mvm_phy_ctxt_add(mvm, mvmvif->deflink.phy_ctxt, + &chandef, 1, 1); +} + /* 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, @@ -4601,12 +4696,8 @@ int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const 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; + int ret; IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, duration, type); @@ -4626,89 +4717,27 @@ int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* 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); + ret = iwl_mvm_roc_station(mvm, channel, vif, duration); goto out_unlock; case NL80211_IFTYPE_P2P_DEVICE: /* handle below */ break; default: - IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type); + IWL_ERR(mvm, "ROC: Invalid vif type=%u\n", vif->type); ret = -EINVAL; goto out_unlock; } - for (i = 0; i < NUM_PHY_CTX; i++) { - phy_ctxt = &mvm->phy_ctxts[i]; - if (phy_ctxt->ref == 0 || mvmvif->deflink.phy_ctxt == phy_ctxt) - continue; - - if (phy_ctxt->ref && channel == phy_ctxt->channel) { - ret = ops->switch_phy_ctxt(mvm, vif, phy_ctxt); - if (ret) - goto out_unlock; - - 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->deflink.phy_ctxt->channel) - goto schedule_time_event; - - cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - - /* - * Check if the remain-on-channel is on a different band and that - * requires context removal, see iwl_mvm_phy_ctxt_changed(). If - * so, we'll need to release and then re-configure here, since we - * must not remove a PHY context that's part of a binding. - */ - band_change_removal = - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) && - mvmvif->deflink.phy_ctxt->channel->band != chandef.chan->band; - 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->deflink.phy_ctxt, - &chandef, 1, 1); - if (ret) - goto out_unlock; - } else { - /* - * The PHY context is shared with other MACs (or we're trying to - * switch bands), so remove the P2P Device from the binding, - * allocate an new PHY context and create a new binding. - */ - phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!phy_ctxt) { - ret = -ENOSPC; - goto out_unlock; - } - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, - 1, 1); - if (ret) { - IWL_ERR(mvm, "Failed to change PHY context\n"); - goto out_unlock; - } - - ret = ops->switch_phy_ctxt(mvm, vif, phy_ctxt); - if (ret) - goto out_unlock; + ret = iwl_mvm_p2p_find_phy_ctxt(mvm, vif, channel); + if (ret) + goto out_unlock; - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt); - } + ret = ops->link(mvm, vif); + if (ret) + goto out_unlock; -schedule_time_event: - /* Schedule the time events */ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); - out_unlock: mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); @@ -4780,15 +4809,14 @@ static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, goto out; } - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); + ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); if (ret) { IWL_ERR(mvm, "Failed to add PHY context\n"); goto out; } - iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); *phy_ctxt_id = phy_ctxt->id; out: return ret; @@ -5253,8 +5281,8 @@ int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw) return mvm->ibss_manager; } -int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - bool set) +static 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); @@ -5655,7 +5683,8 @@ void iwl_mvm_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } if (drop) { - if (iwl_mvm_flush_sta(mvm, mvmsta, false)) + if (iwl_mvm_flush_sta(mvm, mvmsta->deflink.sta_id, + mvmsta->tfd_queue_msk)) IWL_ERR(mvm, "flush request fail\n"); } else { if (iwl_mvm_has_new_tx_api(mvm)) @@ -5677,22 +5706,21 @@ void iwl_mvm_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void iwl_mvm_mac_flush_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int i; + struct iwl_mvm_link_sta *mvm_link_sta; + struct ieee80211_link_sta *link_sta; + int link_id; mutex_lock(&mvm->mutex); - for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { - struct iwl_mvm_sta *mvmsta; - struct ieee80211_sta *tmp; - - tmp = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (tmp != sta) + for_each_sta_active_link(vif, sta, link_sta, link_id) { + mvm_link_sta = rcu_dereference_protected(mvmsta->link[link_id], + lockdep_is_held(&mvm->mutex)); + if (!mvm_link_sta) continue; - mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (iwl_mvm_flush_sta(mvm, mvmsta, false)) + if (iwl_mvm_flush_sta(mvm, mvm_link_sta->sta_id, + mvmsta->tfd_queue_msk)) IWL_ERR(mvm, "flush request fail\n"); } mutex_unlock(&mvm->mutex); @@ -5702,7 +5730,11 @@ 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; + int ret = 0; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(SYSTEM_GROUP, + SYSTEM_STATISTICS_CMD), + IWL_FW_CMD_VER_UNKNOWN); memset(survey, 0, sizeof(*survey)); @@ -5722,13 +5754,8 @@ int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, goto out; } - survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_RX | - SURVEY_INFO_TIME_TX | - SURVEY_INFO_TIME_SCAN; - survey->time = mvm->accu_radio_stats.on_time_rf + - mvm->radio_stats.on_time_rf; - do_div(survey->time, USEC_PER_MSEC); + survey->filled = SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; survey->time_rx = mvm->accu_radio_stats.rx_time + mvm->radio_stats.rx_time; @@ -5738,11 +5765,20 @@ int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, mvm->radio_stats.tx_time; do_div(survey->time_tx, USEC_PER_MSEC); + /* the new fw api doesn't support the following fields */ + if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) + goto out; + + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_SCAN; + survey->time = mvm->accu_radio_stats.on_time_rf + + mvm->radio_stats.on_time_rf; + do_div(survey->time, USEC_PER_MSEC); + survey->time_scan = mvm->accu_radio_stats.on_time_scan + mvm->radio_stats.on_time_scan; do_div(survey->time_scan, USEC_PER_MSEC); - ret = 0; out: mutex_unlock(&mvm->mutex); return ret; @@ -5891,6 +5927,7 @@ void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, 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); + int i; if (mvmsta->deflink.avg_energy) { sinfo->signal_avg = -(s8)mvmsta->deflink.avg_energy; @@ -5919,8 +5956,11 @@ void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, if (iwl_mvm_request_statistics(mvm, false)) goto unlock; - sinfo->rx_beacon = mvmvif->deflink.beacon_stats.num_beacons + - mvmvif->deflink.beacon_stats.accu_num_beacons; + sinfo->rx_beacon = 0; + for_each_mvm_vif_valid_link(mvmvif, i) + sinfo->rx_beacon += mvmvif->link[i]->beacon_stats.num_beacons + + mvmvif->link[i]->beacon_stats.accu_num_beacons; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX); if (mvmvif->deflink.beacon_stats.avg_signal) { /* firmware only reports a value after RXing a few beacons */ @@ -6309,6 +6349,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate, #ifdef CONFIG_IWLWIFI_DEBUGFS + .vif_add_debugfs = iwl_mvm_vif_add_debugfs, .link_sta_add_debugfs = iwl_mvm_link_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 2c9f2f71b083..ea3e9e9c6e26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -24,10 +24,15 @@ static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm, return 0; } - /* AP group keys are per link and should be on the mcast STA */ + /* AP group keys are per link and should be on the mcast/bcast STA */ if (vif->type == NL80211_IFTYPE_AP && - !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* IGTK/BIGTK to bcast STA */ + if (keyconf->keyidx >= 4) + return BIT(link_info->bcast_sta.sta_id); + /* GTK for data to mcast STA */ 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) @@ -91,7 +96,12 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, if (!sta && vif->type == NL80211_IFTYPE_STATION) sta = mvmvif->ap_sta; - if (!IS_ERR_OR_NULL(sta) && sta->mfp) + /* Set the MFP flag also for an AP interface where the key is an IGTK + * key as in such a case the station would always be NULL + */ + if ((!IS_ERR_OR_NULL(sta) && sta->mfp) || + (vif->type == NL80211_IFTYPE_AP && + (keyconf->keyidx == 4 || keyconf->keyidx == 5))) flags |= IWL_SEC_KEY_FLAG_MFP; return flags; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 46e207211f21..ff6cb064051b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -10,6 +10,7 @@ static int iwl_mvm_mld_mac_add_interface(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; + int i; mutex_lock(&mvm->mutex); @@ -22,8 +23,9 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, /* 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; + for_each_mvm_vif_valid_link(mvmvif, i) + mvmvif->link[i]->beacon_stats.accu_num_beacons += + mvmvif->link[i]->beacon_stats.num_beacons; /* Allocate resources for the MAC context, and add it to the fw */ ret = iwl_mvm_mac_ctxt_init(mvm, vif); @@ -56,43 +58,15 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, 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; + ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); + if (ret) + goto out_free_bf; - /* Save a pointer to p2p device vif, so it can later be used to - * update the p2p device MAC when a GO is started/stopped - */ + /* 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 + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) 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) @@ -107,7 +81,7 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); } - iwl_mvm_vif_dbgfs_register(mvm, vif); + iwl_mvm_vif_dbgfs_add_link(mvm, vif); if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && vif->type == NL80211_IFTYPE_STATION && !vif->p2p && @@ -119,10 +93,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, 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; @@ -130,7 +100,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, 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: @@ -168,7 +137,7 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, if (vif->bss_conf.ftm_responder) memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats)); - iwl_mvm_vif_dbgfs_clean(mvm, vif); + iwl_mvm_vif_dbgfs_rm_link(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. @@ -185,14 +154,18 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, iwl_mvm_power_update_mac(mvm); + /* Before the interface removal, mac80211 would cancel the ROC, and the + * ROC worker would be scheduled if needed. The worker would be flushed + * in iwl_mvm_prepare_mac_removal() and thus at this point the link is + * not active. So need only to remove the link. + */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + if (mvmvif->deflink.phy_ctxt) { + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); + mvmvif->deflink.phy_ctxt = NULL; + } 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; + iwl_mvm_remove_link(mvm, vif, &vif->bss_conf); } else { iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); } @@ -240,8 +213,8 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, mvmvif->esr_active = true; - /* Disable SMPS overrideing by user */ - vif->driver_flags |= IEEE80211_VIF_DISABLE_SMPS_OVERRIDE; + /* Indicate to mac80211 that EML is enabled */ + vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE; iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, IEEE80211_SMPS_OFF); @@ -399,7 +372,7 @@ static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, mvmvif->esr_active = false; - vif->driver_flags &= ~IEEE80211_VIF_DISABLE_SMPS_OVERRIDE; + vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, IEEE80211_SMPS_AUTOMATIC); @@ -489,10 +462,17 @@ static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 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); + /* in the non-MLD case, remove/re-add the link to clean up FW state */ + if (!ieee80211_vif_is_mld(vif) && !mvmvif->ap_sta && + !WARN_ON_ONCE(vif->cfg.assoc)) { + iwl_mvm_remove_link(mvm, vif, link_conf); + iwl_mvm_add_link(mvm, vif, link_conf); + } mutex_unlock(&mvm->mutex); } @@ -623,6 +603,126 @@ static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, &callbacks); } +struct iwl_mvm_link_sel_data { + u8 link_id; + enum nl80211_band band; + bool active; +}; + +static bool iwl_mvm_mld_valid_link_pair(struct iwl_mvm_link_sel_data *a, + struct iwl_mvm_link_sel_data *b) +{ + return a->band != b->band; +} + +void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool valid_links_changed) +{ + struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; + unsigned long usable_links = ieee80211_vif_usable_links(vif); + u32 max_active_links = iwl_mvm_max_active_links(mvm, vif); + u16 new_active_links; + u8 link_id, n_data = 0, i, j; + + if (!IWL_MVM_AUTO_EML_ENABLE) + return; + + if (!ieee80211_vif_is_mld(vif) || usable_links == 1) + return; + + /* The logic below is a simple version that doesn't suit more than 2 + * links + */ + WARN_ON_ONCE(max_active_links > 2); + + /* if only a single active link is supported, assume that the one + * selected by higher layer for connection establishment is the best. + */ + if (max_active_links == 1 && !valid_links_changed) + return; + + /* If we are already using the maximal number of active links, don't do + * any change. This can later be optimized to pick a 'better' link pair. + */ + if (hweight16(vif->active_links) == max_active_links) + return; + + rcu_read_lock(); + + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + rcu_dereference(vif->link_conf[link_id]); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + data[n_data].link_id = link_id; + data[n_data].band = link_conf->chandef.chan->band; + data[n_data].active = vif->active_links & BIT(link_id); + n_data++; + } + + rcu_read_unlock(); + + /* this is expected to be the current active link */ + if (n_data == 1) + return; + + new_active_links = 0; + + /* Assume that after association only a single link is active, thus, + * select only the 2nd link + */ + if (!valid_links_changed) { + for (i = 0; i < n_data; i++) { + if (data[i].active) + break; + } + + if (WARN_ON_ONCE(i == n_data)) + return; + + for (j = 0; j < n_data; j++) { + if (i == j) + continue; + + if (iwl_mvm_mld_valid_link_pair(&data[i], &data[j])) + break; + } + + if (j != n_data) + new_active_links = BIT(data[i].link_id) | + BIT(data[j].link_id); + } else { + /* Try to find a valid link pair for EMLSR operation. If a pair + * is not found continue using the current active link. + */ + for (i = 0; i < n_data; i++) { + for (j = 0; j < n_data; j++) { + if (i == j) + continue; + + if (iwl_mvm_mld_valid_link_pair(&data[i], + &data[j])) + break; + } + + /* found a valid pair for EMLSR, use it */ + if (j != n_data) { + new_active_links = BIT(data[i].link_id) | + BIT(data[j].link_id); + break; + } + } + } + + if (WARN_ON(!new_active_links)) + return; + + if (vif->active_links != new_active_links) + ieee80211_set_active_links_async(vif, new_active_links); +} + static void iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -653,7 +753,7 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, } /* Update EHT Puncturing info */ - if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc && has_eht) + if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc) link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; if (link_changes) { @@ -667,6 +767,9 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + if (changes & BSS_CHANGED_MLD_VALID_LINKS) + iwl_mvm_mld_select_links(mvm, vif, true); + memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, ETH_ALEN); @@ -757,6 +860,12 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && protect) { + /* We are in assoc so only one link is active- + * The association link + */ + unsigned int link_id = + ffs(vif->active_links) - 1; + /* If we're not restarting and still haven't * heard a beacon (dtim period unknown) then * make sure we still have enough minimum time @@ -766,7 +875,7 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, * time could be small without us having heard * a beacon yet. */ - iwl_mvm_protect_assoc(mvm, vif, 0); + iwl_mvm_protect_assoc(mvm, vif, 0, link_id); } iwl_mvm_sf_update(mvm, vif, false); @@ -968,36 +1077,29 @@ iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, 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) +static int iwl_mvm_mld_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret = 0; + int ret; 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")) + /* The PHY context ID might have changed so need to set it */ + ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false); + if (WARN(ret, "Failed to set PHY context ID\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, + LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_RATES_INFO, + true); - ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false); - if (WARN(ret, "Failed to deactivate link\n")) + if (WARN(ret, "Failed linking P2P_DEVICE\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; + /* The station and queue allocation must be done only after the linking + * is done, as otherwise the FW might incorrectly configure its state. + */ + return iwl_mvm_mld_add_bcast_sta(mvm, vif, &vif->bss_conf); } static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1006,7 +1108,7 @@ static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { static const 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, + .link = iwl_mvm_mld_roc_link, }; return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); @@ -1173,8 +1275,6 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .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, @@ -1209,6 +1309,8 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .abort_pmsr = iwl_mvm_abort_pmsr, #ifdef CONFIG_IWLWIFI_DEBUGFS + .vif_add_debugfs = iwl_mvm_vif_add_debugfs, + .link_add_debugfs = iwl_mvm_link_add_debugfs, .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs, #endif .set_hw_timestamp = iwl_mvm_set_hw_timestamp, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index 1464aad039e1..ca5e4fbcf8ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -347,7 +347,7 @@ static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm, return -EINVAL; if (flush) - iwl_mvm_flush_sta(mvm, int_sta, true); + iwl_mvm_flush_sta(mvm, int_sta->sta_id, int_sta->tfd_queue_msk); iwl_mvm_mld_disable_txq(mvm, BIT(int_sta->sta_id), queuptr, tid); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 66d9de0f1511..f2af3e571409 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -121,6 +121,7 @@ struct iwl_mvm_time_event_data { * if the te is in the time event list or not (when id == TE_MAX) */ u32 id; + u8 link_id; }; /* Power management */ @@ -646,18 +647,9 @@ struct iwl_mvm_tcm { * @queue: queue of this reorder buffer * @last_amsdu: track last ASMDU SN for duplication detection * @last_sub_index: track ASMDU sub frame index for duplication detection - * @reorder_timer: timer for frames are in the reorder buffer. For AMSDU - * it is the time of last received sub-frame - * @removed: prevent timer re-arming * @valid: reordering is valid for this queue * @lock: protect reorder buffer internal state * @mvm: mvm pointer, needed for frame timer context - * @consec_oldsn_drops: consecutive drops due to old SN - * @consec_oldsn_ampdu_gp2: A-MPDU GP2 timestamp to track - * when to apply old SN consecutive drop workaround - * @consec_oldsn_prev_drop: track whether or not an MPDU - * that was single/part of the previous A-MPDU was - * dropped due to old SN */ struct iwl_mvm_reorder_buffer { u16 head_sn; @@ -666,33 +658,21 @@ struct iwl_mvm_reorder_buffer { int queue; u16 last_amsdu; u8 last_sub_index; - struct timer_list reorder_timer; - bool removed; bool valid; spinlock_t lock; struct iwl_mvm *mvm; - unsigned int consec_oldsn_drops; - u32 consec_oldsn_ampdu_gp2; - unsigned int consec_oldsn_prev_drop:1; } ____cacheline_aligned_in_smp; /** - * struct _iwl_mvm_reorder_buf_entry - reorder buffer entry per-queue/per-seqno + * struct iwl_mvm_reorder_buf_entry - reorder buffer entry per-queue/per-seqno * @frames: list of skbs stored - * @reorder_time: time the packet was stored in the reorder buffer */ -struct _iwl_mvm_reorder_buf_entry { - struct sk_buff_head frames; - unsigned long reorder_time; -}; - -/* make this indirection to get the aligned thing */ struct iwl_mvm_reorder_buf_entry { - struct _iwl_mvm_reorder_buf_entry e; + struct sk_buff_head frames; } #ifndef __CHECKER__ /* sparse doesn't like this construct: "bad integer constant expression" */ -__aligned(roundup_pow_of_two(sizeof(struct _iwl_mvm_reorder_buf_entry))) +__aligned(roundup_pow_of_two(sizeof(struct sk_buff_head))) #endif ; @@ -1214,6 +1194,8 @@ struct iwl_mvm { struct iwl_time_sync_data time_sync; struct iwl_mei_scan_filter mei_scan_filter; + + bool statistics_clear; }; /* Extract MVM priv from op_mode and _hw */ @@ -1674,7 +1656,7 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status); static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } #endif 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(struct iwl_mvm *mvm, u32 sta_id, u32 tfd_queue_mask); int iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id, u16 tids); /* Utils to extract sta related data */ @@ -1705,6 +1687,16 @@ static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) } /* Statistics */ +void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_handle_rx_system_oper_part1_stats(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +static inline void +iwl_mvm_handle_rx_system_end_stats_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ +} + void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt); void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, @@ -1921,41 +1913,10 @@ 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, - const 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_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); @@ -1971,13 +1932,12 @@ void iwl_mvm_bss_info_changed_station_assoc(struct iwl_mvm *mvm, * * @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 + * @link: For a P2P Device interface, pointer to a function that links the + * MAC/Link to the PHY context */ 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 (*link)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); }; int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1988,7 +1948,7 @@ 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); + u32 duration_override, unsigned int link_id); /* Quota management */ static inline size_t iwl_mvm_quota_cmd_size(struct iwl_mvm *mvm) @@ -2048,18 +2008,19 @@ void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm); -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void iwl_mvm_vif_dbgfs_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_vif_dbgfs_rm_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif); #else static inline void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm) { } static inline void -iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +iwl_mvm_vif_dbgfs_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { } static inline void -iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +iwl_mvm_vif_dbgfs_rm_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { } #endif /* CONFIG_IWLWIFI_DEBUGFS */ @@ -2292,7 +2253,7 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, bool *changed); struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, bool *changed); -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm); +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm, bool force_regd_sync); void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm); /* smart fifo */ @@ -2345,7 +2306,8 @@ void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm); void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool sta_added); void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); + struct ieee80211_vif *vif, + unsigned int link_id); int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u8 oper_class, @@ -2364,7 +2326,6 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, enum iwl_mvm_rxq_notif_type type, bool sync, const void *data, u32 size); -void iwl_mvm_reorder_timer_expired(struct timer_list *t); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); struct ieee80211_vif *iwl_mvm_get_vif_by_macid(struct iwl_mvm *mvm, u32 macid); bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm); @@ -2404,6 +2365,10 @@ void iwl_mvm_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, struct dentry *dir); +void iwl_mvm_link_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir); #endif /* new MLD related APIs */ @@ -2713,8 +2678,6 @@ void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, 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, @@ -2758,4 +2721,6 @@ int iwl_mvm_set_hw_timestamp(struct ieee80211_hw *hw, int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, struct ieee80211_vif *vif); bool iwl_mvm_enable_fils(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *ctx); +void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool valid_links_changed); #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 17a1e5717dde..c0dd441e800e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -573,7 +573,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm) * try to replay the last set MCC to FW. If it doesn't exist, * queue an update to cfg80211 to retrieve the default alpha2 from FW. */ - retval = iwl_mvm_init_fw_regd(mvm); + retval = iwl_mvm_init_fw_regd(mvm, true); if (retval != -ENOENT) return retval; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 465090f67aaf..fef86a8b4163 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -322,6 +322,19 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_NO_SIZE(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, + iwl_mvm_handle_rx_system_oper_stats, + RX_HANDLER_ASYNC_LOCKED, + struct iwl_system_statistics_notif_oper), + RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, + iwl_mvm_handle_rx_system_oper_part1_stats, + RX_HANDLER_ASYNC_LOCKED, + struct iwl_system_statistics_part1_notif_oper), + RX_HANDLER_GRP(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF, + iwl_mvm_handle_rx_system_end_stats_notif, + RX_HANDLER_ASYNC_LOCKED, + struct iwl_system_statistics_end_notif), + RX_HANDLER(BA_WINDOW_STATUS_NOTIFICATION_ID, iwl_mvm_window_status_notif, RX_HANDLER_SYNC, struct iwl_ba_window_status_notif), @@ -426,6 +439,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION, iwl_mvm_time_sync_msmt_confirm_event, RX_HANDLER_SYNC, struct iwl_time_msmt_cfm_notify), + RX_HANDLER_GRP(MAC_CONF_GROUP, ROC_NOTIF, + iwl_mvm_rx_roc_notif, RX_HANDLER_SYNC, + struct iwl_roc_notif), }; #undef RX_HANDLER #undef RX_HANDLER_GRP @@ -535,6 +551,8 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), HCMD_NAME(SYSTEM_FEATURES_CONTROL_CMD), HCMD_NAME(RFI_DEACTIVATE_NOTIF), + HCMD_NAME(SYSTEM_STATISTICS_CMD), + HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), }; /* Please keep this array *SORTED* by hex value. @@ -549,6 +567,8 @@ static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(AUX_STA_CMD), HCMD_NAME(STA_REMOVE_CMD), HCMD_NAME(STA_DISABLE_TX_CMD), + HCMD_NAME(ROC_CMD), + HCMD_NAME(ROC_NOTIF), HCMD_NAME(SESSION_PROTECTION_NOTIF), HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), }; @@ -589,6 +609,14 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { /* Please keep this array *SORTED* by hex value. * Access is done through binary search */ +static const struct iwl_hcmd_names iwl_mvm_statistics_names[] = { + HCMD_NAME(STATISTICS_OPER_NOTIF), + HCMD_NAME(STATISTICS_OPER_PART1_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ static const struct iwl_hcmd_names iwl_mvm_scan_names[] = { HCMD_NAME(OFFLOAD_MATCH_INFO_NOTIF), }; @@ -640,6 +668,7 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = { [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names), [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mvm_regulatory_and_nvm_names), + [STATISTICS_GROUP] = HCMD_ARR(iwl_mvm_statistics_names), }; /* this forward declaration can avoid to export the function */ @@ -1304,7 +1333,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), - "%s", fw->fw_version); + "%.31s", fw->fw_version); trans_cfg.fw_reset_handshake = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); @@ -1950,9 +1979,6 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - if (mvm->pldr_sync) - return; - if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) && !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index a5b432bc9e2f..4e1fccff3987 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -192,6 +192,9 @@ int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd.rlc.rx_chain_info, chains_static, chains_dynamic); + IWL_DEBUG_FW(mvm, "Send RLC command: phy=%d, rx_chain_info=0x%x\n", + ctxt->id, cmd.rlc.rx_chain_info); + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(RLC_CONFIG_CMD, DATA_PATH_GROUP, 2), 0, sizeof(cmd), &cmd); @@ -265,6 +268,8 @@ 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) { + int ret; + WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && ctxt->ref); lockdep_assert_held(&mvm->mutex); @@ -273,9 +278,16 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, ctxt->width = chandef->width; ctxt->center_freq1 = chandef->center_freq1; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD); + ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD); + + if (ret) + return ret; + + ctxt->ref++; + + return 0; } /* @@ -285,6 +297,11 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { lockdep_assert_held(&mvm->mutex); + + /* If we were taking the first ref, we should have + * called iwl_mvm_phy_ctxt_add. + */ + WARN_ON(!ctxt->ref); ctxt->ref++; } @@ -301,7 +318,11 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, lockdep_assert_held(&mvm->mutex); - if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) >= 2 && + if (WARN_ON_ONCE(!ctxt->ref)) + return -EINVAL; + + if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, + RLC_CONFIG_CMD), 0) >= 2 && ctxt->channel == chandef->chan && ctxt->width == chandef->width && ctxt->center_freq1 == chandef->center_freq1) @@ -335,6 +356,7 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { + struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); if (WARN_ON_ONCE(!ctxt)) @@ -342,41 +364,13 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) ctxt->ref--; - /* - * Move unused phy's to a default channel. When the phy is moved the, - * fw will cleanup immediate quiet bit if it was previously set, - * otherwise we might not be able to reuse this phy. - */ - if (ctxt->ref == 0) { - struct ieee80211_channel *chan = NULL; - struct cfg80211_chan_def chandef; - struct ieee80211_supported_band *sband; - enum nl80211_band band; - int channel; - - for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { - sband = mvm->hw->wiphy->bands[band]; - - if (!sband) - continue; - - for (channel = 0; channel < sband->n_channels; channel++) - if (!(sband->channels[channel].flags & - IEEE80211_CHAN_DISABLED)) { - chan = &sband->channels[channel]; - break; - } - - if (chan) - break; - } - - if (WARN_ON(!chan)) - return; - - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1); - } + if (ctxt->ref) + return; + + cfg80211_chandef_create(&chandef, ctxt->channel, NL80211_CHAN_NO_HT); + + iwl_mvm_phy_ctxt_apply(mvm, ctxt, &chandef, 1, 1, + FW_CTXT_ACTION_REMOVE); } static void iwl_mvm_binding_iterator(void *_data, u8 *mac, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 542c192698a4..8caa971770c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -553,7 +553,7 @@ struct iwl_mvm_stat_data { struct iwl_mvm_stat_data_all_macs { struct iwl_mvm *mvm; __le32 flags; - struct iwl_statistics_ntfy_per_mac *per_mac_stats; + struct iwl_stats_ntfy_per_mac *per_mac; }; static void iwl_mvm_update_vif_sig(struct ieee80211_vif *vif, int sig) @@ -658,7 +658,7 @@ static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_stat_data_all_macs *data = _data; - struct iwl_statistics_ntfy_per_mac *mac_stats; + struct iwl_stats_ntfy_per_mac *mac_stats; int sig; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 vif_id = mvmvif->id; @@ -669,7 +669,7 @@ static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac, if (vif->type != NL80211_IFTYPE_STATION) return; - mac_stats = &data->per_mac_stats[vif_id]; + mac_stats = &data->per_mac[vif_id]; mvmvif->deflink.beacon_stats.num_beacons = le32_to_cpu(mac_stats->beacon_counter); @@ -759,7 +759,7 @@ iwl_mvm_stats_ver_15(struct iwl_mvm *mvm, struct iwl_mvm_stat_data_all_macs data = { .mvm = mvm, .flags = stats->flags, - .per_mac_stats = stats->per_mac_stats, + .per_mac = stats->per_mac, }; ieee80211_iterate_active_interfaces(mvm->hw, @@ -829,6 +829,142 @@ static bool iwl_mvm_verify_stats_len(struct iwl_mvm *mvm, } static void +iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm, + struct iwl_stats_ntfy_per_link *per_link) +{ + u32 air_time[MAC_INDEX_AUX] = {}; + u32 rx_bytes[MAC_INDEX_AUX] = {}; + int fw_link_id; + + for (fw_link_id = 0; fw_link_id < ARRAY_SIZE(mvm->link_id_to_link_conf); + fw_link_id++) { + struct iwl_stats_ntfy_per_link *link_stats; + struct ieee80211_bss_conf *bss_conf; + struct iwl_mvm_vif *mvmvif; + int link_id; + int sig; + + bss_conf = iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, fw_link_id, + false); + if (!bss_conf) + continue; + + if (bss_conf->vif->type != NL80211_IFTYPE_STATION) + continue; + + link_id = bss_conf->link_id; + if (link_id >= ARRAY_SIZE(mvmvif->link)) + continue; + + mvmvif = iwl_mvm_vif_from_mac80211(bss_conf->vif); + if (!mvmvif || !mvmvif->link[link_id]) + continue; + + link_stats = &per_link[fw_link_id]; + + mvmvif->link[link_id]->beacon_stats.num_beacons = + le32_to_cpu(link_stats->beacon_counter); + + /* we basically just use the u8 to store 8 bits and then treat + * it as a s8 whenever we take it out to a different type. + */ + mvmvif->link[link_id]->beacon_stats.avg_signal = + -le32_to_cpu(link_stats->beacon_average_energy); + + /* make sure that beacon statistics don't go backwards with TCM + * request to clear statistics + */ + if (mvm->statistics_clear) + mvmvif->link[link_id]->beacon_stats.accu_num_beacons += + mvmvif->link[link_id]->beacon_stats.num_beacons; + + sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); + iwl_mvm_update_vif_sig(bss_conf->vif, sig); + + if (WARN_ONCE(mvmvif->id >= MAC_INDEX_AUX, + "invalid mvmvif id: %d", mvmvif->id)) + continue; + + air_time[mvmvif->id] += + le32_to_cpu(per_link[fw_link_id].air_time); + rx_bytes[mvmvif->id] += + le32_to_cpu(per_link[fw_link_id].rx_bytes); + } + + /* Don't update in case the statistics are not cleared, since + * we will end up counting twice the same airtime, once in TCM + * request and once in statistics notification. + */ + if (mvm->statistics_clear) { + __le32 air_time_le[MAC_INDEX_AUX]; + __le32 rx_bytes_le[MAC_INDEX_AUX]; + int vif_id; + + for (vif_id = 0; vif_id < ARRAY_SIZE(air_time_le); vif_id++) { + air_time_le[vif_id] = cpu_to_le32(air_time[vif_id]); + rx_bytes_le[vif_id] = cpu_to_le32(rx_bytes[vif_id]); + } + + iwl_mvm_update_tcm_from_stats(mvm, air_time_le, rx_bytes_le); + } +} + +void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + u8 average_energy[IWL_MVM_STATION_COUNT_MAX]; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_system_statistics_notif_oper *stats; + int i; + u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP, + STATISTICS_OPER_NOTIF, 0); + + if (notif_ver != 3) { + IWL_FW_CHECK_FAILED(mvm, + "Oper stats notif ver %d is not supported\n", + notif_ver); + return; + } + + stats = (void *)&pkt->data; + iwl_mvm_stat_iterator_all_links(mvm, stats->per_link); + + for (i = 0; i < ARRAY_SIZE(average_energy); i++) + average_energy[i] = + le32_to_cpu(stats->per_sta[i].average_energy); + + ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter, + average_energy); +} + +void iwl_mvm_handle_rx_system_oper_part1_stats(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_system_statistics_part1_notif_oper *part1_stats; + int i; + u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP, + STATISTICS_OPER_PART1_NOTIF, 0); + + if (notif_ver != 4) { + IWL_FW_CHECK_FAILED(mvm, + "Part1 stats notif ver %d is not supported\n", + notif_ver); + return; + } + + part1_stats = (void *)&pkt->data; + mvm->radio_stats.rx_time = 0; + mvm->radio_stats.tx_time = 0; + for (i = 0; i < ARRAY_SIZE(part1_stats->per_link); i++) { + mvm->radio_stats.rx_time += + le64_to_cpu(part1_stats->per_link[i].rx_time); + mvm->radio_stats.tx_time += + le64_to_cpu(part1_stats->per_link[i].tx_time); + } +} + +static void iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -887,11 +1023,11 @@ iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm, for (i = 0; i < ARRAY_SIZE(average_energy); i++) average_energy[i] = - le32_to_cpu(stats->per_sta_stats[i].average_energy); + le32_to_cpu(stats->per_sta[i].average_energy); for (i = 0; i < ARRAY_SIZE(air_time); i++) { - air_time[i] = stats->per_mac_stats[i].air_time; - rx_bytes[i] = stats->per_mac_stats[i].rx_bytes; + air_time[i] = stats->per_mac[i].air_time; + rx_bytes[i] = stats->per_mac[i].rx_bytes; } } @@ -917,6 +1053,13 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, __le32 *bytes, *air_time, flags; int expected_size; u8 *energy; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(SYSTEM_GROUP, + SYSTEM_STATISTICS_CMD), + IWL_FW_CMD_VER_UNKNOWN); + + if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) + return; /* From ver 14 and up we use TLV statistics format */ if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 041afc97a911..886d00098528 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -550,44 +550,12 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue, return false; } -/* - * Returns true if sn2 - buffer_size < sn1 < sn2. - * To be used only in order to compare reorder buffer head with NSSN. - * We fully trust NSSN unless it is behind us due to reorder timeout. - * Reorder timeout can only bring us up to buffer_size SNs ahead of NSSN. - */ -static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size) -{ - return ieee80211_sn_less(sn1, sn2) && - !ieee80211_sn_less(sn1, sn2 - buffer_size); -} - -static void iwl_mvm_sync_nssn(struct iwl_mvm *mvm, u8 baid, u16 nssn) -{ - if (IWL_MVM_USE_NSSN_SYNC) { - struct iwl_mvm_nssn_sync_data notif = { - .baid = baid, - .nssn = nssn, - }; - - iwl_mvm_sync_rx_queues_internal(mvm, IWL_MVM_RXQ_NSSN_SYNC, false, - ¬if, sizeof(notif)); - } -} - -#define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10) - -enum iwl_mvm_release_flags { - IWL_MVM_RELEASE_SEND_RSS_SYNC = BIT(0), - IWL_MVM_RELEASE_FROM_RSS_SYNC = BIT(1), -}; - static void iwl_mvm_release_frames(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct napi_struct *napi, struct iwl_mvm_baid_data *baid_data, struct iwl_mvm_reorder_buffer *reorder_buf, - u16 nssn, u32 flags) + u16 nssn) { struct iwl_mvm_reorder_buf_entry *entries = &baid_data->entries[reorder_buf->queue * @@ -596,31 +564,12 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, lockdep_assert_held(&reorder_buf->lock); - /* - * We keep the NSSN not too far behind, if we are sync'ing it and it - * is more than 2048 ahead of us, it must be behind us. Discard it. - * This can happen if the queue that hit the 0 / 2048 seqno was lagging - * behind and this queue already processed packets. The next if - * would have caught cases where this queue would have processed less - * than 64 packets, but it may have processed more than 64 packets. - */ - if ((flags & IWL_MVM_RELEASE_FROM_RSS_SYNC) && - ieee80211_sn_less(nssn, ssn)) - goto set_timer; - - /* ignore nssn smaller than head sn - this can happen due to timeout */ - if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size)) - goto set_timer; - - while (iwl_mvm_is_sn_less(ssn, nssn, reorder_buf->buf_size)) { + while (ieee80211_sn_less(ssn, nssn)) { int index = ssn % reorder_buf->buf_size; - struct sk_buff_head *skb_list = &entries[index].e.frames; + struct sk_buff_head *skb_list = &entries[index].frames; struct sk_buff *skb; ssn = ieee80211_sn_inc(ssn); - if ((flags & IWL_MVM_RELEASE_SEND_RSS_SYNC) && - (ssn == 2048 || ssn == 0)) - iwl_mvm_sync_nssn(mvm, baid_data->baid, ssn); /* * Empty the list. Will have more than one frame for A-MSDU. @@ -635,99 +584,6 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, } } reorder_buf->head_sn = nssn; - -set_timer: - if (reorder_buf->num_stored && !reorder_buf->removed) { - u16 index = reorder_buf->head_sn % reorder_buf->buf_size; - - while (skb_queue_empty(&entries[index].e.frames)) - index = (index + 1) % reorder_buf->buf_size; - /* modify timer to match next frame's expiration time */ - mod_timer(&reorder_buf->reorder_timer, - entries[index].e.reorder_time + 1 + - RX_REORDER_BUF_TIMEOUT_MQ); - } else { - del_timer(&reorder_buf->reorder_timer); - } -} - -void iwl_mvm_reorder_timer_expired(struct timer_list *t) -{ - struct iwl_mvm_reorder_buffer *buf = from_timer(buf, t, reorder_timer); - struct iwl_mvm_baid_data *baid_data = - iwl_mvm_baid_data_from_reorder_buf(buf); - struct iwl_mvm_reorder_buf_entry *entries = - &baid_data->entries[buf->queue * baid_data->entries_per_queue]; - int i; - u16 sn = 0, index = 0; - bool expired = false; - bool cont = false; - - spin_lock(&buf->lock); - - if (!buf->num_stored || buf->removed) { - spin_unlock(&buf->lock); - return; - } - - for (i = 0; i < buf->buf_size ; i++) { - index = (buf->head_sn + i) % buf->buf_size; - - if (skb_queue_empty(&entries[index].e.frames)) { - /* - * If there is a hole and the next frame didn't expire - * we want to break and not advance SN - */ - cont = false; - continue; - } - if (!cont && - !time_after(jiffies, entries[index].e.reorder_time + - RX_REORDER_BUF_TIMEOUT_MQ)) - break; - - expired = true; - /* continue until next hole after this expired frames */ - cont = true; - sn = ieee80211_sn_add(buf->head_sn, i + 1); - } - - if (expired) { - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - 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 */ - IWL_DEBUG_HT(buf->mvm, - "Releasing expired frames for sta %u, sn %d\n", - sta_id, sn); - iwl_mvm_event_frame_timeout_callback(buf->mvm, mvmsta->vif, - sta, baid_data->tid); - iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, - buf, sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); - rcu_read_unlock(); - } else { - /* - * If no frame expired and there are stored frames, index is now - * pointing to the first unexpired frame - modify timer - * accordingly to this frame. - */ - mod_timer(&buf->reorder_timer, - entries[index].e.reorder_time + - 1 + RX_REORDER_BUF_TIMEOUT_MQ); - } - -out: - spin_unlock(&buf->lock); } static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue, @@ -760,10 +616,8 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue, spin_lock_bh(&reorder_buf->lock); iwl_mvm_release_frames(mvm, sta, NULL, ba_data, reorder_buf, ieee80211_sn_add(reorder_buf->head_sn, - reorder_buf->buf_size), - 0); + reorder_buf->buf_size)); spin_unlock_bh(&reorder_buf->lock); - del_timer_sync(&reorder_buf->reorder_timer); out: rcu_read_unlock(); @@ -771,8 +625,7 @@ out: static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, struct napi_struct *napi, - u8 baid, u16 nssn, int queue, - u32 flags) + u8 baid, u16 nssn, int queue) { struct ieee80211_sta *sta; struct iwl_mvm_reorder_buffer *reorder_buf; @@ -790,8 +643,7 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, ba_data = rcu_dereference(mvm->baid_map[baid]); if (!ba_data) { - WARN(!(flags & IWL_MVM_RELEASE_FROM_RSS_SYNC), - "BAID %d not found in map\n", baid); + WARN(true, "BAID %d not found in map\n", baid); goto out; } @@ -805,22 +657,13 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, spin_lock_bh(&reorder_buf->lock); iwl_mvm_release_frames(mvm, sta, napi, ba_data, - reorder_buf, nssn, flags); + reorder_buf, nssn); spin_unlock_bh(&reorder_buf->lock); out: rcu_read_unlock(); } -static void iwl_mvm_nssn_sync(struct iwl_mvm *mvm, - struct napi_struct *napi, int queue, - const struct iwl_mvm_nssn_sync_data *data) -{ - iwl_mvm_release_frames_from_notif(mvm, napi, data->baid, - data->nssn, queue, - IWL_MVM_RELEASE_FROM_RSS_SYNC); -} - void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { @@ -855,14 +698,6 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, break; iwl_mvm_del_ba(mvm, queue, (void *)internal_notif->data); break; - case IWL_MVM_RXQ_NSSN_SYNC: - if (WARN_ONCE(len != sizeof(struct iwl_mvm_nssn_sync_data), - "invalid nssn sync notification size %d (%d)", - len, (int)sizeof(struct iwl_mvm_nssn_sync_data))) - break; - iwl_mvm_nssn_sync(mvm, napi, queue, - (void *)internal_notif->data); - break; default: WARN_ONCE(1, "Invalid identifier %d", internal_notif->type); } @@ -876,55 +711,6 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, } } -static void iwl_mvm_oldsn_workaround(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, int tid, - struct iwl_mvm_reorder_buffer *buffer, - u32 reorder, u32 gp2, int queue) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (gp2 != buffer->consec_oldsn_ampdu_gp2) { - /* we have a new (A-)MPDU ... */ - - /* - * reset counter to 0 if we didn't have any oldsn in - * the last A-MPDU (as detected by GP2 being identical) - */ - if (!buffer->consec_oldsn_prev_drop) - buffer->consec_oldsn_drops = 0; - - /* either way, update our tracking state */ - buffer->consec_oldsn_ampdu_gp2 = gp2; - } else if (buffer->consec_oldsn_prev_drop) { - /* - * tracking state didn't change, and we had an old SN - * indication before - do nothing in this case, we - * already noted this one down and are waiting for the - * next A-MPDU (by GP2) - */ - return; - } - - /* return unless this MPDU has old SN */ - if (!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN)) - return; - - /* update state */ - buffer->consec_oldsn_prev_drop = 1; - buffer->consec_oldsn_drops++; - - /* if limit is reached, send del BA and reset state */ - if (buffer->consec_oldsn_drops == IWL_MVM_AMPDU_CONSEC_DROPS_DELBA) { - IWL_WARN(mvm, - "reached %d old SN frames from %pM on queue %d, stopping BA session on TID %d\n", - IWL_MVM_AMPDU_CONSEC_DROPS_DELBA, - sta->addr, queue, tid); - ieee80211_stop_rx_ba_session(mvmsta->vif, BIT(tid), sta->addr); - buffer->consec_oldsn_prev_drop = 0; - buffer->consec_oldsn_drops = 0; - } -} - /* * Returns true if the MPDU was buffered\dropped, false if it should be passed * to upper layer. @@ -936,11 +722,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); struct iwl_mvm_baid_data *baid_data; struct iwl_mvm_reorder_buffer *buffer; - struct sk_buff *tail; u32 reorder = le32_to_cpu(desc->reorder_data); bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; bool last_subframe = @@ -957,6 +741,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, baid = (reorder & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT; + if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000) + return false; + /* * This also covers the case of receiving a Block Ack Request * outside a BA session; we'll pass it to mac80211 and that @@ -1018,59 +805,18 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, buffer->valid = true; } - if (ieee80211_is_back_req(hdr->frame_control)) { - iwl_mvm_release_frames(mvm, sta, napi, baid_data, - buffer, nssn, 0); + /* drop any duplicated packets */ + if (desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE)) goto drop; - } - - /* - * If there was a significant jump in the nssn - adjust. - * If the SN is smaller than the NSSN it might need to first go into - * the reorder buffer, in which case we just release up to it and the - * rest of the function will take care of storing it and releasing up to - * the nssn. - * This should not happen. This queue has been lagging and it should - * have been updated by a IWL_MVM_RXQ_NSSN_SYNC notification. Be nice - * and update the other queues. - */ - if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, - buffer->buf_size) || - !ieee80211_sn_less(sn, buffer->head_sn + buffer->buf_size)) { - u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn; - - iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, - min_sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); - } - - iwl_mvm_oldsn_workaround(mvm, sta, tid, buffer, reorder, - rx_status->device_timestamp, queue); /* drop any oudated packets */ - if (ieee80211_sn_less(sn, buffer->head_sn)) + if (reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN) goto drop; /* release immediately if allowed by nssn and no stored frames */ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { - if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, - buffer->buf_size) && - (!amsdu || last_subframe)) { - /* - * If we crossed the 2048 or 0 SN, notify all the - * queues. This is done in order to avoid having a - * head_sn that lags behind for too long. When that - * happens, we can get to a situation where the head_sn - * is within the interval [nssn - buf_size : nssn] - * which will make us think that the nssn is a packet - * that we already freed because of the reordering - * buffer and we will ignore it. So maintain the - * head_sn somewhat updated across all the queues: - * when it crosses 0 and 2048. - */ - if (sn == 2048 || sn == 0) - iwl_mvm_sync_nssn(mvm, baid, sn); + if (!amsdu || last_subframe) buffer->head_sn = nssn; - } /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; @@ -1085,37 +831,18 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * while technically there is no hole and we can move forward. */ if (!buffer->num_stored && sn == buffer->head_sn) { - if (!amsdu || last_subframe) { - if (sn == 2048 || sn == 0) - iwl_mvm_sync_nssn(mvm, baid, sn); + if (!amsdu || last_subframe) buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); - } + /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; } - index = sn % buffer->buf_size; - - /* - * Check if we already stored this frame - * As AMSDU is either received or not as whole, logic is simple: - * If we have frames in that position in the buffer and the last frame - * originated from AMSDU had a different SN then it is a retransmission. - * If it is the same SN then if the subframe index is incrementing it - * is the same AMSDU - otherwise it is a retransmission. - */ - tail = skb_peek_tail(&entries[index].e.frames); - if (tail && !amsdu) - goto drop; - else if (tail && (sn != buffer->last_amsdu || - buffer->last_sub_index >= sub_frame_idx)) - goto drop; - /* put in reorder buffer */ - __skb_queue_tail(&entries[index].e.frames, skb); + index = sn % buffer->buf_size; + __skb_queue_tail(&entries[index].frames, skb); buffer->num_stored++; - entries[index].e.reorder_time = jiffies; if (amsdu) { buffer->last_amsdu = sn; @@ -1135,8 +862,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, */ if (!amsdu || last_subframe) iwl_mvm_release_frames(mvm, sta, napi, baid_data, - buffer, nssn, - IWL_MVM_RELEASE_SEND_RSS_SYNC); + buffer, nssn); spin_unlock_bh(&buffer->lock); return true; @@ -2617,9 +2343,15 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, 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))) + likely(!iwl_mvm_mei_filter_scan(mvm, skb))) { + if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 && + (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && + !(desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME)) + rx_status->flag |= RX_FLAG_AMSDU_MORE; + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta, link_sta); + } out: rcu_read_unlock(); } @@ -2762,7 +2494,7 @@ void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_release_frames_from_notif(mvm, napi, release->baid, le16_to_cpu(release->nssn), - queue, 0); + queue); } void iwl_mvm_rx_bar_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, @@ -2806,7 +2538,7 @@ void iwl_mvm_rx_bar_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, IWL_DEBUG_DROP(mvm, "Received a BAR, expect packet loss: nssn %d\n", nssn); - iwl_mvm_release_frames_from_notif(mvm, napi, baid, nssn, queue, 0); + iwl_mvm_release_frames_from_notif(mvm, napi, baid, nssn, queue); out: rcu_read_unlock(); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 3cbe2c0b8d6b..75c5c58e14a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -3408,7 +3408,7 @@ int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify) if (!(mvm->scan_status & type)) return 0; - if (iwl_mvm_is_radio_killed(mvm)) { + if (!test_bit(STATUS_DEVICE_ENABLED, &mvm->trans->status)) { ret = 0; goto out; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 61d564735d57..bba96a968890 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2060,7 +2060,8 @@ bool iwl_mvm_sta_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, *status = IWL_MVM_QUEUE_FREE; } - if (vif->type == NL80211_IFTYPE_STATION) { + if (vif->type == NL80211_IFTYPE_STATION && + mvm_link->ap_sta_id == sta_id) { /* if associated - we can't remove the AP STA now */ if (vif->cfg.assoc) return true; @@ -2098,7 +2099,8 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, return ret; /* flush its queues here since we are freeing mvm_sta */ - ret = iwl_mvm_flush_sta(mvm, mvm_sta, false); + ret = iwl_mvm_flush_sta(mvm, mvm_sta->deflink.sta_id, + mvm_sta->tfd_queue_msk); if (ret) return ret; if (iwl_mvm_has_new_tx_api(mvm)) { @@ -2409,7 +2411,8 @@ void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - iwl_mvm_flush_sta(mvm, &mvmvif->deflink.bcast_sta, true); + iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id, + mvmvif->deflink.bcast_sta.tfd_queue_msk); switch (vif->type) { case NL80211_IFTYPE_AP: @@ -2665,7 +2668,8 @@ 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->deflink.mcast_sta, true); + iwl_mvm_flush_sta(mvm, mvmvif->deflink.mcast_sta.sta_id, + mvmvif->deflink.mcast_sta.tfd_queue_msk); iwl_mvm_disable_txq(mvm, NULL, mvmvif->deflink.mcast_sta.sta_id, &mvmvif->deflink.cab_queue, 0); @@ -2715,18 +2719,9 @@ static void iwl_mvm_free_reorder(struct iwl_mvm *mvm, WARN_ON(1); for (j = 0; j < reorder_buf->buf_size; j++) - __skb_queue_purge(&entries[j].e.frames); - /* - * Prevent timer re-arm. This prevents a very far fetched case - * where we timed out on the notification. There may be prior - * RX frames pending in the RX queue before the notification - * that might get processed between now and the actual deletion - * and we would re-arm the timer although we are deleting the - * reorder buffer. - */ - reorder_buf->removed = true; + __skb_queue_purge(&entries[j].frames); + spin_unlock_bh(&reorder_buf->lock); - del_timer_sync(&reorder_buf->reorder_timer); } } @@ -2746,15 +2741,12 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm, reorder_buf->num_stored = 0; reorder_buf->head_sn = ssn; reorder_buf->buf_size = buf_size; - /* rx reorder timer */ - timer_setup(&reorder_buf->reorder_timer, - iwl_mvm_reorder_timer_expired, 0); spin_lock_init(&reorder_buf->lock); reorder_buf->mvm = mvm; reorder_buf->queue = i; reorder_buf->valid = false; for (j = 0; j < reorder_buf->buf_size; j++) - __skb_queue_head_init(&entries[j].e.frames); + __skb_queue_head_init(&entries[j].frames); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 7738fdf1d336..b33a0ce096d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -286,12 +286,10 @@ struct iwl_mvm_key_pn { * * @IWL_MVM_RXQ_EMPTY: empty sync notification * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA - * @IWL_MVM_RXQ_NSSN_SYNC: notify all the RSS queues with the new NSSN */ enum iwl_mvm_rxq_notif_type { IWL_MVM_RXQ_EMPTY, IWL_MVM_RXQ_NOTIF_DEL_BA, - IWL_MVM_RXQ_NSSN_SYNC, }; /** @@ -315,11 +313,6 @@ struct iwl_mvm_delba_data { u32 baid; } __packed; -struct iwl_mvm_nssn_sync_data { - u32 baid; - u32 nssn; -} __packed; - /** * struct iwl_mvm_rxq_dup_data - per station per rx queue data * @last_seq: last sequence per tid for duplicate packet detection diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index dae6f2a1aad9..e7d5f4ebeb25 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2014 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020, 2022 Intel Corporation + * Copyright (C) 2018-2020, 2022-2023 Intel Corporation */ #include <linux/etherdevice.h> #include "mvm.h" @@ -144,7 +144,8 @@ void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, } void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + unsigned int link_id) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; @@ -154,7 +155,7 @@ void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) iwl_mvm_schedule_session_protection(mvm, vif, duration, - duration, true); + duration, true, link_id); else iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index e1f6cea649c3..218fdf1ed530 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -42,6 +42,7 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, te_data->uid = 0; te_data->id = TE_MAX; te_data->vif = NULL; + te_data->link_id = -1; } void iwl_mvm_roc_done_wk(struct work_struct *wk) @@ -78,9 +79,29 @@ 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->deflink.bcast_sta, - true); + struct ieee80211_vif *vif = mvm->p2p_device_vif; + + mvmvif = iwl_mvm_vif_from_mac80211(vif); + iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id, + mvmvif->deflink.bcast_sta.tfd_queue_msk); + + if (mvm->mld_api_is_used) { + iwl_mvm_mld_rm_bcast_sta(mvm, vif, + &vif->bss_conf); + + iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, + LINK_CONTEXT_MODIFY_ACTIVE, + false); + } else { + iwl_mvm_rm_p2p_bcast_sta(mvm, vif); + iwl_mvm_binding_remove_vif(mvm, vif); + } + + /* Do not remove the PHY context as removing and adding + * a PHY context has timing overheads. Leaving it + * configured in FW would be useful in case the next ROC + * is with the same channel. + */ } } @@ -93,7 +114,8 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) */ if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { /* do the same in case of hot spot 2.0 */ - iwl_mvm_flush_sta(mvm, &mvm->aux_sta, true); + iwl_mvm_flush_sta(mvm, mvm->aux_sta.sta_id, + mvm->aux_sta.tfd_queue_msk); if (mvm->mld_api_is_used) { iwl_mvm_mld_rm_aux_sta(mvm); @@ -378,6 +400,22 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, } } +void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_roc_notif *notif = (void *)pkt->data; + + if (le32_to_cpu(notif->success) && le32_to_cpu(notif->started) && + le32_to_cpu(notif->activity) == ROC_ACTIVITY_HOTSPOT) { + set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); + ieee80211_ready_on_channel(mvm->hw); + } else { + iwl_mvm_roc_finished(mvm); + ieee80211_remain_on_channel_expired(mvm->hw); + } +} + /* * Handle A Aux ROC time event */ @@ -651,19 +689,46 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, } } +/* Determine whether mac or link id should be used, and validate the link id */ +static int iwl_mvm_get_session_prot_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 link_id) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), 1); + + if (ver < 2) + return mvmvif->id; + + if (WARN(link_id < 0 || !mvmvif->link[link_id], + "Invalid link ID for session protection: %u\n", link_id)) + return -EINVAL; + + if (WARN(ieee80211_vif_is_mld(vif) && + !(vif->active_links & BIT(link_id)), + "Session Protection on an inactive link: %u\n", link_id)) + return -EINVAL; + + return mvmvif->link[link_id]->fw_link_id; +} + static void iwl_mvm_cancel_session_protection(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - u32 id) + struct ieee80211_vif *vif, + u32 id, u32 link_id) { + int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id); struct iwl_mvm_session_prot_cmd cmd = { - .id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), + .id_and_color = cpu_to_le32(mac_link_id), .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), .conf_id = cpu_to_le32(id), }; int ret; + if (mac_link_id < 0) + return; + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd); @@ -677,10 +742,12 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, u32 *uid) { u32 id; + struct ieee80211_vif *vif = te_data->vif; struct iwl_mvm_vif *mvmvif; enum nl80211_iftype iftype; + unsigned int link_id; - if (!te_data->vif) + if (!vif) return false; mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); @@ -695,6 +762,7 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, /* Save time event uid before clearing its data */ *uid = te_data->uid; id = te_data->id; + link_id = te_data->link_id; /* * The clear_data function handles time events that were already removed @@ -712,7 +780,8 @@ static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, id != HOT_SPOT_CMD) { if (mvmvif && id < SESSION_PROTECT_CONF_MAX_ID) { /* Session protection is still ongoing. Cancel it */ - iwl_mvm_cancel_session_protection(mvm, mvmvif, id); + iwl_mvm_cancel_session_protection(mvm, vif, id, + link_id); if (iftype == NL80211_IFTYPE_P2P_DEVICE) { iwl_mvm_p2p_roc_finished(mvm); } @@ -829,18 +898,41 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mvm_session_prot_notif *notif = (void *)pkt->data; + unsigned int ver = + iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), 2); + int id = le32_to_cpu(notif->mac_link_id); struct ieee80211_vif *vif; struct iwl_mvm_vif *mvmvif; + unsigned int notif_link_id; rcu_read_lock(); - vif = iwl_mvm_rcu_dereference_vif_id(mvm, le32_to_cpu(notif->mac_id), - true); + + if (ver <= 2) { + vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true); + } else { + struct ieee80211_bss_conf *link_conf = + iwl_mvm_rcu_fw_link_id_to_link_conf(mvm, id, true); + + if (!link_conf) + goto out_unlock; + + notif_link_id = link_conf->link_id; + vif = link_conf->vif; + } if (!vif) goto out_unlock; mvmvif = iwl_mvm_vif_from_mac80211(vif); + if (WARN(ver > 2 && mvmvif->time_event_data.link_id >= 0 && + mvmvif->time_event_data.link_id != notif_link_id, + "SESION_PROTECTION_NOTIF was received for link %u, while the current time event is on link %u\n", + notif_link_id, mvmvif->time_event_data.link_id)) + goto out_unlock; + /* The vif is not a P2P_DEVICE, maintain its time_event_data */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { struct iwl_mvm_time_event_data *te_data = @@ -880,8 +972,8 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, if (!le32_to_cpu(notif->status) || !le32_to_cpu(notif->start)) { /* End TE, notify mac80211 */ mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; - ieee80211_remain_on_channel_expired(mvm->hw); iwl_mvm_p2p_roc_finished(mvm); + ieee80211_remain_on_channel_expired(mvm->hw); } else if (le32_to_cpu(notif->start)) { if (WARN_ON(mvmvif->time_event_data.id != le32_to_cpu(notif->conf_id))) @@ -903,8 +995,7 @@ iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_session_prot_cmd cmd = { .id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), + cpu_to_le32(iwl_mvm_get_session_prot_id(mvm, vif, 0)), .action = cpu_to_le32(FW_CTXT_ACTION_ADD), .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), }; @@ -914,6 +1005,9 @@ iwl_mvm_start_p2p_roc_session_protection(struct iwl_mvm *mvm, /* The time_event_data.id field is reused to save session * protection's configuration. */ + + mvmvif->time_event_data.link_id = 0; + switch (type) { case IEEE80211_ROC_TYPE_NORMAL: mvmvif->time_event_data.id = @@ -1030,6 +1124,37 @@ void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm) __iwl_mvm_remove_time_event(mvm, te_data, &uid); } +static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity) +{ + int ret; + struct iwl_roc_req roc_cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .activity = cpu_to_le32(activity), + }; + + lockdep_assert_held(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + 0, sizeof(roc_cmd), &roc_cmd); + WARN_ON(ret); +} + +static void iwl_mvm_roc_station_remove(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif) +{ + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, ROC_CMD); + u8 fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + if (fw_ver == IWL_FW_CMD_VER_UNKNOWN) + iwl_mvm_remove_aux_roc_te(mvm, mvmvif, + &mvmvif->hs_time_event_data); + else if (fw_ver == 3) + iwl_mvm_roc_rm_cmd(mvm, ROC_ACTIVITY_HOTSPOT); + else + IWL_ERR(mvm, "ROC command version %d mismatch!\n", fw_ver); +} + void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif; @@ -1040,12 +1165,12 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif = iwl_mvm_vif_from_mac80211(vif); if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - iwl_mvm_cancel_session_protection(mvm, mvmvif, - mvmvif->time_event_data.id); + iwl_mvm_cancel_session_protection(mvm, vif, + mvmvif->time_event_data.id, + mvmvif->time_event_data.link_id); iwl_mvm_p2p_roc_finished(mvm); } else { - iwl_mvm_remove_aux_roc_te(mvm, mvmvif, - &mvmvif->hs_time_event_data); + iwl_mvm_roc_station_remove(mvm, mvmvif); iwl_mvm_roc_finished(mvm); } @@ -1164,25 +1289,28 @@ static bool iwl_mvm_session_prot_notif(struct iwl_notif_wait_data *notif_wait, void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 duration, u32 min_duration, - bool wait_for_notif) + bool wait_for_notif, + unsigned int link_id) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; const u16 notif[] = { WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF) }; struct iwl_notification_wait wait_notif; + int mac_link_id = iwl_mvm_get_session_prot_id(mvm, vif, link_id); struct iwl_mvm_session_prot_cmd cmd = { - .id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), + .id_and_color = cpu_to_le32(mac_link_id), .action = cpu_to_le32(FW_CTXT_ACTION_ADD), .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), }; + if (mac_link_id < 0) + return; + lockdep_assert_held(&mvm->mutex); spin_lock_bh(&mvm->time_event_lock); - if (te_data->running && + if (te_data->running && te_data->link_id == link_id && time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", jiffies_to_msecs(te_data->end_jiffies - jiffies)); @@ -1199,6 +1327,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, te_data->id = le32_to_cpu(cmd.conf_id); te_data->duration = le32_to_cpu(cmd.duration_tu); te_data->vif = vif; + te_data->link_id = link_id; spin_unlock_bh(&mvm->time_event_lock); IWL_DEBUG_TE(mvm, "Add new session protection, duration %d TU\n", @@ -1208,11 +1337,7 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd)) { - IWL_ERR(mvm, - "Couldn't send the SESSION_PROTECTION_CMD\n"); - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); + goto send_cmd_err; } return; @@ -1225,12 +1350,19 @@ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, SESSION_PROTECTION_CMD), 0, sizeof(cmd), &cmd)) { - IWL_ERR(mvm, - "Couldn't send the SESSION_PROTECTION_CMD\n"); iwl_remove_notification(&mvm->notif_wait, &wait_notif); + goto send_cmd_err; } else if (iwl_wait_notification(&mvm->notif_wait, &wait_notif, TU_TO_JIFFIES(100))) { IWL_ERR(mvm, "Failed to protect session until session protection\n"); } + return; + +send_cmd_err: + IWL_ERR(mvm, + "Couldn't send the SESSION_PROTECTION_CMD\n"); + spin_lock_bh(&mvm->time_event_lock); + iwl_mvm_te_clear_data(mvm, te_data); + spin_unlock_bh(&mvm->time_event_lock); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h index cf24efce90d0..49256ba4cf58 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -101,6 +101,14 @@ void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); /** + * iwl_mvm_rx_roc_notif - handles %DISCOVERY_ROC_NTF. + * @mvm: the mvm component + * @rxb: RX buffer + */ +void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/** * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality * @mvm: the mvm component * @vif: the virtual interface for which the roc is requested. It is assumed @@ -198,11 +206,13 @@ iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data) * @duration: the requested duration of the protection * @min_duration: the minimum duration of the protection * @wait_for_notif: if true, will block until the start of the protection + * @link_id: The link to schedule a session protection for */ void iwl_mvm_schedule_session_protection(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 duration, u32 min_duration, - bool wait_for_notif); + bool wait_for_notif, + unsigned int link_id); /** * iwl_mvm_rx_session_protect_notif - handles %SESSION_PROTECTION_NOTIF diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 157e96fa23c1..dee9c367dcd3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -642,7 +642,6 @@ static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device, int trip, int temp) { struct iwl_mvm *mvm = thermal_zone_device_priv(device); - struct iwl_mvm_thermal_device *tzone; int ret; mutex_lock(&mvm->mutex); @@ -658,12 +657,6 @@ static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device, goto out; } - tzone = &mvm->tz_device; - if (!tzone) { - ret = -EIO; - goto out; - } - ret = iwl_mvm_send_temp_report_ths_cmd(mvm); out: mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 674ddf951b79..ae5cd13cd6dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1623,7 +1623,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, seq_ctl = le16_to_cpu(tx_resp->seq_ctl); /* we can free until ssn % q.n_bd not inclusive */ - iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs); + iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs, false); while (!skb_queue_empty(&skbs)) { struct sk_buff *skb = __skb_dequeue(&skbs); @@ -1724,7 +1724,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, RS_DRV_DATA_PACK(lq_color, tx_resp->reduced_tpc); if (likely(!iwl_mvm_time_sync_frame(mvm, skb, hdr->addr1))) - ieee80211_tx_status(mvm->hw, skb); + ieee80211_tx_status_skb(mvm->hw, skb); } /* This is an aggregation queue or might become one, so we use @@ -1975,7 +1975,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, * block-ack window (we assume that they've been successfully * transmitted ... if not, it's too late anyway). */ - iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs); + iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs, is_flush); skb_queue_walk(&reclaimed_skbs, skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -2080,7 +2080,7 @@ out: while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(mvm->hw, skb); + ieee80211_tx_status_skb(mvm->hw, skb); } } @@ -2317,24 +2317,10 @@ free_rsp: return ret; } -int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal) +int iwl_mvm_flush_sta(struct iwl_mvm *mvm, u32 sta_id, u32 tfd_queue_mask) { - u32 sta_id, tfd_queue_msk; - - if (internal) { - struct iwl_mvm_int_sta *int_sta = sta; - - sta_id = int_sta->sta_id; - tfd_queue_msk = int_sta->tfd_queue_msk; - } else { - struct iwl_mvm_sta *mvm_sta = sta; - - 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, tfd_queue_msk); + return iwl_mvm_flush_tx_path(mvm, tfd_queue_mask); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 48016b4343d2..91286018a69d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -342,6 +342,60 @@ static bool iwl_wait_stats_complete(struct iwl_notif_wait_data *notif_wait, return true; } +static int iwl_mvm_request_system_statistics(struct iwl_mvm *mvm, bool clear, + u8 cmd_ver) +{ + struct iwl_system_statistics_cmd system_cmd = { + .cfg_mask = clear ? + cpu_to_le32(IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK) : + cpu_to_le32(IWL_STATS_CFG_FLG_RESET_MSK | + IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK), + .type_id_mask = cpu_to_le32(IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_CMD), + .len[0] = sizeof(system_cmd), + .data[0] = &system_cmd, + }; + struct iwl_notification_wait stats_wait; + static const u16 stats_complete[] = { + WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF), + }; + int ret; + + if (cmd_ver != 1) { + IWL_FW_CHECK_FAILED(mvm, + "Invalid system statistics command version:%d\n", + cmd_ver); + return -EOPNOTSUPP; + } + + iwl_init_notification_wait(&mvm->notif_wait, &stats_wait, + stats_complete, ARRAY_SIZE(stats_complete), + NULL, NULL); + + mvm->statistics_clear = clear; + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + iwl_remove_notification(&mvm->notif_wait, &stats_wait); + return ret; + } + + /* 500ms for OPERATIONAL, PART1 and END notification should be enough + * for FW to collect data from all LMACs and send + * STATISTICS_NOTIFICATION to host + */ + ret = iwl_wait_notification(&mvm->notif_wait, &stats_wait, HZ / 2); + if (ret) + return ret; + + if (clear) + iwl_mvm_accu_radio_stats(mvm); + + return ret; +} + int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear) { struct iwl_statistics_cmd scmd = { @@ -353,8 +407,15 @@ int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear) .len[0] = sizeof(scmd), .data[0] = &scmd, }; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(SYSTEM_GROUP, + SYSTEM_STATISTICS_CMD), + IWL_FW_CMD_VER_UNKNOWN); int ret; + if (cmd_ver != IWL_FW_CMD_VER_UNKNOWN) + return iwl_mvm_request_system_statistics(mvm, clear, cmd_ver); + /* From version 15 - STATISTICS_NOTIFICATION, the reply for * STATISTICS_CMD is empty, and the response is with * STATISTICS_NOTIFICATION notification diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index e8687683ff29..26a0953603ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1134,7 +1134,7 @@ static int get_crf_id(struct iwl_trans *iwl_trans) /* Enable access to peripheral registers */ val = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG); - val |= ENABLE_WFPM; + val |= WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK; iwl_write_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG, val); /* Read crf info */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index fa46dad5fd68..c9e5bda8f0b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -161,6 +161,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_synchronize_irqs(trans); iwl_pcie_rx_napi_sync(trans); iwl_txq_gen2_tx_free(trans); iwl_pcie_rx_stop(trans); @@ -230,11 +231,14 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int queue_size = max_t(u32, IWL_CMD_QUEUE_SIZE, trans->cfg->min_txq_size); + int ret; /* TODO: most of the logic can be removed in A0 - but not in Z0 */ spin_lock_bh(&trans_pcie->irq_lock); - iwl_pcie_gen2_apm_init(trans); + ret = iwl_pcie_gen2_apm_init(trans); spin_unlock_bh(&trans_pcie->irq_lock); + if (ret) + return ret; iwl_op_mode_nic_config(trans->op_mode); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 385e152f04fe..a468e5efeecd 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1264,6 +1264,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_synchronize_irqs(trans); iwl_pcie_rx_napi_sync(trans); iwl_pcie_tx_stop(trans); iwl_pcie_rx_stop(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index 340240b8954f..ca74b1b63cac 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -1575,7 +1575,7 @@ void iwl_txq_progress(struct iwl_txq *txq) /* Frees buffers until index _not_ inclusive */ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs) + struct sk_buff_head *skbs, bool is_flush) { struct iwl_txq *txq = trans->txqs.txq[txq_id]; int tfd_num, read_ptr, last_to_free; @@ -1650,9 +1650,11 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (iwl_txq_space(trans, txq) > txq->low_mark && test_bit(txq_id, trans->txqs.queue_stopped)) { struct sk_buff_head overflow_skbs; + struct sk_buff *skb; __skb_queue_head_init(&overflow_skbs); - skb_queue_splice_init(&txq->overflow_q, &overflow_skbs); + skb_queue_splice_init(&txq->overflow_q, + is_flush ? skbs : &overflow_skbs); /* * We are going to transmit from the overflow queue. @@ -1672,8 +1674,7 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, */ spin_unlock_bh(&txq->lock); - while (!skb_queue_empty(&overflow_skbs)) { - struct sk_buff *skb = __skb_dequeue(&overflow_skbs); + while ((skb = __skb_dequeue(&overflow_skbs))) { struct iwl_device_tx_cmd *dev_cmd_ptr; dev_cmd_ptr = *(void **)((u8 *)skb->cb + diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.h b/drivers/net/wireless/intel/iwlwifi/queue/tx.h index 52aa885af49b..124b29aac4a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.h @@ -181,7 +181,7 @@ void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_txq *txq, u16 byte_cnt, int num_tbs); void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs); + struct sk_buff_head *skbs, bool is_flush); void iwl_txq_set_q_ptrs(struct iwl_trans *trans, int txq_id, int ptr); void iwl_trans_txq_freeze_timer(struct iwl_trans *trans, unsigned long txqs, bool freeze); |