mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 15:36:55 +08:00
Merge tag 'wireless-next-2025-09-11' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes Berg says: ==================== Plenty of things going on, notably: - iwlwifi: major cleanups/rework - brcmfmac: gets AP isolation support - mac80211: gets more S1G support * tag 'wireless-next-2025-09-11' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (94 commits) wifi: mwifiex: fix endianness handling in mwifiex_send_rgpower_table wifi: cfg80211: Remove the redundant wiphy_dev wifi: mac80211: fix incorrect comment wifi: cfg80211: update the time stamps in hidden ssid wifi: mac80211: Fix HE capabilities element check wifi: mac80211: add tx_handlers_drop statistics to ethtool wifi: mac80211: fix reporting of all valid links in sta_set_sinfo() wifi: iwlwifi: mld: CHANNEL_SURVEY_NOTIF is always supported wifi: iwlwifi: mld: remove support of iwl_esr_mode_notif version 1 wifi: iwlwifi: mld: remove support from of sta cmd version 1 wifi: iwlwifi: mld: remove support of roc cmd version 5 wifi: iwlwifi: mld: remove support of mac cmd ver 2 wifi: iwlwifi: mld: don't consider phy cmd version 5 wifi: iwlwifi: implement wowlan status notification API update wifi: iwlwifi: fw: Add ASUS to PPAG and TAS list wifi: iwlwifi: add kunit tests for nvm parse wifi: iwlwifi: api: add a flag to iwl_link_ctx_modify_flags wifi: iwlwifi: pcie: move ltr_enabled to the specific transport wifi: iwlwifi: pcie: move pm_support to the specific transport wifi: iwlwifi: rename iwl_finish_nic_init ... ==================== Link: https://patch.msgid.link/20250911100854.20445-3-johannes@sipsolutions.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -555,7 +555,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
|
||||
/* Check whenever the PHY can be turned off again. */
|
||||
|
||||
/* 1. What about buffered unicast traffic for our AID? */
|
||||
cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid);
|
||||
cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid, false);
|
||||
|
||||
/* 2. Maybe the AP wants to send multicast/broadcast data? */
|
||||
cam |= !!(tim_ie->bitmap_ctrl & 0x01);
|
||||
|
||||
@@ -2708,6 +2708,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
|
||||
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
|
||||
wiphy->mgmt_stypes = wil_mgmt_stypes;
|
||||
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
|
||||
wiphy->bss_param_support = WIPHY_BSS_PARAM_AP_ISOLATE;
|
||||
|
||||
wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
|
||||
wiphy->vendor_commands = wil_nl80211_vendor_commands;
|
||||
|
||||
@@ -997,9 +997,9 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356, WCC),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359, WCC),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43751, WCC),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43752, WCC),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373, CYW),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012, CYW),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, CYW),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359, CYW),
|
||||
CYW_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW),
|
||||
{ /* end: all zeroes */ }
|
||||
|
||||
@@ -5958,6 +5958,26 @@ static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
|
||||
return brcmf_set_pmk(ifp, NULL, 0);
|
||||
}
|
||||
|
||||
static int brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct bss_parameters *params)
|
||||
{
|
||||
struct brcmf_if *ifp = netdev_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* In AP mode, the "ap_isolate" value represents
|
||||
* 0 = allow low-level bridging of frames between associated stations
|
||||
* 1 = restrict low-level bridging of frames to isolate associated stations
|
||||
* -1 = do not change existing setting
|
||||
*/
|
||||
if (params->ap_isolate >= 0) {
|
||||
ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", params->ap_isolate);
|
||||
if (ret < 0)
|
||||
brcmf_err("ap_isolate iovar failed: ret=%d\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cfg80211_ops brcmf_cfg80211_ops = {
|
||||
.add_virtual_intf = brcmf_cfg80211_add_iface,
|
||||
.del_virtual_intf = brcmf_cfg80211_del_iface,
|
||||
@@ -6005,6 +6025,7 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
|
||||
.update_connect_params = brcmf_cfg80211_update_conn_params,
|
||||
.set_pmk = brcmf_cfg80211_set_pmk,
|
||||
.del_pmk = brcmf_cfg80211_del_pmk,
|
||||
.change_bss = brcmf_cfg80211_change_bss,
|
||||
};
|
||||
|
||||
struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
|
||||
@@ -7659,6 +7680,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
|
||||
BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
|
||||
BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
|
||||
|
||||
wiphy->bss_param_support = WIPHY_BSS_PARAM_AP_ISOLATE;
|
||||
|
||||
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
|
||||
WIPHY_FLAG_PS_ON_BY_DEFAULT |
|
||||
WIPHY_FLAG_HAVE_AP_SME |
|
||||
|
||||
@@ -738,8 +738,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
|
||||
case BRCM_CC_4364_CHIP_ID:
|
||||
case CY_CC_4373_CHIP_ID:
|
||||
return 0x160000;
|
||||
case CY_CC_43752_CHIP_ID:
|
||||
case BRCM_CC_43751_CHIP_ID:
|
||||
case BRCM_CC_43752_CHIP_ID:
|
||||
case BRCM_CC_4377_CHIP_ID:
|
||||
return 0x170000;
|
||||
case BRCM_CC_4378_CHIP_ID:
|
||||
@@ -1452,7 +1452,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
|
||||
return (reg & CC_SR_CTL0_ENABLE_MASK) != 0;
|
||||
case BRCM_CC_4359_CHIP_ID:
|
||||
case BRCM_CC_43751_CHIP_ID:
|
||||
case CY_CC_43752_CHIP_ID:
|
||||
case BRCM_CC_43752_CHIP_ID:
|
||||
case CY_CC_43012_CHIP_ID:
|
||||
addr = CORE_CC_REG(pmu->base, retention_ctl);
|
||||
reg = chip->ops->read32(chip->ctx, addr);
|
||||
|
||||
@@ -554,12 +554,16 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
|
||||
data = (u8 *)fw->data;
|
||||
data_len = fw->size;
|
||||
} else {
|
||||
if ((data = bcm47xx_nvram_get_contents(&data_len)))
|
||||
data = bcm47xx_nvram_get_contents(&data_len);
|
||||
if (data) {
|
||||
free_bcm47xx_nvram = true;
|
||||
else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
|
||||
kfree_nvram = true;
|
||||
else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
|
||||
goto fail;
|
||||
} else {
|
||||
data = brcmf_fw_nvram_from_efi(&data_len);
|
||||
if (data)
|
||||
kfree_nvram = true;
|
||||
else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
|
||||
@@ -655,10 +655,10 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
|
||||
BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
|
||||
BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
|
||||
BRCMF_FW_ENTRY(BRCM_CC_43751_CHIP_ID, 0xFFFFFFFF, 43752),
|
||||
BRCMF_FW_ENTRY(BRCM_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752),
|
||||
BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
|
||||
BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012),
|
||||
BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439),
|
||||
BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752)
|
||||
};
|
||||
|
||||
#define TXCTL_CREDITS 2
|
||||
@@ -3426,8 +3426,8 @@ err:
|
||||
static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus)
|
||||
{
|
||||
if (bus->ci->chip == BRCM_CC_43751_CHIP_ID ||
|
||||
bus->ci->chip == CY_CC_43012_CHIP_ID ||
|
||||
bus->ci->chip == CY_CC_43752_CHIP_ID)
|
||||
bus->ci->chip == BRCM_CC_43752_CHIP_ID ||
|
||||
bus->ci->chip == CY_CC_43012_CHIP_ID)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@@ -4278,8 +4278,8 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
|
||||
|
||||
switch (sdiod->func1->device) {
|
||||
case SDIO_DEVICE_ID_BROADCOM_43751:
|
||||
case SDIO_DEVICE_ID_BROADCOM_43752:
|
||||
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
|
||||
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752:
|
||||
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
||||
CY_4373_F2_WATERMARK);
|
||||
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
#define CY_CC_4373_CHIP_ID 0x4373
|
||||
#define CY_CC_43012_CHIP_ID 43012
|
||||
#define CY_CC_43439_CHIP_ID 43439
|
||||
#define CY_CC_43752_CHIP_ID 43752
|
||||
|
||||
/* USB Device IDs */
|
||||
#define BRCM_USB_43143_DEVICE_ID 0xbd1e
|
||||
|
||||
@@ -50,28 +50,4 @@ struct ieee80211_measurement_params {
|
||||
__le16 duration;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_info_element {
|
||||
u8 id;
|
||||
u8 len;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_measurement_request {
|
||||
struct ieee80211_info_element ie;
|
||||
u8 token;
|
||||
u8 mode;
|
||||
u8 type;
|
||||
struct ieee80211_measurement_params params[];
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_measurement_report {
|
||||
struct ieee80211_info_element ie;
|
||||
u8 token;
|
||||
u8 mode;
|
||||
u8 type;
|
||||
union {
|
||||
struct ieee80211_basic_report basic[0];
|
||||
} u;
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "iwl-prph.h"
|
||||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware API version supported */
|
||||
#define IWL_BZ_UCODE_API_MAX 102
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_BZ_UCODE_CORE_MAX 99
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_BZ_UCODE_API_MIN 98
|
||||
#define IWL_BZ_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_BZ_SMEM_OFFSET 0x400000
|
||||
@@ -75,7 +75,7 @@ static const struct iwl_family_base_params iwl_bz_base = {
|
||||
},
|
||||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = IWL_BZ_UCODE_API_MAX,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_BZ_UCODE_API_MIN,
|
||||
};
|
||||
|
||||
@@ -101,8 +101,8 @@ const struct iwl_mac_cfg iwl_gl_mac_cfg = {
|
||||
.low_latency_xtal = true,
|
||||
};
|
||||
|
||||
IWL_FW_AND_PNVM(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_API_MAX);
|
||||
IWL_CORE_FW(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX);
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#include "iwl-prph.h"
|
||||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware API version supported */
|
||||
#define IWL_DR_UCODE_API_MAX 102
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_DR_UCODE_CORE_MAX 99
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_DR_UCODE_API_MIN 98
|
||||
#define IWL_DR_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_DR_SMEM_OFFSET 0x400000
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
#define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0"
|
||||
|
||||
#define IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_DR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
static const struct iwl_family_base_params iwl_dr_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 65536,
|
||||
@@ -73,7 +70,7 @@ static const struct iwl_family_base_params iwl_dr_base = {
|
||||
},
|
||||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = IWL_DR_UCODE_API_MAX,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_DR_UCODE_API_MIN,
|
||||
};
|
||||
|
||||
@@ -89,5 +86,5 @@ const struct iwl_mac_cfg iwl_dr_mac_cfg = {
|
||||
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
|
||||
};
|
||||
|
||||
MODULE_FIRMWARE(IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX));
|
||||
IWL_CORE_FW(IWL_DR_A_PE_A_FW_PRE, IWL_DR_UCODE_CORE_MAX);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define IWL_GF_UCODE_API_MAX 100
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_GF_UCODE_API_MIN 98
|
||||
#define IWL_GF_UCODE_API_MIN 100
|
||||
|
||||
#define IWL_SO_A_GF_A_FW_PRE "iwlwifi-so-a0-gf-a0"
|
||||
#define IWL_TY_A_GF_A_FW_PRE "iwlwifi-ty-a0-gf-a0"
|
||||
@@ -23,6 +23,18 @@
|
||||
#define IWL_SC_A_GF_A_FW_PRE "iwlwifi-sc-a0-gf-a0"
|
||||
#define IWL_SC_A_GF4_A_FW_PRE "iwlwifi-sc-a0-gf4-a0"
|
||||
|
||||
#define IWL_BZ_A_GF_A_MODULE_FIRMWARE(api) \
|
||||
IWL_BZ_A_GF_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
#define IWL_BZ_A_GF4_A_MODULE_FIRMWARE(api) \
|
||||
IWL_BZ_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
#define IWL_SC_A_GF_A_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_GF_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
#define IWL_SC_A_GF4_A_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_GF_NVM_VERSION 0x0a1d
|
||||
|
||||
@@ -67,7 +79,7 @@ IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_BZ_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_BZ_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_GF4_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define IWL_HR_UCODE_API_MAX 100
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_HR_UCODE_API_MIN 98
|
||||
#define IWL_HR_UCODE_API_MIN 100
|
||||
|
||||
#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0"
|
||||
#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0"
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "iwl-prph.h"
|
||||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware API version supported */
|
||||
#define IWL_SC_UCODE_API_MAX 102
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_SC_UCODE_CORE_MAX 99
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_SC_UCODE_API_MIN 98
|
||||
#define IWL_SC_UCODE_API_MIN 100
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_SC_NVM_VERSION 0x0a1d
|
||||
@@ -78,7 +78,7 @@ static const struct iwl_family_base_params iwl_sc_base = {
|
||||
},
|
||||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = IWL_SC_UCODE_API_MAX,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_SC_UCODE_API_MIN,
|
||||
};
|
||||
|
||||
@@ -94,8 +94,8 @@ const struct iwl_mac_cfg iwl_sc_mac_cfg = {
|
||||
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
|
||||
};
|
||||
|
||||
IWL_FW_AND_PNVM(IWL_SC_A_FM_B_FW_PRE, IWL_SC_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC_A_WH_A_FW_PRE, IWL_SC_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC2_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SC2_A_WH_A_FW_PRE, IWL_SC_UCODE_API_MAX);
|
||||
IWL_CORE_FW(IWL_SC_A_FM_B_FW_PRE, IWL_SC_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_SC_A_FM_C_FW_PRE, IWL_SC_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_SC_A_WH_A_FW_PRE, IWL_SC_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_SC2_A_FM_C_FW_PRE, IWL_SC_UCODE_CORE_MAX);
|
||||
IWL_CORE_FW(IWL_SC2_A_WH_A_FW_PRE, IWL_SC_UCODE_CORE_MAX);
|
||||
|
||||
@@ -766,7 +766,7 @@ static int iwl_init_otp_access(struct iwl_trans *trans)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
|
||||
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
||||
|
||||
iwl_trans_d3_suspend(priv->trans, false, true);
|
||||
iwl_trans_d3_suspend(priv->trans, true);
|
||||
|
||||
goto out;
|
||||
|
||||
@@ -422,7 +422,6 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
|
||||
struct ieee80211_vif *vif;
|
||||
u32 base;
|
||||
int ret;
|
||||
enum iwl_d3_status d3_status;
|
||||
struct error_table_start {
|
||||
/* cf. struct iwl_error_event_table */
|
||||
u32 valid;
|
||||
@@ -451,15 +450,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
|
||||
/* we'll clear ctx->vif during iwlagn_prepare_restart() */
|
||||
vif = ctx->vif;
|
||||
|
||||
ret = iwl_trans_d3_resume(priv->trans, &d3_status, false, true);
|
||||
ret = iwl_trans_d3_resume(priv->trans, true);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
if (d3_status != IWL_D3_STATUS_ALIVE) {
|
||||
IWL_INFO(priv, "Device was reset during suspend\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* uCode is no longer operating by itself */
|
||||
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
||||
|
||||
@@ -368,7 +368,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
|
||||
/* initialize to default */
|
||||
void iwl_power_initialize(struct iwl_priv *priv)
|
||||
{
|
||||
priv->power_data.bus_pm = priv->trans->pm_support;
|
||||
priv->power_data.bus_pm = iwl_trans_is_pm_supported(priv->trans);
|
||||
|
||||
priv->power_data.debug_sleep_level_override = -1;
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "acpi.h"
|
||||
#include "fw/runtime.h"
|
||||
|
||||
const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
|
||||
0xA5, 0xB3, 0x1F, 0x73,
|
||||
0x8E, 0x28, 0x5A, 0xDE);
|
||||
static const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
|
||||
0xA5, 0xB3, 0x1F, 0x73,
|
||||
0x8E, 0x28, 0x5A, 0xDE);
|
||||
|
||||
static const size_t acpi_dsm_size[DSM_FUNC_NUM_FUNCS] = {
|
||||
[DSM_FUNC_QUERY] = sizeof(u32),
|
||||
|
||||
@@ -140,8 +140,6 @@ struct iwl_dsm_internal_product_reset_cmd {
|
||||
|
||||
struct iwl_fw_runtime;
|
||||
|
||||
extern const guid_t iwl_guid;
|
||||
|
||||
union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,
|
||||
int func, union acpi_object *args,
|
||||
const guid_t *guid);
|
||||
|
||||
@@ -367,6 +367,7 @@ enum iwl_wowlan_flags {
|
||||
ENABLE_NBNS_FILTERING = BIT(2),
|
||||
ENABLE_DHCP_FILTERING = BIT(3),
|
||||
ENABLE_STORE_BEACON = BIT(4),
|
||||
HAS_BEACON_PROTECTION = BIT(5),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -631,9 +632,64 @@ struct iwl_wowlan_gtk_status_v3 {
|
||||
struct iwl_wowlan_all_rsc_tsc_v5 sc;
|
||||
} __packed; /* WOWLAN_GTK_MATERIAL_VER_3 */
|
||||
|
||||
/**
|
||||
* enum iwl_wowlan_key_status - Status of security keys in WoWLAN notifications
|
||||
* @IWL_WOWLAN_NOTIF_NO_KEY: No key is present; this entry should be ignored.
|
||||
* @IWL_WOWLAN_STATUS_OLD_KEY: old key exists; no rekey occurred, and only
|
||||
* metadata is available.
|
||||
* @IWL_WOWLAN_STATUS_NEW_KEY: A new key was created after a rekey; new key
|
||||
* material is available.
|
||||
*/
|
||||
enum iwl_wowlan_key_status {
|
||||
IWL_WOWLAN_NOTIF_NO_KEY = 0,
|
||||
IWL_WOWLAN_STATUS_OLD_KEY = 1,
|
||||
IWL_WOWLAN_STATUS_NEW_KEY = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_gtk_status - GTK status
|
||||
* @key: GTK material
|
||||
* @key_len: GTK length, if set to 0, the key is not available
|
||||
* @key_flags: information about the key:
|
||||
* bits[0:1]: key index assigned by the AP
|
||||
* bits[2:6]: GTK index of the key in the internal DB
|
||||
* bit[7]: Set iff this is the currently used GTK
|
||||
* @key_status: key status, see &enum iwl_wowlan_key_status
|
||||
* @reserved: padding
|
||||
* @tkip_mic_key: TKIP RX MIC key
|
||||
* @sc: RSC/TSC counters
|
||||
*/
|
||||
struct iwl_wowlan_gtk_status {
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
u8 key_len;
|
||||
u8 key_flags;
|
||||
u8 key_status;
|
||||
u8 reserved;
|
||||
u8 tkip_mic_key[IWL_MIC_KEY_SIZE];
|
||||
struct iwl_wowlan_all_rsc_tsc_v5 sc;
|
||||
} __packed; /* WOWLAN_GTK_MATERIAL_VER_4 */
|
||||
|
||||
#define IWL_WOWLAN_GTK_IDX_MASK (BIT(0) | BIT(1))
|
||||
#define IWL_WOWLAN_IGTK_BIGTK_IDX_MASK (BIT(0))
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_igtk_status_v1 - IGTK status
|
||||
* @key: IGTK material
|
||||
* @ipn: the IGTK packet number (replay counter)
|
||||
* @key_len: IGTK length, if set to 0, the key is not available
|
||||
* @key_flags: information about the key:
|
||||
* bits[0]: key index assigned by the AP (0: index 4, 1: index 5)
|
||||
* (0: index 6, 1: index 7 with bigtk)
|
||||
* bits[1:5]: IGTK index of the key in the internal DB
|
||||
* bit[6]: Set iff this is the currently used IGTK
|
||||
*/
|
||||
struct iwl_wowlan_igtk_status_v1 {
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
u8 ipn[6];
|
||||
u8 key_len;
|
||||
u8 key_flags;
|
||||
} __packed; /* WOWLAN_IGTK_MATERIAL_VER_1 */
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_igtk_status - IGTK status
|
||||
* @key: IGTK material
|
||||
@@ -644,13 +700,17 @@ struct iwl_wowlan_gtk_status_v3 {
|
||||
* (0: index 6, 1: index 7 with bigtk)
|
||||
* bits[1:5]: IGTK index of the key in the internal DB
|
||||
* bit[6]: Set iff this is the currently used IGTK
|
||||
* @key_status: key status, see &enum iwl_wowlan_key_status
|
||||
* @reserved: padding
|
||||
*/
|
||||
struct iwl_wowlan_igtk_status {
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
u8 ipn[6];
|
||||
u8 key_len;
|
||||
u8 key_flags;
|
||||
} __packed; /* WOWLAN_IGTK_MATERIAL_VER_1 */
|
||||
u8 key_status;
|
||||
u8 reserved[3];
|
||||
} __packed; /* WOWLAN_IGTK_MATERIAL_VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_status_v6 - WoWLAN status
|
||||
@@ -700,7 +760,7 @@ struct iwl_wowlan_status_v6 {
|
||||
*/
|
||||
struct iwl_wowlan_status_v7 {
|
||||
struct iwl_wowlan_gtk_status_v2 gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
__le64 replay_ctr;
|
||||
__le16 pattern_number;
|
||||
__le16 non_qos_seq_ctr;
|
||||
@@ -735,7 +795,7 @@ struct iwl_wowlan_status_v7 {
|
||||
*/
|
||||
struct iwl_wowlan_info_notif_v1 {
|
||||
struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
__le64 replay_ctr;
|
||||
__le16 pattern_number;
|
||||
__le16 reserved1;
|
||||
@@ -817,8 +877,8 @@ struct iwl_wowlan_mlo_gtk {
|
||||
*/
|
||||
struct iwl_wowlan_info_notif_v3 {
|
||||
struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status bigtk[WOWLAN_BIGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 bigtk[WOWLAN_BIGTK_KEYS_NUM];
|
||||
__le64 replay_ctr;
|
||||
__le16 pattern_number;
|
||||
__le16 reserved1;
|
||||
@@ -832,6 +892,45 @@ struct iwl_wowlan_info_notif_v3 {
|
||||
u8 reserved2[2];
|
||||
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_3 */
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_info_notif_v5 - WoWLAN information notification
|
||||
* @gtk: GTK data
|
||||
* @igtk: IGTK data
|
||||
* @bigtk: BIGTK data
|
||||
* @replay_ctr: GTK rekey replay counter
|
||||
* @pattern_number: number of the matched patterns
|
||||
* @qos_seq_ctr: QoS sequence counters to use next
|
||||
* @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason
|
||||
* @num_of_gtk_rekeys: number of GTK rekeys
|
||||
* @transmitted_ndps: number of transmitted neighbor discovery packets
|
||||
* @received_beacons: number of received beacons
|
||||
* @tid_tear_down: bit mask of tids whose BA sessions were closed
|
||||
* in suspend state
|
||||
* @station_id: station id
|
||||
* @num_mlo_link_keys: number of &struct iwl_wowlan_mlo_gtk structs
|
||||
* following this notif
|
||||
* @tid_offloaded_tx: tid used by the firmware to transmit data packets
|
||||
* while in wowlan
|
||||
* @mlo_gtks: array of GTKs of size num_mlo_link_keys
|
||||
*/
|
||||
struct iwl_wowlan_info_notif_v5 {
|
||||
struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status_v1 bigtk[WOWLAN_BIGTK_KEYS_NUM];
|
||||
__le64 replay_ctr;
|
||||
__le16 pattern_number;
|
||||
__le16 qos_seq_ctr;
|
||||
__le32 wakeup_reasons;
|
||||
__le32 num_of_gtk_rekeys;
|
||||
__le32 transmitted_ndps;
|
||||
__le32 received_beacons;
|
||||
u8 tid_tear_down;
|
||||
u8 station_id;
|
||||
u8 num_mlo_link_keys;
|
||||
u8 tid_offloaded_tx;
|
||||
struct iwl_wowlan_mlo_gtk mlo_gtks[];
|
||||
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_5 */
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_info_notif - WoWLAN information notification
|
||||
* @gtk: GTK data
|
||||
@@ -854,7 +953,7 @@ struct iwl_wowlan_info_notif_v3 {
|
||||
* @mlo_gtks: array of GTKs of size num_mlo_link_keys
|
||||
*/
|
||||
struct iwl_wowlan_info_notif {
|
||||
struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_gtk_status gtk[WOWLAN_GTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status igtk[WOWLAN_IGTK_KEYS_NUM];
|
||||
struct iwl_wowlan_igtk_status bigtk[WOWLAN_BIGTK_KEYS_NUM];
|
||||
__le64 replay_ctr;
|
||||
@@ -869,7 +968,7 @@ struct iwl_wowlan_info_notif {
|
||||
u8 num_mlo_link_keys;
|
||||
u8 tid_offloaded_tx;
|
||||
struct iwl_wowlan_mlo_gtk mlo_gtks[];
|
||||
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_5 */
|
||||
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_6 */
|
||||
|
||||
/**
|
||||
* struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification
|
||||
|
||||
@@ -420,6 +420,8 @@ struct iwl_mac_config_cmd {
|
||||
* eht_support set to true. No longer used since _VER_3 of this command.
|
||||
* @LINK_CONTEXT_MODIFY_BANDWIDTH: Covers iwl_link_ctx_cfg_cmd::modify_bandwidth.
|
||||
* Request RX OMI to the AP to modify bandwidth of this link.
|
||||
* @LINK_CONTEXT_MODIFY_UHR_PARAMS: covers iwl_link_ctx_cfg_cmd::npca_params and
|
||||
* iwl_link_ctx_cfg_cmd::prio_edca_params. Since _VER_7.
|
||||
* @LINK_CONTEXT_MODIFY_ALL: set all above flags
|
||||
*/
|
||||
enum iwl_link_ctx_modify_flags {
|
||||
@@ -432,6 +434,7 @@ enum iwl_link_ctx_modify_flags {
|
||||
LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE = BIT(6),
|
||||
LINK_CONTEXT_MODIFY_EHT_PARAMS = BIT(7),
|
||||
LINK_CONTEXT_MODIFY_BANDWIDTH = BIT(8),
|
||||
LINK_CONTEXT_MODIFY_UHR_PARAMS = BIT(9),
|
||||
LINK_CONTEXT_MODIFY_ALL = 0xff,
|
||||
}; /* LINK_CONTEXT_MODIFY_MASK_E_VER_1 */
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ enum iwl_prot_offload_subcmd_ids {
|
||||
/**
|
||||
* @WOWLAN_INFO_NOTIFICATION: Notification in
|
||||
* &struct iwl_wowlan_info_notif_v1, iwl_wowlan_info_notif_v3,
|
||||
* or &struct iwl_wowlan_info_notif
|
||||
* &struct iwl_wowlan_info_notif_v5 or &struct iwl_wowlan_info_notif
|
||||
*/
|
||||
WOWLAN_INFO_NOTIFICATION = 0xFD,
|
||||
|
||||
|
||||
@@ -571,17 +571,16 @@ enum iwl_ppag_flags {
|
||||
/**
|
||||
* union iwl_ppag_table_cmd - union for all versions of PPAG command
|
||||
* @v1: command version 1 structure.
|
||||
* @v2: command version from 2 to 6 are same structure as v2.
|
||||
* but has a different format of the flags bitmap
|
||||
* @v3: command version 7 structure.
|
||||
* @v5: command version 5 structure.
|
||||
* @v7: command version 7 structure.
|
||||
* @v1.flags: values from &enum iwl_ppag_flags
|
||||
* @v1.gain: table of antenna gain values per chain and sub-band
|
||||
* @v1.reserved: reserved
|
||||
* @v2.flags: values from &enum iwl_ppag_flags
|
||||
* @v2.gain: table of antenna gain values per chain and sub-band
|
||||
* @v3.ppag_config_info: see @struct bios_value_u32
|
||||
* @v3.gain: table of antenna gain values per chain and sub-band
|
||||
* @v3.reserved: reserved
|
||||
* @v5.flags: values from &enum iwl_ppag_flags
|
||||
* @v5.gain: table of antenna gain values per chain and sub-band
|
||||
* @v7.ppag_config_info: see @struct bios_value_u32
|
||||
* @v7.gain: table of antenna gain values per chain and sub-band
|
||||
* @v7.reserved: reserved
|
||||
*/
|
||||
union iwl_ppag_table_cmd {
|
||||
struct {
|
||||
@@ -593,30 +592,19 @@ union iwl_ppag_table_cmd {
|
||||
__le32 flags;
|
||||
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
|
||||
s8 reserved[2];
|
||||
} __packed v2; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_2, VER3, VER4,
|
||||
* VER5, VER6
|
||||
*/
|
||||
} __packed v5; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_5 */
|
||||
struct {
|
||||
struct bios_value_u32 ppag_config_info;
|
||||
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
|
||||
s8 reserved[2];
|
||||
} __packed v3; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */
|
||||
} __packed v7; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */
|
||||
} __packed;
|
||||
|
||||
#define IWL_PPAG_CMD_V4_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK)
|
||||
#define IWL_PPAG_CMD_V5_MASK (IWL_PPAG_CMD_V4_MASK | \
|
||||
#define IWL_PPAG_CMD_V1_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK)
|
||||
#define IWL_PPAG_CMD_V5_MASK (IWL_PPAG_CMD_V1_MASK | \
|
||||
IWL_PPAG_ETSI_LPI_UHB_MASK | \
|
||||
IWL_PPAG_USA_LPI_UHB_MASK)
|
||||
|
||||
#define IWL_PPAG_CMD_V6_MASK (IWL_PPAG_CMD_V5_MASK | \
|
||||
IWL_PPAG_ETSI_VLP_UHB_MASK | \
|
||||
IWL_PPAG_ETSI_SP_UHB_MASK | \
|
||||
IWL_PPAG_USA_VLP_UHB_MASK | \
|
||||
IWL_PPAG_USA_SP_UHB_MASK | \
|
||||
IWL_PPAG_CANADA_LPI_UHB_MASK | \
|
||||
IWL_PPAG_CANADA_VLP_UHB_MASK | \
|
||||
IWL_PPAG_CANADA_SP_UHB_MASK)
|
||||
|
||||
#define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE 26
|
||||
#define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE 13
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
* for BPSK (MCS 0) with 2 spatial
|
||||
* streams
|
||||
* @IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK: enable support for EHT extra LTF
|
||||
* @IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK: support ELR 1.5 Mbps
|
||||
* @IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK: support ELR 3 Mbps
|
||||
*/
|
||||
enum iwl_tlc_mng_cfg_flags {
|
||||
IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(0),
|
||||
@@ -32,6 +34,8 @@ enum iwl_tlc_mng_cfg_flags {
|
||||
IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK = BIT(3),
|
||||
IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK = BIT(4),
|
||||
IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK = BIT(6),
|
||||
IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK = BIT(7),
|
||||
IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK = BIT(8),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -200,6 +204,37 @@ struct iwl_tlc_config_cmd_v4 {
|
||||
__le16 max_tx_op;
|
||||
} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_4 */
|
||||
|
||||
/**
|
||||
* struct iwl_tlc_config_cmd - TLC configuration
|
||||
* @sta_id: station id
|
||||
* @reserved1: reserved
|
||||
* @max_ch_width: max supported channel width from &enum iwl_tlc_mng_cfg_cw
|
||||
* @mode: &enum iwl_tlc_mng_cfg_mode
|
||||
* @chains: bitmask of &enum iwl_tlc_mng_cfg_chains
|
||||
* @sgi_ch_width_supp: bitmap of SGI support per channel width
|
||||
* use BIT(&enum iwl_tlc_mng_cfg_cw)
|
||||
* @flags: bitmask of &enum iwl_tlc_mng_cfg_flags
|
||||
* @non_ht_rates: bitmap of supported legacy rates
|
||||
* @ht_rates: bitmap of &enum iwl_tlc_mng_ht_rates, per <nss, channel-width>
|
||||
* pair (0 - 80mhz width and below, 1 - 160mhz, 2 - 320mhz).
|
||||
* @max_mpdu_len: max MPDU length, in bytes
|
||||
* @max_tx_op: max TXOP in uSecs for all AC (BK, BE, VO, VI),
|
||||
* set zero for no limit.
|
||||
*/
|
||||
struct iwl_tlc_config_cmd {
|
||||
u8 sta_id;
|
||||
u8 reserved1[3];
|
||||
u8 max_ch_width;
|
||||
u8 mode;
|
||||
u8 chains;
|
||||
u8 sgi_ch_width_supp;
|
||||
__le16 flags;
|
||||
__le16 non_ht_rates;
|
||||
__le32 ht_rates[IWL_TLC_NSS_MAX][IWL_TLC_MCS_PER_BW_NUM_V4];
|
||||
__le16 max_mpdu_len;
|
||||
__le16 max_tx_op;
|
||||
} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_5 */
|
||||
|
||||
/**
|
||||
* enum iwl_tlc_update_flags - updated fields
|
||||
* @IWL_TLC_NOTIF_FLAG_RATE: last initial rate update
|
||||
|
||||
@@ -830,7 +830,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
|
||||
}
|
||||
|
||||
/* reading RXF/TXF sizes */
|
||||
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
|
||||
if (iwl_trans_is_fw_error(fwrt->trans)) {
|
||||
fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
|
||||
fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
|
||||
|
||||
@@ -2393,7 +2393,7 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
|
||||
struct iwl_fw_ini_dump_cfg_name *cfg_name;
|
||||
u32 size = sizeof(*tlv) + sizeof(*dump);
|
||||
u32 num_of_cfg_names = 0;
|
||||
u32 hw_type, is_cdb, is_jacket;
|
||||
u32 hw_type, is_cdb;
|
||||
|
||||
list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) {
|
||||
size += sizeof(*cfg_name);
|
||||
@@ -2426,11 +2426,7 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
|
||||
hw_type = CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev);
|
||||
|
||||
is_cdb = CSR_HW_RFID_IS_CDB(fwrt->trans->info.hw_rf_id);
|
||||
is_jacket = !!(iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR) &
|
||||
WFPM_OTP_CFG1_IS_JACKET_BIT);
|
||||
|
||||
/* Use bits 12 and 13 to indicate jacket/CDB, respectively */
|
||||
hw_type |= (is_jacket | (is_cdb << 1)) << IWL_JACKET_CDB_SHIFT;
|
||||
hw_type |= IWL_CDB_MASK(is_cdb);
|
||||
|
||||
dump->hw_type = cpu_to_le32(hw_type);
|
||||
|
||||
@@ -2478,36 +2474,6 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,
|
||||
return entry->size;
|
||||
}
|
||||
|
||||
static u32 iwl_dump_ini_file_name_info(struct iwl_fw_runtime *fwrt,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct iwl_fw_ini_dump_entry *entry;
|
||||
struct iwl_dump_file_name_info *tlv;
|
||||
u32 len = strnlen(fwrt->trans->dbg.dump_file_name_ext,
|
||||
IWL_FW_INI_MAX_NAME);
|
||||
|
||||
if (!fwrt->trans->dbg.dump_file_name_ext_valid)
|
||||
return 0;
|
||||
|
||||
entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + len);
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
entry->size = sizeof(*tlv) + len;
|
||||
|
||||
tlv = (void *)entry->data;
|
||||
tlv->type = cpu_to_le32(IWL_INI_DUMP_NAME_TYPE);
|
||||
tlv->len = cpu_to_le32(len);
|
||||
memcpy(tlv->data, fwrt->trans->dbg.dump_file_name_ext, len);
|
||||
|
||||
/* add the dump file name extension tlv to the list */
|
||||
list_add_tail(&entry->list, list);
|
||||
|
||||
fwrt->trans->dbg.dump_file_name_ext_valid = false;
|
||||
|
||||
return entry->size;
|
||||
}
|
||||
|
||||
static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = {
|
||||
[IWL_FW_INI_REGION_INVALID] = {},
|
||||
[IWL_FW_INI_REGION_INTERNAL_BUFFER] = {
|
||||
@@ -2764,7 +2730,6 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
|
||||
&iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]);
|
||||
|
||||
if (size) {
|
||||
size += iwl_dump_ini_file_name_info(fwrt, list);
|
||||
size += iwl_dump_ini_info(fwrt, trigger, list);
|
||||
}
|
||||
|
||||
@@ -3151,7 +3116,7 @@ static void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
|
||||
.len[0] = sizeof(hcmd_data),
|
||||
};
|
||||
|
||||
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status))
|
||||
if (iwl_trans_is_fw_error(fwrt->trans))
|
||||
return;
|
||||
|
||||
if (fw_has_capa(&fwrt->fw->ucode_capa,
|
||||
|
||||
@@ -14,13 +14,6 @@
|
||||
#include "iwl-csr.h"
|
||||
#include "pnvm.h"
|
||||
|
||||
#define FW_ASSERT_LMAC_FATAL 0x70
|
||||
#define FW_ASSERT_LMAC2_FATAL 0x72
|
||||
#define FW_ASSERT_UMAC_FATAL 0x71
|
||||
#define UMAC_RT_NMI_LMAC2_FATAL 0x72
|
||||
#define RT_NMI_INTERRUPT_OTHER_LMAC_FATAL 0x73
|
||||
#define FW_ASSERT_NMI_UNKNOWN 0x84
|
||||
|
||||
/*
|
||||
* Note: This structure is read from the device with IO accesses,
|
||||
* and the reading already does the endian conversion. As it is
|
||||
@@ -103,17 +96,6 @@ struct iwl_umac_error_event_table {
|
||||
#define ERROR_START_OFFSET (1 * sizeof(u32))
|
||||
#define ERROR_ELEM_SIZE (7 * sizeof(u32))
|
||||
|
||||
static bool iwl_fwrt_if_errorid_other_cpu(u32 err_id)
|
||||
{
|
||||
err_id &= 0xFF;
|
||||
|
||||
if ((err_id >= FW_ASSERT_LMAC_FATAL &&
|
||||
err_id <= RT_NMI_INTERRUPT_OTHER_LMAC_FATAL) ||
|
||||
err_id == FW_ASSERT_NMI_UNKNOWN)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)
|
||||
{
|
||||
struct iwl_trans *trans = fwrt->trans;
|
||||
@@ -131,13 +113,6 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)
|
||||
if (table.valid)
|
||||
fwrt->dump.umac_err_id = table.error_id;
|
||||
|
||||
if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.umac_err_id) &&
|
||||
!fwrt->trans->dbg.dump_file_name_ext_valid) {
|
||||
fwrt->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"0x%x", fwrt->dump.umac_err_id);
|
||||
}
|
||||
|
||||
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
|
||||
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
|
||||
IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n",
|
||||
@@ -203,7 +178,7 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu
|
||||
if (err)
|
||||
return;
|
||||
|
||||
err = iwl_finish_nic_init(trans);
|
||||
err = iwl_trans_activate_nic(trans);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
@@ -213,13 +188,6 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu
|
||||
if (table.valid)
|
||||
fwrt->dump.lmac_err_id[lmac_num] = table.error_id;
|
||||
|
||||
if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.lmac_err_id[lmac_num]) &&
|
||||
!fwrt->trans->dbg.dump_file_name_ext_valid) {
|
||||
fwrt->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"0x%x", fwrt->dump.lmac_err_id[lmac_num]);
|
||||
}
|
||||
|
||||
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
|
||||
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
|
||||
IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n",
|
||||
@@ -305,16 +273,6 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
|
||||
|
||||
iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
|
||||
|
||||
if (table.valid)
|
||||
fwrt->dump.tcm_err_id[idx] = table.error_id;
|
||||
|
||||
if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.tcm_err_id[idx]) &&
|
||||
!fwrt->trans->dbg.dump_file_name_ext_valid) {
|
||||
fwrt->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"0x%x", fwrt->dump.tcm_err_id[idx]);
|
||||
}
|
||||
|
||||
IWL_ERR(fwrt, "TCM%d status:\n", idx + 1);
|
||||
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
|
||||
IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2);
|
||||
@@ -378,16 +336,6 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
|
||||
|
||||
iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
|
||||
|
||||
if (table.valid)
|
||||
fwrt->dump.rcm_err_id[idx] = table.error_id;
|
||||
|
||||
if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.rcm_err_id[idx]) &&
|
||||
!fwrt->trans->dbg.dump_file_name_ext_valid) {
|
||||
fwrt->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"0x%x", fwrt->dump.rcm_err_id[idx]);
|
||||
}
|
||||
|
||||
IWL_ERR(fwrt, "RCM%d status:\n", idx + 1);
|
||||
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
|
||||
IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2);
|
||||
|
||||
@@ -372,7 +372,8 @@ struct iwl_fw_ini_dump_cfg_name {
|
||||
u8 cfg_name[IWL_FW_INI_MAX_CFG_NAME];
|
||||
} __packed;
|
||||
|
||||
#define IWL_JACKET_CDB_SHIFT 12
|
||||
#define IWL_CDB_MASK(val) val << 13
|
||||
|
||||
|
||||
/* struct iwl_fw_ini_dump_info - ini dump information
|
||||
* @version: dump version
|
||||
|
||||
@@ -237,11 +237,12 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
|
||||
static u8 *iwl_pnvm_get_from_fs(struct iwl_trans *trans, size_t *len)
|
||||
{
|
||||
const struct firmware *pnvm;
|
||||
char pnvm_name[MAX_PNVM_NAME];
|
||||
size_t new_len;
|
||||
u8 *data;
|
||||
int ret;
|
||||
|
||||
iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name));
|
||||
@@ -250,29 +251,73 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len)
|
||||
if (ret) {
|
||||
IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
|
||||
pnvm_name, ret);
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_len = pnvm->size;
|
||||
*data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
|
||||
data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL);
|
||||
release_firmware(pnvm);
|
||||
|
||||
if (!*data)
|
||||
return -ENOMEM;
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
*len = new_len;
|
||||
|
||||
return 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* enum iwl_pnvm_source - different PNVM possible sources
|
||||
*
|
||||
* @IWL_PNVM_SOURCE_NONE: No PNVM.
|
||||
* @IWL_PNVM_SOURCE_BIOS: PNVM should be read from BIOS.
|
||||
* @IWL_PNVM_SOURCE_EXTERNAL: read .pnvm external file
|
||||
* @IWL_PNVM_SOURCE_EMBEDDED: PNVM is embedded in the .ucode file.
|
||||
*/
|
||||
enum iwl_pnvm_source {
|
||||
IWL_PNVM_SOURCE_NONE,
|
||||
IWL_PNVM_SOURCE_BIOS,
|
||||
IWL_PNVM_SOURCE_EXTERNAL,
|
||||
IWL_PNVM_SOURCE_EMBEDDED
|
||||
};
|
||||
|
||||
static enum iwl_pnvm_source iwl_select_pnvm_source(struct iwl_trans *trans,
|
||||
bool intel_sku)
|
||||
{
|
||||
|
||||
/* Get PNVM from BIOS for non-Intel SKU */
|
||||
if (!intel_sku)
|
||||
return IWL_PNVM_SOURCE_BIOS;
|
||||
|
||||
/* Before those devices, PNVM didn't exist at all */
|
||||
if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
|
||||
return IWL_PNVM_SOURCE_NONE;
|
||||
|
||||
/* After those devices, we moved to embedded PNVM */
|
||||
if (trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_AX210)
|
||||
return IWL_PNVM_SOURCE_EMBEDDED;
|
||||
|
||||
/* For IWL_DEVICE_FAMILY_AX210, depends on the CRF */
|
||||
if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_GF)
|
||||
return IWL_PNVM_SOURCE_EXTERNAL;
|
||||
|
||||
return IWL_PNVM_SOURCE_NONE;
|
||||
}
|
||||
|
||||
static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
|
||||
__le32 sku_id[3], const struct iwl_fw *fw)
|
||||
{
|
||||
struct pnvm_sku_package *package;
|
||||
enum iwl_pnvm_source pnvm_src =
|
||||
iwl_select_pnvm_source(trans_p, sku_id[2] == 0);
|
||||
u8 *image = NULL;
|
||||
|
||||
/* Get PNVM from BIOS for non-Intel SKU */
|
||||
if (sku_id[2]) {
|
||||
IWL_DEBUG_FW(trans_p, "PNVM source %d\n", pnvm_src);
|
||||
|
||||
if (pnvm_src == IWL_PNVM_SOURCE_NONE)
|
||||
return NULL;
|
||||
|
||||
if (pnvm_src == IWL_PNVM_SOURCE_BIOS) {
|
||||
package = iwl_uefi_get_pnvm(trans_p, len);
|
||||
if (!IS_ERR_OR_NULL(package)) {
|
||||
if (*len >= sizeof(*package)) {
|
||||
@@ -289,18 +334,26 @@ static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
|
||||
if (image)
|
||||
return image;
|
||||
}
|
||||
|
||||
/* PNVM doesn't exist in BIOS. Find the fallback source */
|
||||
pnvm_src = iwl_select_pnvm_source(trans_p, true);
|
||||
IWL_DEBUG_FW(trans_p, "PNVM in BIOS doesn't exist, try %d\n",
|
||||
pnvm_src);
|
||||
}
|
||||
|
||||
if (fw->pnvm_data) {
|
||||
*len = fw->pnvm_size;
|
||||
if (pnvm_src == IWL_PNVM_SOURCE_EXTERNAL) {
|
||||
image = iwl_pnvm_get_from_fs(trans_p, len);
|
||||
if (image)
|
||||
return image;
|
||||
}
|
||||
|
||||
if (pnvm_src == IWL_PNVM_SOURCE_EMBEDDED && fw->pnvm_data) {
|
||||
*len = fw->pnvm_size;
|
||||
return fw->pnvm_data;
|
||||
}
|
||||
|
||||
/* If it's not available, or for Intel SKU, try from the filesystem */
|
||||
if (iwl_pnvm_get_from_fs(trans_p, &image, len))
|
||||
return NULL;
|
||||
return image;
|
||||
IWL_ERR(trans_p, "Couldn't get PNVM from required source: %d\n", pnvm_src);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -59,11 +59,16 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
|
||||
},
|
||||
},
|
||||
{ .ident = "ASUS",
|
||||
{ .ident = "ASUSTEK",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
},
|
||||
},
|
||||
{ .ident = "ASUS",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
|
||||
},
|
||||
},
|
||||
{ .ident = "GOOGLE-HP",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
|
||||
@@ -141,11 +146,16 @@ static const struct dmi_system_id dmi_tas_approved_list[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
},
|
||||
},
|
||||
{ .ident = "ASUS",
|
||||
{ .ident = "ASUSTEK",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
},
|
||||
},
|
||||
{ .ident = "ASUS",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUS"),
|
||||
},
|
||||
},
|
||||
{ .ident = "GOOGLE-HP",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Google"),
|
||||
@@ -305,6 +315,7 @@ static bool iwl_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Utility function for iwlmvm and iwlxvt */
|
||||
int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
|
||||
union iwl_ppag_table_cmd *cmd, int *cmd_size)
|
||||
{
|
||||
@@ -344,18 +355,18 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
|
||||
num_sub_bands = IWL_NUM_SUB_BANDS_V1;
|
||||
gain = cmd->v1.gain[0];
|
||||
*cmd_size = sizeof(cmd->v1);
|
||||
cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
|
||||
cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK);
|
||||
if (fwrt->ppag_bios_rev >= 1) {
|
||||
/* in this case FW supports revision 0 */
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
"PPAG table rev is %d, send truncated table\n",
|
||||
fwrt->ppag_bios_rev);
|
||||
}
|
||||
} else if (cmd_ver >= 2 && cmd_ver <= 6) {
|
||||
} else if (cmd_ver == 5) {
|
||||
num_sub_bands = IWL_NUM_SUB_BANDS_V2;
|
||||
gain = cmd->v2.gain[0];
|
||||
*cmd_size = sizeof(cmd->v2);
|
||||
cmd->v2.flags = cpu_to_le32(fwrt->ppag_flags);
|
||||
gain = cmd->v5.gain[0];
|
||||
*cmd_size = sizeof(cmd->v5);
|
||||
cmd->v5.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK);
|
||||
if (fwrt->ppag_bios_rev == 0) {
|
||||
/* in this case FW supports revisions 1,2 or 3 */
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
@@ -363,11 +374,11 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
|
||||
}
|
||||
} else if (cmd_ver == 7) {
|
||||
num_sub_bands = IWL_NUM_SUB_BANDS_V2;
|
||||
gain = cmd->v3.gain[0];
|
||||
*cmd_size = sizeof(cmd->v3);
|
||||
cmd->v3.ppag_config_info.table_source = fwrt->ppag_bios_source;
|
||||
cmd->v3.ppag_config_info.table_revision = fwrt->ppag_bios_rev;
|
||||
cmd->v3.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
|
||||
gain = cmd->v7.gain[0];
|
||||
*cmd_size = sizeof(cmd->v7);
|
||||
cmd->v7.ppag_config_info.table_source = fwrt->ppag_bios_source;
|
||||
cmd->v7.ppag_config_info.table_revision = fwrt->ppag_bios_rev;
|
||||
cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
|
||||
} else {
|
||||
IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
|
||||
return -EINVAL;
|
||||
@@ -378,30 +389,22 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
|
||||
"PPAG MODE bits were read from bios: %d\n",
|
||||
fwrt->ppag_flags);
|
||||
|
||||
if (cmd_ver == 6)
|
||||
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V6_MASK);
|
||||
else if (cmd_ver == 5)
|
||||
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V5_MASK);
|
||||
else if (cmd_ver < 5)
|
||||
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V4_MASK);
|
||||
|
||||
if ((cmd_ver == 1 &&
|
||||
!fw_has_capa(&fwrt->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) ||
|
||||
(cmd_ver == 2 && fwrt->ppag_bios_rev >= 2)) {
|
||||
if (cmd_ver == 1 &&
|
||||
!fw_has_capa(&fwrt->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
|
||||
cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
|
||||
IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
|
||||
} else {
|
||||
IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
|
||||
}
|
||||
|
||||
/* The 'flags' field is the same in v1 and v2 so we can just
|
||||
/* The 'flags' field is the same in v1 and v5 so we can just
|
||||
* use v1 to access it.
|
||||
*/
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
"PPAG MODE bits going to be sent: %d\n",
|
||||
(cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) :
|
||||
le32_to_cpu(cmd->v3.ppag_config_info.value));
|
||||
le32_to_cpu(cmd->v7.ppag_config_info.value));
|
||||
|
||||
for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
|
||||
for (j = 0; j < num_sub_bands; j++) {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "fw/api/phy.h"
|
||||
#include "fw/api/config.h"
|
||||
#include "fw/api/nvm-reg.h"
|
||||
#include "fw/img.h"
|
||||
#include "iwl-trans.h"
|
||||
|
||||
#define BIOS_SAR_MAX_PROFILE_NUM 4
|
||||
|
||||
@@ -150,8 +150,6 @@ struct iwl_fw_runtime {
|
||||
unsigned long non_collect_ts_start[IWL_FW_INI_TIME_POINT_NUM];
|
||||
u32 *d3_debug_data;
|
||||
u32 lmac_err_id[MAX_NUM_LMAC];
|
||||
u32 tcm_err_id[MAX_NUM_TCM];
|
||||
u32 rcm_err_id[MAX_NUM_RCM];
|
||||
u32 umac_err_id;
|
||||
|
||||
struct iwl_txf_iter_data txf_iter_data;
|
||||
|
||||
@@ -727,6 +727,8 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
|
||||
struct uefi_cnv_var_general_cfg *data;
|
||||
int ret = -EINVAL;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(data->functions) < DSM_FUNC_NUM_FUNCS);
|
||||
|
||||
/* Not supported function index */
|
||||
if (func >= DSM_FUNC_NUM_FUNCS || func == 5)
|
||||
return -EOPNOTSUPP;
|
||||
@@ -742,11 +744,6 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) {
|
||||
IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(data->functions[DSM_FUNC_QUERY] & BIT(func))) {
|
||||
IWL_DEBUG_RADIO(fwrt, "DSM func %d not in 0x%x\n",
|
||||
func, data->functions[DSM_FUNC_QUERY]);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include "iwl-csr.h"
|
||||
#include "iwl-drv.h"
|
||||
@@ -107,6 +108,9 @@ enum iwl_nvm_type {
|
||||
MODULE_FIRMWARE(pfx "-" __stringify(api) ".ucode"); \
|
||||
MODULE_FIRMWARE(pfx ".pnvm")
|
||||
|
||||
#define IWL_CORE_FW(pfx, core) \
|
||||
MODULE_FIRMWARE(pfx "-c" __stringify(core) ".ucode")
|
||||
|
||||
static inline u8 num_of_ant(u8 mask)
|
||||
{
|
||||
return !!((mask) & ANT_A) +
|
||||
@@ -192,8 +196,8 @@ struct iwl_family_base_params {
|
||||
|
||||
u8 max_ll_items;
|
||||
u8 led_compensation;
|
||||
u8 ucode_api_max;
|
||||
u8 ucode_api_min;
|
||||
u16 ucode_api_max;
|
||||
u16 ucode_api_min;
|
||||
u32 mac_addr_from_csr:10;
|
||||
u8 nvm_hw_section_num;
|
||||
netdev_features_t features;
|
||||
@@ -210,6 +214,34 @@ struct iwl_family_base_params {
|
||||
const struct iwl_fw_mon_regs mon_dbgi_regs;
|
||||
};
|
||||
|
||||
/*
|
||||
* FW is released as "core N release", and we used to have a
|
||||
* gap of 3 between the API version and core number. Now the
|
||||
* reported API version will be 1000 + core and we encode it
|
||||
* in the filename as "c<core>".
|
||||
*/
|
||||
#define API_IS_CORE_START 1000
|
||||
#define API_TO_CORE_OFFS 3
|
||||
#define ENCODE_CORE_AS_API(core) (API_IS_CORE_START + (core))
|
||||
|
||||
static inline bool iwl_api_is_core_number(int api)
|
||||
{
|
||||
return api >= API_IS_CORE_START;
|
||||
}
|
||||
|
||||
static inline int iwl_api_to_core(int api)
|
||||
{
|
||||
if (iwl_api_is_core_number(api))
|
||||
return api - API_IS_CORE_START;
|
||||
|
||||
return api - API_TO_CORE_OFFS;
|
||||
}
|
||||
|
||||
#define FW_API_FMT "%s%d"
|
||||
#define FW_API_ARG(n) \
|
||||
iwl_api_is_core_number(n) ? "c" : "", \
|
||||
iwl_api_is_core_number(n) ? (n) - API_IS_CORE_START : (n)
|
||||
|
||||
/*
|
||||
* @stbc: support Tx STBC and 1*SS Rx STBC
|
||||
* @ldpc: support Tx/Rx with LDPC
|
||||
@@ -422,8 +454,8 @@ struct iwl_rf_cfg {
|
||||
u8 valid_tx_ant;
|
||||
u8 valid_rx_ant;
|
||||
u8 non_shared_ant;
|
||||
u8 ucode_api_max;
|
||||
u8 ucode_api_min;
|
||||
u16 ucode_api_max;
|
||||
u16 ucode_api_min;
|
||||
u16 num_rbds;
|
||||
};
|
||||
|
||||
|
||||
@@ -337,10 +337,18 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (CSR_HW_RFID_TYPE(drv->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_WH &&
|
||||
CSR_HW_RFID_STEP(drv->trans->info.hw_rf_id) == SILICON_A_STEP) {
|
||||
IWL_ERR(drv, "WH A step is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw_name_pre = iwl_drv_get_fwname_pre(drv->trans, _fw_name_pre);
|
||||
|
||||
if (first)
|
||||
drv->fw_index = ucode_api_max;
|
||||
else if (drv->fw_index == ENCODE_CORE_AS_API(99))
|
||||
drv->fw_index = 101; /* last API-scheme number below core 99 */
|
||||
else
|
||||
drv->fw_index--;
|
||||
|
||||
@@ -348,13 +356,15 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
|
||||
IWL_ERR(drv, "no suitable firmware found!\n");
|
||||
|
||||
if (ucode_api_min == ucode_api_max) {
|
||||
IWL_ERR(drv, "%s-%d is required\n", fw_name_pre,
|
||||
ucode_api_max);
|
||||
IWL_ERR(drv, "%s-" FW_API_FMT " is required\n",
|
||||
fw_name_pre, FW_API_ARG(ucode_api_max));
|
||||
} else {
|
||||
IWL_ERR(drv, "minimum version required: %s-%d\n",
|
||||
fw_name_pre, ucode_api_min);
|
||||
IWL_ERR(drv, "maximum version supported: %s-%d\n",
|
||||
fw_name_pre, ucode_api_max);
|
||||
IWL_ERR(drv,
|
||||
"minimum version required: %s-" FW_API_FMT "\n",
|
||||
fw_name_pre, FW_API_ARG(ucode_api_min));
|
||||
IWL_ERR(drv,
|
||||
"maximum version supported: %s-" FW_API_FMT "\n",
|
||||
fw_name_pre, FW_API_ARG(ucode_api_max));
|
||||
}
|
||||
|
||||
IWL_ERR(drv,
|
||||
@@ -362,8 +372,9 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s-%d.ucode",
|
||||
fw_name_pre, drv->fw_index);
|
||||
snprintf(drv->firmware_name, sizeof(drv->firmware_name),
|
||||
"%s-" FW_API_FMT ".ucode",
|
||||
fw_name_pre, FW_API_ARG(drv->fw_index));
|
||||
|
||||
IWL_DEBUG_FW_INFO(drv, "attempting to load firmware '%s'\n",
|
||||
drv->firmware_name);
|
||||
@@ -1588,6 +1599,7 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv)
|
||||
*/
|
||||
static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
||||
{
|
||||
unsigned int min_core, max_core, loaded_core;
|
||||
struct iwl_drv *drv = context;
|
||||
struct iwl_fw *fw = &drv->fw;
|
||||
const struct iwl_ucode_header *ucode;
|
||||
@@ -1650,11 +1662,24 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
||||
* firmware filename ... but we don't check for that and only rely
|
||||
* on the API version read from firmware header from here on forward
|
||||
*/
|
||||
if (api_ver < api_min || api_ver > api_max) {
|
||||
|
||||
/*
|
||||
* if -cN.ucode file was loaded, core version == file version,
|
||||
* otherwise core version == file version (API version) - 3
|
||||
*/
|
||||
if (iwl_api_is_core_number(drv->fw_index))
|
||||
loaded_core = api_ver;
|
||||
else
|
||||
loaded_core = api_ver - API_TO_CORE_OFFS;
|
||||
|
||||
min_core = iwl_api_to_core(api_min);
|
||||
max_core = iwl_api_to_core(api_max);
|
||||
|
||||
if (loaded_core < min_core || loaded_core > max_core) {
|
||||
IWL_ERR(drv,
|
||||
"Driver unable to support your firmware API. "
|
||||
"Driver supports v%u, firmware is v%u.\n",
|
||||
api_max, api_ver);
|
||||
"Driver supports FW core %u..%u, firmware is %u.\n",
|
||||
min_core, max_core, loaded_core);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2015-2016 Intel Deutschland GmbH
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
@@ -13,6 +12,7 @@
|
||||
#include "iwl-debug.h"
|
||||
#include "iwl-prph.h"
|
||||
#include "iwl-fh.h"
|
||||
#include "pcie/gen1_2/internal.h"
|
||||
|
||||
void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
|
||||
{
|
||||
@@ -160,7 +160,7 @@ int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
|
||||
|
||||
do {
|
||||
if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
|
||||
return t;
|
||||
return 0;
|
||||
udelay(IWL_POLL_INTERVAL);
|
||||
t += IWL_POLL_INTERVAL;
|
||||
} while (t < timeout);
|
||||
@@ -396,96 +396,11 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IWL_HOST_MON_BLOCK_PEMON 0x00
|
||||
#define IWL_HOST_MON_BLOCK_HIPM 0x22
|
||||
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_VEC0 0x00
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_VEC1 0x01
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_WFPM 0x06
|
||||
|
||||
static void iwl_dump_host_monitor_block(struct iwl_trans *trans,
|
||||
u32 block, u32 vec, u32 iter)
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
int i;
|
||||
|
||||
IWL_ERR(trans, "Host monitor block 0x%x vector 0x%x\n", block, vec);
|
||||
iwl_write32(trans, CSR_MONITOR_CFG_REG, (block << 8) | vec);
|
||||
for (i = 0; i < iter; i++)
|
||||
IWL_ERR(trans, " value [iter %d]: 0x%08x\n",
|
||||
i, iwl_read32(trans, CSR_MONITOR_STATUS_REG));
|
||||
return iwl_pcie_gen1_2_activate_nic(trans);
|
||||
}
|
||||
|
||||
static void iwl_dump_host_monitor(struct iwl_trans *trans)
|
||||
{
|
||||
switch (trans->mac_cfg->device_family) {
|
||||
case IWL_DEVICE_FAMILY_22000:
|
||||
case IWL_DEVICE_FAMILY_AX210:
|
||||
IWL_ERR(trans, "CSR_RESET = 0x%x\n",
|
||||
iwl_read32(trans, CSR_RESET));
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC0, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC1, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_WFPM, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_HIPM,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC0, 1);
|
||||
break;
|
||||
default:
|
||||
/* not supported yet */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int iwl_finish_nic_init(struct iwl_trans *trans)
|
||||
{
|
||||
const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg;
|
||||
u32 poll_ready;
|
||||
int err;
|
||||
|
||||
if (mac_cfg->bisr_workaround) {
|
||||
/* ensure the TOP FSM isn't still in previous reset */
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set "initialization complete" bit to move adapter from
|
||||
* D0U* --> D0A* (powered-up active) state.
|
||||
*/
|
||||
if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ |
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
|
||||
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
|
||||
} else {
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
||||
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY;
|
||||
}
|
||||
|
||||
if (mac_cfg->device_family == IWL_DEVICE_FAMILY_8000)
|
||||
udelay(2);
|
||||
|
||||
/*
|
||||
* Wait for clock stabilization; once stabilized, access to
|
||||
* device-internal resources is supported, e.g. iwl_write_prph()
|
||||
* and accesses to uCode SRAM.
|
||||
*/
|
||||
err = iwl_poll_bits(trans, CSR_GP_CNTRL, poll_ready, 25000);
|
||||
if (err < 0) {
|
||||
IWL_DEBUG_INFO(trans, "Failed to wake NIC\n");
|
||||
|
||||
iwl_dump_host_monitor(trans);
|
||||
}
|
||||
|
||||
if (mac_cfg->bisr_workaround) {
|
||||
/* ensure BISR shift has finished */
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_finish_nic_init);
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
|
||||
|
||||
void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
|
||||
u32 sw_err_bit)
|
||||
|
||||
@@ -57,7 +57,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
|
||||
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
|
||||
void iwl_force_nmi(struct iwl_trans *trans);
|
||||
|
||||
int iwl_finish_nic_init(struct iwl_trans *trans);
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
/* Error handling */
|
||||
int iwl_dump_fh(struct iwl_trans *trans, char **buf);
|
||||
|
||||
@@ -139,50 +139,6 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
|
||||
#define RATES_52_OFFS 4
|
||||
#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS)
|
||||
|
||||
/**
|
||||
* enum iwl_nvm_channel_flags - channel flags in NVM
|
||||
* @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
|
||||
* @NVM_CHANNEL_IBSS: usable as an IBSS channel and deprecated
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY: active scanning allowed and
|
||||
* AP allowed only in 20 MHz. Valid only
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_ACTIVE: active scanning allowed and allows IBSS
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_RADAR: radar detection required
|
||||
* @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
|
||||
* @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
|
||||
* on same channel on 2.4 or same UNII band on 5.2
|
||||
* @NVM_CHANNEL_UNIFORM: uniform spreading required
|
||||
* @NVM_CHANNEL_20MHZ: 20 MHz channel okay
|
||||
* @NVM_CHANNEL_40MHZ: 40 MHz channel okay
|
||||
* @NVM_CHANNEL_80MHZ: 80 MHz channel okay
|
||||
* @NVM_CHANNEL_160MHZ: 160 MHz channel okay
|
||||
* @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?)
|
||||
* @NVM_CHANNEL_VLP: client support connection to UHB VLP AP
|
||||
* @NVM_CHANNEL_AFC: client support connection to UHB AFC AP
|
||||
* @NVM_CHANNEL_VLP_AP_NOT_ALLOWED: UHB VLP AP not allowed,
|
||||
* Valid only when %NVM_CHANNEL_VLP is enabled.
|
||||
*/
|
||||
enum iwl_nvm_channel_flags {
|
||||
NVM_CHANNEL_VALID = BIT(0),
|
||||
NVM_CHANNEL_IBSS = BIT(1),
|
||||
NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY = BIT(2),
|
||||
NVM_CHANNEL_ACTIVE = BIT(3),
|
||||
NVM_CHANNEL_RADAR = BIT(4),
|
||||
NVM_CHANNEL_INDOOR_ONLY = BIT(5),
|
||||
NVM_CHANNEL_GO_CONCURRENT = BIT(6),
|
||||
NVM_CHANNEL_UNIFORM = BIT(7),
|
||||
NVM_CHANNEL_20MHZ = BIT(8),
|
||||
NVM_CHANNEL_40MHZ = BIT(9),
|
||||
NVM_CHANNEL_80MHZ = BIT(10),
|
||||
NVM_CHANNEL_160MHZ = BIT(11),
|
||||
NVM_CHANNEL_DC_HIGH = BIT(12),
|
||||
NVM_CHANNEL_VLP = BIT(13),
|
||||
NVM_CHANNEL_AFC = BIT(14),
|
||||
NVM_CHANNEL_VLP_AP_NOT_ALLOWED = BIT(15),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_reg_capa_flags_v1 - global flags applied for the whole regulatory
|
||||
* domain.
|
||||
@@ -282,30 +238,6 @@ enum iwl_reg_capa_flags_v4 {
|
||||
*/
|
||||
#define REG_CAPA_V4_RESP_VER 8
|
||||
|
||||
/**
|
||||
* struct iwl_reg_capa - struct for global regulatory capabilities, Used for
|
||||
* handling the different APIs of reg_capa_flags.
|
||||
*
|
||||
* @allow_40mhz: 11n channel with a width of 40Mhz is allowed
|
||||
* for this regulatory domain.
|
||||
* @allow_80mhz: 11ac channel with a width of 80Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5 and 6 Ghz).
|
||||
* @allow_160mhz: 11ac channel with a width of 160Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5 and 6 Ghz).
|
||||
* @allow_320mhz: 11be channel with a width of 320Mhz is allowed
|
||||
* for this regulatory domain (valid only in 6 Ghz).
|
||||
* @disable_11ax: 11ax is forbidden for this regulatory domain.
|
||||
* @disable_11be: 11be is forbidden for this regulatory domain.
|
||||
*/
|
||||
struct iwl_reg_capa {
|
||||
bool allow_40mhz;
|
||||
bool allow_80mhz;
|
||||
bool allow_160mhz;
|
||||
bool allow_320mhz;
|
||||
bool disable_11ax;
|
||||
bool disable_11be;
|
||||
};
|
||||
|
||||
static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level,
|
||||
int chan, u32 flags)
|
||||
{
|
||||
@@ -1042,10 +974,6 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
|
||||
iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |=
|
||||
IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
|
||||
|
||||
if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT))
|
||||
iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |=
|
||||
IEEE80211_HE_MAC_CAP2_BCAST_TWT;
|
||||
|
||||
if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000 &&
|
||||
!is_ap) {
|
||||
iftype_data->vendor_elems.data = iwl_vendor_caps;
|
||||
@@ -1600,9 +1528,10 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
|
||||
|
||||
static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
||||
int ch_idx, u16 nvm_flags,
|
||||
struct iwl_reg_capa reg_capa)
|
||||
VISIBLE_IF_IWLWIFI_KUNIT
|
||||
u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
||||
int ch_idx, u16 nvm_flags,
|
||||
struct iwl_reg_capa reg_capa)
|
||||
{
|
||||
u32 flags = NL80211_RRF_NO_HT40;
|
||||
|
||||
@@ -1692,6 +1621,7 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
||||
|
||||
return flags;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_nvm_get_regdom_bw_flags);
|
||||
|
||||
static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,80 @@ enum iwl_nvm_sbands_flags {
|
||||
IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_reg_capa - struct for global regulatory capabilities, Used for
|
||||
* handling the different APIs of reg_capa_flags.
|
||||
*
|
||||
* @allow_40mhz: 11n channel with a width of 40Mhz is allowed
|
||||
* for this regulatory domain.
|
||||
* @allow_80mhz: 11ac channel with a width of 80Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5 and 6 Ghz).
|
||||
* @allow_160mhz: 11ac channel with a width of 160Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5 and 6 Ghz).
|
||||
* @allow_320mhz: 11be channel with a width of 320Mhz is allowed
|
||||
* for this regulatory domain (valid only in 6 Ghz).
|
||||
* @disable_11ax: 11ax is forbidden for this regulatory domain.
|
||||
* @disable_11be: 11be is forbidden for this regulatory domain.
|
||||
*/
|
||||
struct iwl_reg_capa {
|
||||
bool allow_40mhz;
|
||||
bool allow_80mhz;
|
||||
bool allow_160mhz;
|
||||
bool allow_320mhz;
|
||||
bool disable_11ax;
|
||||
bool disable_11be;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_nvm_channel_flags - channel flags in NVM
|
||||
* @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
|
||||
* @NVM_CHANNEL_IBSS: usable as an IBSS channel and deprecated
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY: active scanning allowed and
|
||||
* AP allowed only in 20 MHz. Valid only
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_ACTIVE: active scanning allowed and allows IBSS
|
||||
* when %IWL_NVM_SBANDS_FLAGS_LAR enabled.
|
||||
* @NVM_CHANNEL_RADAR: radar detection required
|
||||
* @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
|
||||
* @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
|
||||
* on same channel on 2.4 or same UNII band on 5.2
|
||||
* @NVM_CHANNEL_UNIFORM: uniform spreading required
|
||||
* @NVM_CHANNEL_20MHZ: 20 MHz channel okay
|
||||
* @NVM_CHANNEL_40MHZ: 40 MHz channel okay
|
||||
* @NVM_CHANNEL_80MHZ: 80 MHz channel okay
|
||||
* @NVM_CHANNEL_160MHZ: 160 MHz channel okay
|
||||
* @NVM_CHANNEL_DC_HIGH: DC HIGH required/allowed (?)
|
||||
* @NVM_CHANNEL_VLP: client support connection to UHB VLP AP
|
||||
* @NVM_CHANNEL_AFC: client support connection to UHB AFC AP
|
||||
* @NVM_CHANNEL_VLP_AP_NOT_ALLOWED: UHB VLP AP not allowed,
|
||||
* Valid only when %NVM_CHANNEL_VLP is enabled.
|
||||
*/
|
||||
enum iwl_nvm_channel_flags {
|
||||
NVM_CHANNEL_VALID = BIT(0),
|
||||
NVM_CHANNEL_IBSS = BIT(1),
|
||||
NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY = BIT(2),
|
||||
NVM_CHANNEL_ACTIVE = BIT(3),
|
||||
NVM_CHANNEL_RADAR = BIT(4),
|
||||
NVM_CHANNEL_INDOOR_ONLY = BIT(5),
|
||||
NVM_CHANNEL_GO_CONCURRENT = BIT(6),
|
||||
NVM_CHANNEL_UNIFORM = BIT(7),
|
||||
NVM_CHANNEL_20MHZ = BIT(8),
|
||||
NVM_CHANNEL_40MHZ = BIT(9),
|
||||
NVM_CHANNEL_80MHZ = BIT(10),
|
||||
NVM_CHANNEL_160MHZ = BIT(11),
|
||||
NVM_CHANNEL_DC_HIGH = BIT(12),
|
||||
NVM_CHANNEL_VLP = BIT(13),
|
||||
NVM_CHANNEL_AFC = BIT(14),
|
||||
NVM_CHANNEL_VLP_AP_NOT_ALLOWED = BIT(15),
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
|
||||
u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
||||
int ch_idx, u16 nvm_flags,
|
||||
struct iwl_reg_capa reg_capa);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* iwl_parse_nvm_data - parse NVM data and return values
|
||||
*
|
||||
|
||||
@@ -268,9 +268,7 @@ static void iwl_trans_restart_wk(struct work_struct *wk)
|
||||
|
||||
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
||||
struct device *dev,
|
||||
const struct iwl_mac_cfg *mac_cfg,
|
||||
unsigned int txcmd_size,
|
||||
unsigned int txcmd_align)
|
||||
const struct iwl_mac_cfg *mac_cfg)
|
||||
{
|
||||
struct iwl_trans *trans;
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
@@ -292,22 +290,12 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
||||
|
||||
INIT_DELAYED_WORK(&trans->restart.wk, iwl_trans_restart_wk);
|
||||
|
||||
snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
|
||||
"iwl_cmd_pool:%s", dev_name(trans->dev));
|
||||
trans->dev_cmd_pool =
|
||||
kmem_cache_create(trans->dev_cmd_pool_name,
|
||||
txcmd_size, txcmd_align,
|
||||
SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!trans->dev_cmd_pool)
|
||||
return NULL;
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
void iwl_trans_free(struct iwl_trans *trans)
|
||||
{
|
||||
cancel_delayed_work_sync(&trans->restart.wk);
|
||||
kmem_cache_destroy(trans->dev_cmd_pool);
|
||||
}
|
||||
|
||||
int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
|
||||
@@ -318,9 +306,6 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
|
||||
test_bit(STATUS_RFKILL_OPMODE, &trans->status)))
|
||||
return -ERFKILL;
|
||||
|
||||
if (unlikely(test_bit(STATUS_SUSPENDED, &trans->status)))
|
||||
return -EHOSTDOWN;
|
||||
|
||||
if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
|
||||
return -EIO;
|
||||
|
||||
@@ -348,6 +333,19 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_send_cmd);
|
||||
|
||||
struct iwl_device_tx_cmd *iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_alloc_tx_cmd(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_alloc_tx_cmd);
|
||||
|
||||
void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
|
||||
struct iwl_device_tx_cmd *dev_cmd)
|
||||
{
|
||||
iwl_pcie_gen1_2_free_tx_cmd(trans, dev_cmd);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_free_tx_cmd);
|
||||
|
||||
/* Comparator for struct iwl_hcmd_names.
|
||||
* Used in the binary search over a list of host commands.
|
||||
*
|
||||
@@ -399,7 +397,7 @@ void iwl_trans_op_mode_enter(struct iwl_trans *trans,
|
||||
|
||||
WARN_ON_ONCE(!trans->conf.rx_mpdu_cmd);
|
||||
|
||||
iwl_trans_pcie_op_mode_enter(trans);
|
||||
iwl_pcie_gen1_2_op_mode_enter(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_op_mode_enter);
|
||||
|
||||
@@ -408,8 +406,6 @@ int iwl_trans_start_hw(struct iwl_trans *trans)
|
||||
might_sleep();
|
||||
|
||||
clear_bit(STATUS_TRANS_RESET_IN_PROGRESS, &trans->status);
|
||||
/* opmode may not resume if it detects errors */
|
||||
clear_bit(STATUS_SUSPENDED, &trans->status);
|
||||
|
||||
return iwl_trans_pcie_start_hw(trans);
|
||||
}
|
||||
@@ -507,33 +503,19 @@ iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask,
|
||||
sanitize_ops, sanitize_ctx);
|
||||
}
|
||||
|
||||
int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, bool reset)
|
||||
int iwl_trans_d3_suspend(struct iwl_trans *trans, bool reset)
|
||||
{
|
||||
int err;
|
||||
|
||||
might_sleep();
|
||||
|
||||
err = iwl_trans_pcie_d3_suspend(trans, test, reset);
|
||||
|
||||
if (!err)
|
||||
set_bit(STATUS_SUSPENDED, &trans->status);
|
||||
|
||||
return err;
|
||||
return iwl_trans_pcie_d3_suspend(trans, reset);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_d3_suspend);
|
||||
|
||||
int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status,
|
||||
bool test, bool reset)
|
||||
int iwl_trans_d3_resume(struct iwl_trans *trans, bool reset)
|
||||
{
|
||||
int err;
|
||||
|
||||
might_sleep();
|
||||
|
||||
err = iwl_trans_pcie_d3_resume(trans, status, test, reset);
|
||||
|
||||
clear_bit(STATUS_SUSPENDED, &trans->status);
|
||||
|
||||
return err;
|
||||
return iwl_trans_pcie_d3_resume(trans, reset);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_d3_resume);
|
||||
|
||||
@@ -825,3 +807,18 @@ void iwl_trans_set_reduce_power(struct iwl_trans *trans,
|
||||
{
|
||||
iwl_trans_pcie_ctx_info_v2_set_reduce_power(trans, capa);
|
||||
}
|
||||
|
||||
bool iwl_trans_is_pm_supported(struct iwl_trans *trans)
|
||||
{
|
||||
if (WARN_ON(trans->mac_cfg->gen2))
|
||||
return false;
|
||||
|
||||
return iwl_pcie_gen1_is_pm_supported(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_is_pm_supported);
|
||||
|
||||
bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_is_ltr_enabled(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_is_ltr_enabled);
|
||||
|
||||
@@ -274,16 +274,6 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
|
||||
#define IWL_MAX_RX_HW_QUEUES 16
|
||||
#define IWL_9000_MAX_RX_HW_QUEUES 1
|
||||
|
||||
/**
|
||||
* enum iwl_d3_status - WoWLAN image/device status
|
||||
* @IWL_D3_STATUS_ALIVE: firmware is still running after resume
|
||||
* @IWL_D3_STATUS_RESET: device was reset while suspended
|
||||
*/
|
||||
enum iwl_d3_status {
|
||||
IWL_D3_STATUS_ALIVE,
|
||||
IWL_D3_STATUS_RESET,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_trans_status: transport status flags
|
||||
* @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
|
||||
@@ -294,16 +284,12 @@ enum iwl_d3_status {
|
||||
* @STATUS_RFKILL_OPMODE: RF-kill state reported to opmode
|
||||
* @STATUS_FW_ERROR: the fw is in error state
|
||||
* @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
|
||||
* @STATUS_SUPPRESS_CMD_ERROR_ONCE: suppress "FW error in SYNC CMD" once,
|
||||
* e.g. for testing
|
||||
* @STATUS_IN_SW_RESET: device is undergoing reset, cleared by opmode
|
||||
* via iwl_trans_finish_sw_reset()
|
||||
* @STATUS_RESET_PENDING: reset worker was scheduled, but didn't dump
|
||||
* the firmware state yet
|
||||
* @STATUS_TRANS_RESET_IN_PROGRESS: reset is still in progress, don't
|
||||
* attempt another reset yet
|
||||
* @STATUS_SUSPENDED: device is suspended, don't send commands that
|
||||
* aren't marked accordingly
|
||||
*/
|
||||
enum iwl_trans_status {
|
||||
STATUS_SYNC_HCMD_ACTIVE,
|
||||
@@ -314,11 +300,9 @@ enum iwl_trans_status {
|
||||
STATUS_RFKILL_OPMODE,
|
||||
STATUS_FW_ERROR,
|
||||
STATUS_TRANS_DEAD,
|
||||
STATUS_SUPPRESS_CMD_ERROR_ONCE,
|
||||
STATUS_IN_SW_RESET,
|
||||
STATUS_RESET_PENDING,
|
||||
STATUS_TRANS_RESET_IN_PROGRESS,
|
||||
STATUS_SUSPENDED,
|
||||
};
|
||||
|
||||
static inline int
|
||||
@@ -658,8 +642,6 @@ struct iwl_pc_data {
|
||||
* @restart_required: indicates debug restart is required
|
||||
* @last_tp_resetfw: last handling of reset during debug timepoint
|
||||
* @imr_data: IMR debug data allocation
|
||||
* @dump_file_name_ext: dump file name extension
|
||||
* @dump_file_name_ext_valid: dump file name extension if valid or not
|
||||
* @num_pc: number of program counter for cpu
|
||||
* @pc_data: details of the program counter
|
||||
* @yoyo_bin_loaded: tells if a yoyo debug file has been loaded
|
||||
@@ -698,8 +680,6 @@ struct iwl_trans_debug {
|
||||
bool restart_required;
|
||||
u32 last_tp_resetfw;
|
||||
struct iwl_imr_data imr_data;
|
||||
u8 dump_file_name_ext[IWL_FW_INI_MAX_NAME];
|
||||
bool dump_file_name_ext_valid;
|
||||
u32 num_pc;
|
||||
struct iwl_pc_data *pc_data;
|
||||
bool yoyo_bin_loaded;
|
||||
@@ -830,7 +810,6 @@ struct iwl_txq {
|
||||
* @hw_rf_id: the device RF ID
|
||||
* @hw_cnv_id: the device CNV ID
|
||||
* @hw_crf_id: the device CRF ID
|
||||
* @hw_wfpm_id: the device wfpm ID
|
||||
* @hw_id: the ID of the device / sub-device
|
||||
* Bits 0:15 represent the sub-device ID
|
||||
* Bits 16:31 represent the device ID.
|
||||
@@ -846,7 +825,6 @@ struct iwl_trans_info {
|
||||
u32 hw_rf_id;
|
||||
u32 hw_crf_id;
|
||||
u32 hw_cnv_id;
|
||||
u32 hw_wfpm_id;
|
||||
u32 hw_id;
|
||||
u8 pcie_link_speed;
|
||||
u8 num_rxqs;
|
||||
@@ -866,14 +844,11 @@ struct iwl_trans_info {
|
||||
* @dev: pointer to struct device * that represents the device
|
||||
* @info: device information for use by other layers
|
||||
* @pnvm_loaded: indicates PNVM was loaded
|
||||
* @pm_support: set to true in start_hw if link pm is supported
|
||||
* @ltr_enabled: set to true if the LTR is enabled
|
||||
* @suppress_cmd_error_once: suppress "FW error in SYNC CMD" once,
|
||||
* e.g. for testing
|
||||
* @fail_to_parse_pnvm_image: set to true if pnvm parsing failed
|
||||
* @reduce_power_loaded: indicates reduced power section was loaded
|
||||
* @failed_to_load_reduce_power_image: set to true if pnvm loading failed
|
||||
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
|
||||
* The user should use iwl_trans_{alloc,free}_tx_cmd.
|
||||
* @dev_cmd_pool_name: name for the TX command allocation pool
|
||||
* @dbgfs_dir: iwlwifi debugfs base dir for this device
|
||||
* @sync_cmd_lockdep_map: lockdep map for checking sync commands
|
||||
* @dbg: additional debug data, see &struct iwl_trans_debug
|
||||
@@ -905,18 +880,13 @@ struct iwl_trans {
|
||||
const struct iwl_trans_info info;
|
||||
bool reduced_cap_sku;
|
||||
bool step_urm;
|
||||
bool suppress_cmd_error_once;
|
||||
|
||||
bool pm_support;
|
||||
bool ltr_enabled;
|
||||
u8 pnvm_loaded:1;
|
||||
u8 fail_to_parse_pnvm_image:1;
|
||||
u8 reduce_power_loaded:1;
|
||||
u8 failed_to_load_reduce_power_image:1;
|
||||
|
||||
/* The following fields are internal only */
|
||||
struct kmem_cache *dev_cmd_pool;
|
||||
char dev_cmd_pool_name[50];
|
||||
|
||||
struct dentry *dbgfs_dir;
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
@@ -956,29 +926,21 @@ int iwl_trans_start_fw(struct iwl_trans *trans, const struct iwl_fw *fw,
|
||||
|
||||
void iwl_trans_stop_device(struct iwl_trans *trans);
|
||||
|
||||
int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, bool reset);
|
||||
int iwl_trans_d3_suspend(struct iwl_trans *trans, bool reset);
|
||||
|
||||
int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status,
|
||||
bool test, bool reset);
|
||||
int iwl_trans_d3_resume(struct iwl_trans *trans, bool reset);
|
||||
|
||||
struct iwl_trans_dump_data *
|
||||
iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask,
|
||||
const struct iwl_dump_sanitize_ops *sanitize_ops,
|
||||
void *sanitize_ctx);
|
||||
|
||||
static inline struct iwl_device_tx_cmd *
|
||||
iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
|
||||
{
|
||||
return kmem_cache_zalloc(trans->dev_cmd_pool, GFP_ATOMIC);
|
||||
}
|
||||
struct iwl_device_tx_cmd *iwl_trans_alloc_tx_cmd(struct iwl_trans *trans);
|
||||
|
||||
int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
|
||||
|
||||
static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
|
||||
struct iwl_device_tx_cmd *dev_cmd)
|
||||
{
|
||||
kmem_cache_free(trans->dev_cmd_pool, dev_cmd);
|
||||
}
|
||||
void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
|
||||
struct iwl_device_tx_cmd *dev_cmd);
|
||||
|
||||
int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
||||
struct iwl_device_tx_cmd *dev_cmd, int queue);
|
||||
@@ -1205,9 +1167,7 @@ static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans)
|
||||
*****************************************************/
|
||||
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
|
||||
struct device *dev,
|
||||
const struct iwl_mac_cfg *mac_cfg,
|
||||
unsigned int txcmd_size,
|
||||
unsigned int txcmd_align);
|
||||
const struct iwl_mac_cfg *mac_cfg);
|
||||
void iwl_trans_free(struct iwl_trans *trans);
|
||||
|
||||
static inline bool iwl_trans_is_hw_error_value(u32 val)
|
||||
@@ -1230,11 +1190,6 @@ static inline u16 iwl_trans_get_num_rbds(struct iwl_trans *trans)
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void iwl_trans_suppress_cmd_error_once(struct iwl_trans *trans)
|
||||
{
|
||||
set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &trans->status);
|
||||
}
|
||||
|
||||
static inline bool iwl_trans_device_enabled(struct iwl_trans *trans)
|
||||
{
|
||||
return test_bit(STATUS_DEVICE_ENABLED, &trans->status);
|
||||
@@ -1245,6 +1200,20 @@ static inline bool iwl_trans_is_dead(struct iwl_trans *trans)
|
||||
return test_bit(STATUS_TRANS_DEAD, &trans->status);
|
||||
}
|
||||
|
||||
static inline bool iwl_trans_is_fw_error(struct iwl_trans *trans)
|
||||
{
|
||||
return test_bit(STATUS_FW_ERROR, &trans->status);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function notifies the transport layer of firmware error, the recovery
|
||||
* will be handled by the op mode
|
||||
*/
|
||||
static inline void iwl_trans_notify_fw_error(struct iwl_trans *trans)
|
||||
{
|
||||
trans->state = IWL_TRANS_NO_FW;
|
||||
set_bit(STATUS_FW_ERROR, &trans->status);
|
||||
}
|
||||
/*****************************************************
|
||||
* PCIe handling
|
||||
*****************************************************/
|
||||
@@ -1289,4 +1258,8 @@ static inline u16 iwl_trans_get_device_id(struct iwl_trans *trans)
|
||||
return u32_get_bits(trans->info.hw_id, GENMASK(31, 16));
|
||||
}
|
||||
|
||||
bool iwl_trans_is_pm_supported(struct iwl_trans *trans);
|
||||
|
||||
bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans);
|
||||
|
||||
#endif /* __iwl_trans_h__ */
|
||||
|
||||
@@ -300,13 +300,11 @@ enum iwl_sap_msg {
|
||||
* @type: See &enum iwl_sap_msg.
|
||||
* @len: The length of the message (header not included).
|
||||
* @seq_num: For debug.
|
||||
* @payload: The payload of the message.
|
||||
*/
|
||||
struct iwl_sap_hdr {
|
||||
__le16 type;
|
||||
__le16 len;
|
||||
__le32 seq_num;
|
||||
u8 payload[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "mcc.h"
|
||||
#include "sta.h"
|
||||
#include "mlo.h"
|
||||
#include "key.h"
|
||||
|
||||
#include "fw/api/d3.h"
|
||||
#include "fw/api/offload.h"
|
||||
@@ -40,8 +41,6 @@ enum iwl_mld_d3_notif {
|
||||
struct iwl_mld_resume_key_iter_data {
|
||||
struct iwl_mld *mld;
|
||||
struct iwl_mld_wowlan_status *wowlan_status;
|
||||
u32 num_keys, gtk_cipher, igtk_cipher, bigtk_cipher;
|
||||
bool unhandled_cipher;
|
||||
};
|
||||
|
||||
struct iwl_mld_suspend_key_iter_data {
|
||||
@@ -71,6 +70,12 @@ struct iwl_mld_mcast_key_data {
|
||||
|
||||
};
|
||||
|
||||
struct iwl_mld_wowlan_mlo_key {
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
u8 idx, type, link_id;
|
||||
u8 pn[6];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_mld_wowlan_status - contains wowlan status data from
|
||||
* all wowlan notifications
|
||||
@@ -89,6 +94,8 @@ struct iwl_mld_mcast_key_data {
|
||||
* @bigtk: data of the last two used gtk's by the FW upon resume
|
||||
* @ptk: last seq numbers per tid passed by the FW,
|
||||
* holds both in tkip and aes formats
|
||||
* @num_mlo_keys: number of &struct iwl_mld_wowlan_mlo_key structs
|
||||
* @mlo_keys: array of MLO keys
|
||||
*/
|
||||
struct iwl_mld_wowlan_status {
|
||||
u32 wakeup_reasons;
|
||||
@@ -108,6 +115,9 @@ struct iwl_mld_wowlan_status {
|
||||
struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT];
|
||||
|
||||
} ptk;
|
||||
|
||||
int num_mlo_keys;
|
||||
struct iwl_mld_wowlan_mlo_key mlo_keys[WOWLAN_MAX_MLO_KEYS];
|
||||
};
|
||||
|
||||
#define NETDETECT_QUERY_BUF_LEN \
|
||||
@@ -271,7 +281,7 @@ iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data *gtk_data,
|
||||
static void
|
||||
iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
||||
struct iwl_mld_wowlan_status *wowlan_status,
|
||||
const struct iwl_wowlan_gtk_status_v3 *gtk_data,
|
||||
const struct iwl_wowlan_gtk_status *gtk_data,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
|
||||
{
|
||||
int status_idx = 0;
|
||||
@@ -283,8 +293,9 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
||||
for (int notif_idx = 0; notif_idx < ARRAY_SIZE(wowlan_status->gtk);
|
||||
notif_idx++) {
|
||||
int rsc_idx;
|
||||
u8 key_status = gtk_data[notif_idx].key_status;
|
||||
|
||||
if (!(gtk_data[notif_idx].key_len))
|
||||
if (!key_status)
|
||||
continue;
|
||||
|
||||
wowlan_status->gtk[status_idx].len =
|
||||
@@ -294,10 +305,6 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
||||
wowlan_status->gtk[status_idx].id =
|
||||
wowlan_status->gtk[status_idx].flags &
|
||||
IWL_WOWLAN_GTK_IDX_MASK;
|
||||
memcpy(wowlan_status->gtk[status_idx].key,
|
||||
gtk_data[notif_idx].key,
|
||||
sizeof(gtk_data[notif_idx].key));
|
||||
|
||||
/* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc
|
||||
* The gtk ids can be any two numbers between 0 and 3,
|
||||
* the id_map maps between the key id and the index in sc->mcast
|
||||
@@ -307,13 +314,27 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
||||
iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
|
||||
sc, rsc_idx);
|
||||
|
||||
/* if it's as long as the TKIP encryption key, copy MIC key */
|
||||
if (wowlan_status->gtk[status_idx].len ==
|
||||
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
|
||||
memcpy(wowlan_status->gtk[status_idx].key +
|
||||
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
|
||||
gtk_data[notif_idx].tkip_mic_key,
|
||||
sizeof(gtk_data[notif_idx].tkip_mic_key));
|
||||
if (key_status == IWL_WOWLAN_STATUS_NEW_KEY) {
|
||||
memcpy(wowlan_status->gtk[status_idx].key,
|
||||
gtk_data[notif_idx].key,
|
||||
sizeof(gtk_data[notif_idx].key));
|
||||
|
||||
/* if it's as long as the TKIP encryption key,
|
||||
* copy MIC key
|
||||
*/
|
||||
if (wowlan_status->gtk[status_idx].len ==
|
||||
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
|
||||
memcpy(wowlan_status->gtk[status_idx].key +
|
||||
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
|
||||
gtk_data[notif_idx].tkip_mic_key,
|
||||
sizeof(gtk_data[notif_idx].tkip_mic_key));
|
||||
} else {
|
||||
/* If the key status is WOWLAN_STATUS_OLD_KEY, it
|
||||
* indicates that no key material is present, Set the
|
||||
* key length to 0 as an indication
|
||||
*/
|
||||
wowlan_status->gtk[status_idx].len = 0;
|
||||
}
|
||||
status_idx++;
|
||||
}
|
||||
}
|
||||
@@ -360,11 +381,11 @@ static void
|
||||
iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
|
||||
const struct iwl_wowlan_igtk_status *igtk)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key));
|
||||
|
||||
if (!igtk->key_len)
|
||||
if (!igtk->key_status)
|
||||
return;
|
||||
|
||||
BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key));
|
||||
|
||||
wowlan_status->igtk.len = igtk->key_len;
|
||||
wowlan_status->igtk.flags = igtk->key_flags;
|
||||
wowlan_status->igtk.id =
|
||||
@@ -372,7 +393,15 @@ iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
|
||||
IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) +
|
||||
WOWLAN_IGTK_MIN_INDEX;
|
||||
|
||||
memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key));
|
||||
if (igtk->key_status == IWL_WOWLAN_STATUS_NEW_KEY)
|
||||
memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key));
|
||||
else
|
||||
/* If the key status is WOWLAN_STATUS_OLD_KEY, it indicates
|
||||
* that no key material is present. Set the key length to 0
|
||||
* as an indication.
|
||||
*/
|
||||
wowlan_status->igtk.len = 0;
|
||||
|
||||
iwl_mld_convert_mcast_ipn(&wowlan_status->igtk, igtk);
|
||||
}
|
||||
|
||||
@@ -386,7 +415,7 @@ iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
|
||||
|
||||
for (int notif_idx = 0; notif_idx < WOWLAN_BIGTK_KEYS_NUM;
|
||||
notif_idx++) {
|
||||
if (!bigtk[notif_idx].key_len)
|
||||
if (!bigtk[notif_idx].key_status)
|
||||
continue;
|
||||
|
||||
wowlan_status->bigtk[status_idx].len = bigtk[notif_idx].key_len;
|
||||
@@ -399,32 +428,218 @@ iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status,
|
||||
|
||||
BUILD_BUG_ON(sizeof(wowlan_status->bigtk[status_idx].key) <
|
||||
sizeof(bigtk[notif_idx].key));
|
||||
memcpy(wowlan_status->bigtk[status_idx].key,
|
||||
bigtk[notif_idx].key, sizeof(bigtk[notif_idx].key));
|
||||
if (bigtk[notif_idx].key_status == IWL_WOWLAN_STATUS_NEW_KEY)
|
||||
memcpy(wowlan_status->bigtk[status_idx].key,
|
||||
bigtk[notif_idx].key,
|
||||
sizeof(bigtk[notif_idx].key));
|
||||
else
|
||||
/* If the key status is WOWLAN_STATUS_OLD_KEY, it
|
||||
* indicates that no key material is present. Set the
|
||||
* key length to 0 as an indication.
|
||||
*/
|
||||
wowlan_status->bigtk[status_idx].len = 0;
|
||||
|
||||
iwl_mld_convert_mcast_ipn(&wowlan_status->bigtk[status_idx],
|
||||
&bigtk[notif_idx]);
|
||||
status_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_convert_mlo_keys(struct iwl_mld *mld,
|
||||
const struct iwl_wowlan_info_notif *notif,
|
||||
struct iwl_mld_wowlan_status *wowlan_status)
|
||||
{
|
||||
if (!notif->num_mlo_link_keys)
|
||||
return;
|
||||
|
||||
wowlan_status->num_mlo_keys = notif->num_mlo_link_keys;
|
||||
|
||||
if (IWL_FW_CHECK(mld, wowlan_status->num_mlo_keys > WOWLAN_MAX_MLO_KEYS,
|
||||
"Too many MLO keys: %d, max %d\n",
|
||||
wowlan_status->num_mlo_keys, WOWLAN_MAX_MLO_KEYS))
|
||||
wowlan_status->num_mlo_keys = WOWLAN_MAX_MLO_KEYS;
|
||||
|
||||
for (int i = 0; i < wowlan_status->num_mlo_keys; i++) {
|
||||
const struct iwl_wowlan_mlo_gtk *fw_mlo_key = ¬if->mlo_gtks[i];
|
||||
struct iwl_mld_wowlan_mlo_key *driver_mlo_key =
|
||||
&wowlan_status->mlo_keys[i];
|
||||
u16 flags = le16_to_cpu(fw_mlo_key->flags);
|
||||
|
||||
driver_mlo_key->link_id =
|
||||
u16_get_bits(flags, WOWLAN_MLO_GTK_FLAG_LINK_ID_MSK);
|
||||
driver_mlo_key->type =
|
||||
u16_get_bits(flags, WOWLAN_MLO_GTK_FLAG_KEY_TYPE_MSK);
|
||||
driver_mlo_key->idx =
|
||||
u16_get_bits(flags, WOWLAN_MLO_GTK_FLAG_KEY_ID_MSK);
|
||||
|
||||
BUILD_BUG_ON(sizeof(driver_mlo_key->key) != sizeof(fw_mlo_key->key));
|
||||
BUILD_BUG_ON(sizeof(driver_mlo_key->pn) != sizeof(fw_mlo_key->pn));
|
||||
|
||||
memcpy(driver_mlo_key->key, fw_mlo_key->key, sizeof(fw_mlo_key->key));
|
||||
memcpy(driver_mlo_key->pn, fw_mlo_key->pn, sizeof(fw_mlo_key->pn));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_convert_wowlan_notif_v5(const struct iwl_wowlan_info_notif_v5 *notif_v5,
|
||||
struct iwl_wowlan_info_notif *notif)
|
||||
{
|
||||
/* Convert GTK from v3 to the new format */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->gtk) != ARRAY_SIZE(notif_v5->gtk));
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(notif_v5->gtk); i++) {
|
||||
const struct iwl_wowlan_gtk_status_v3 *gtk_v3 = ¬if_v5->gtk[i];
|
||||
struct iwl_wowlan_gtk_status *gtk = ¬if->gtk[i];
|
||||
|
||||
/* Copy key material and metadata */
|
||||
BUILD_BUG_ON(sizeof(gtk->key) != sizeof(gtk_v3->key));
|
||||
BUILD_BUG_ON(sizeof(gtk->tkip_mic_key) != sizeof(gtk_v3->tkip_mic_key));
|
||||
|
||||
memcpy(gtk->key, gtk_v3->key, sizeof(gtk_v3->key));
|
||||
|
||||
gtk->key_len = gtk_v3->key_len;
|
||||
gtk->key_flags = gtk_v3->key_flags;
|
||||
|
||||
memcpy(gtk->tkip_mic_key, gtk_v3->tkip_mic_key,
|
||||
sizeof(gtk_v3->tkip_mic_key));
|
||||
gtk->sc = gtk_v3->sc;
|
||||
|
||||
/* Set key_status based on whether key material is present.
|
||||
* in v5, a key is either invalid (should be skipped) or has
|
||||
* both meta data and the key itself.
|
||||
*/
|
||||
if (gtk_v3->key_len)
|
||||
gtk->key_status = IWL_WOWLAN_STATUS_NEW_KEY;
|
||||
}
|
||||
|
||||
/* Convert IGTK from v1 to the new format, only one IGTK is passed by FW */
|
||||
BUILD_BUG_ON(offsetof(struct iwl_wowlan_igtk_status, key_status) !=
|
||||
sizeof(struct iwl_wowlan_igtk_status_v1));
|
||||
|
||||
memcpy(¬if->igtk[0], ¬if_v5->igtk[0],
|
||||
offsetof(struct iwl_wowlan_igtk_status, key_status));
|
||||
|
||||
/* Set key_status based on whether key material is present.
|
||||
* in v5, a key is either invalid (should be skipped) or has
|
||||
* both meta data and the key itself.
|
||||
*/
|
||||
if (notif_v5->igtk[0].key_len)
|
||||
notif->igtk[0].key_status = IWL_WOWLAN_STATUS_NEW_KEY;
|
||||
|
||||
/* Convert BIGTK from v1 to the new format */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->bigtk) != ARRAY_SIZE(notif_v5->bigtk));
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(notif_v5->bigtk); i++) {
|
||||
/* Copy everything until key_status */
|
||||
memcpy(¬if->bigtk[i], ¬if_v5->bigtk[i],
|
||||
offsetof(struct iwl_wowlan_igtk_status, key_status));
|
||||
|
||||
/* Set key_status based on whether key material is present.
|
||||
* in v5, a key is either invalid (should be skipped) or has
|
||||
* both meta data and the key itself.
|
||||
*/
|
||||
if (notif_v5->bigtk[i].key_len)
|
||||
notif->bigtk[i].key_status = IWL_WOWLAN_STATUS_NEW_KEY;
|
||||
}
|
||||
|
||||
notif->replay_ctr = notif_v5->replay_ctr;
|
||||
notif->pattern_number = notif_v5->pattern_number;
|
||||
notif->qos_seq_ctr = notif_v5->qos_seq_ctr;
|
||||
notif->wakeup_reasons = notif_v5->wakeup_reasons;
|
||||
notif->num_of_gtk_rekeys = notif_v5->num_of_gtk_rekeys;
|
||||
notif->transmitted_ndps = notif_v5->transmitted_ndps;
|
||||
notif->received_beacons = notif_v5->received_beacons;
|
||||
notif->tid_tear_down = notif_v5->tid_tear_down;
|
||||
notif->station_id = notif_v5->station_id;
|
||||
notif->num_mlo_link_keys = notif_v5->num_mlo_link_keys;
|
||||
notif->tid_offloaded_tx = notif_v5->tid_offloaded_tx;
|
||||
|
||||
/* Copy MLO GTK keys */
|
||||
if (notif_v5->num_mlo_link_keys) {
|
||||
memcpy(notif->mlo_gtks, notif_v5->mlo_gtks,
|
||||
notif_v5->num_mlo_link_keys * sizeof(struct iwl_wowlan_mlo_gtk));
|
||||
}
|
||||
}
|
||||
|
||||
static bool iwl_mld_validate_wowlan_notif_size(struct iwl_mld *mld,
|
||||
u32 len,
|
||||
u32 expected_len,
|
||||
u8 num_mlo_keys,
|
||||
int version)
|
||||
{
|
||||
u32 len_with_mlo_keys;
|
||||
|
||||
if (IWL_FW_CHECK(mld, len < expected_len,
|
||||
"Invalid wowlan_info_notif v%d (expected=%u got=%u)\n",
|
||||
version, expected_len, len))
|
||||
return false;
|
||||
|
||||
len_with_mlo_keys = expected_len +
|
||||
(num_mlo_keys * sizeof(struct iwl_wowlan_mlo_gtk));
|
||||
|
||||
if (IWL_FW_CHECK(mld, len < len_with_mlo_keys,
|
||||
"Invalid wowlan_info_notif v%d with MLO keys (expected=%u got=%u)\n",
|
||||
version, len_with_mlo_keys, len))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
||||
struct iwl_mld_wowlan_status *wowlan_status,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
const struct iwl_wowlan_info_notif *notif = (void *)pkt->data;
|
||||
u32 expected_len, len = iwl_rx_packet_payload_len(pkt);
|
||||
const struct iwl_wowlan_info_notif *notif;
|
||||
struct iwl_wowlan_info_notif *converted_notif __free(kfree) = NULL;
|
||||
u32 len = iwl_rx_packet_payload_len(pkt);
|
||||
int wowlan_info_ver = iwl_fw_lookup_notif_ver(mld->fw,
|
||||
PROT_OFFLOAD_GROUP,
|
||||
WOWLAN_INFO_NOTIFICATION,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
expected_len = sizeof(*notif);
|
||||
if (wowlan_info_ver == 5) {
|
||||
/* v5 format - validate before conversion */
|
||||
const struct iwl_wowlan_info_notif_v5 *notif_v5 = (void *)pkt->data;
|
||||
|
||||
if (IWL_FW_CHECK(mld, len < expected_len,
|
||||
"Invalid wowlan_info_notif (expected=%ud got=%ud)\n",
|
||||
expected_len, len))
|
||||
if (!iwl_mld_validate_wowlan_notif_size(mld, len,
|
||||
sizeof(*notif_v5),
|
||||
notif_v5->num_mlo_link_keys,
|
||||
5))
|
||||
return true;
|
||||
|
||||
converted_notif = kzalloc(struct_size(converted_notif,
|
||||
mlo_gtks,
|
||||
notif_v5->num_mlo_link_keys),
|
||||
GFP_ATOMIC);
|
||||
if (!converted_notif) {
|
||||
IWL_ERR(mld,
|
||||
"Failed to allocate memory for converted wowlan_info_notif\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
iwl_mld_convert_wowlan_notif_v5(notif_v5,
|
||||
converted_notif);
|
||||
notif = converted_notif;
|
||||
} else if (wowlan_info_ver == 6) {
|
||||
notif = (void *)pkt->data;
|
||||
if (!iwl_mld_validate_wowlan_notif_size(mld, len,
|
||||
sizeof(*notif),
|
||||
notif->num_mlo_link_keys,
|
||||
6))
|
||||
return true;
|
||||
} else {
|
||||
/* smaller versions are not supported */
|
||||
IWL_WARN(mld,
|
||||
"Unsupported wowlan_info_notif version %d\n",
|
||||
wowlan_info_ver);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID,
|
||||
"Invalid tid_offloaded_tx %d\n",
|
||||
wowlan_status->tid_offloaded_tx))
|
||||
notif->tid_offloaded_tx))
|
||||
return true;
|
||||
|
||||
iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk,
|
||||
@@ -442,8 +657,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
||||
wowlan_status->num_of_gtk_rekeys =
|
||||
le32_to_cpu(notif->num_of_gtk_rekeys);
|
||||
wowlan_status->wakeup_reasons = le32_to_cpu(notif->wakeup_reasons);
|
||||
|
||||
iwl_mld_convert_mlo_keys(mld, notif, wowlan_status);
|
||||
|
||||
return false;
|
||||
/* TODO: mlo_links (task=MLO)*/
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -619,8 +836,8 @@ iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf *key,
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key,
|
||||
struct iwl_mld_mcast_key_data *key_data)
|
||||
iwl_mld_update_mcast_rx_seq(struct ieee80211_key_conf *key,
|
||||
struct iwl_mld_mcast_key_data *key_data)
|
||||
{
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
@@ -687,132 +904,53 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
|
||||
struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status;
|
||||
u8 status_idx;
|
||||
|
||||
/* TODO: check key link id (task=MLO) */
|
||||
if (data->unhandled_cipher)
|
||||
return;
|
||||
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
/* ignore WEP completely, nothing to do */
|
||||
return;
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP_256:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
if (key->keyidx >= 0 && key->keyidx <= 3) {
|
||||
/* PTK */
|
||||
if (sta) {
|
||||
iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
|
||||
sta, key,
|
||||
key->cipher ==
|
||||
WLAN_CIPHER_SUITE_TKIP);
|
||||
return;
|
||||
/* GTK */
|
||||
} else {
|
||||
status_idx = key->keyidx == wowlan_status->gtk[1].id;
|
||||
iwl_mld_update_mcast_rx_seq(key,
|
||||
&wowlan_status->gtk[status_idx]);
|
||||
}
|
||||
|
||||
if (WARN_ON(data->gtk_cipher &&
|
||||
data->gtk_cipher != key->cipher))
|
||||
return;
|
||||
|
||||
data->gtk_cipher = key->cipher;
|
||||
status_idx = key->keyidx == wowlan_status->gtk[1].id;
|
||||
iwl_mld_set_key_rx_seq(key, &wowlan_status->gtk[status_idx]);
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
|
||||
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
if (key->keyidx == 4 || key->keyidx == 5) {
|
||||
if (WARN_ON(data->igtk_cipher &&
|
||||
data->igtk_cipher != key->cipher))
|
||||
return;
|
||||
|
||||
data->igtk_cipher = key->cipher;
|
||||
if (key->keyidx == wowlan_status->igtk.id)
|
||||
iwl_mld_set_key_rx_seq(key, &wowlan_status->igtk);
|
||||
}
|
||||
if (key->keyidx == 6 || key->keyidx == 7) {
|
||||
if (WARN_ON(data->bigtk_cipher &&
|
||||
data->bigtk_cipher != key->cipher))
|
||||
return;
|
||||
|
||||
data->bigtk_cipher = key->cipher;
|
||||
status_idx = key->keyidx == wowlan_status->bigtk[1].id;
|
||||
iwl_mld_set_key_rx_seq(key, &wowlan_status->bigtk[status_idx]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
data->unhandled_cipher = true;
|
||||
return;
|
||||
}
|
||||
data->num_keys++;
|
||||
|
||||
/* IGTK */
|
||||
if (key->keyidx == 4 || key->keyidx == 5) {
|
||||
if (key->keyidx == wowlan_status->igtk.id)
|
||||
iwl_mld_update_mcast_rx_seq(key, &wowlan_status->igtk);
|
||||
}
|
||||
|
||||
/* BIGTK */
|
||||
if (key->keyidx == 6 || key->keyidx == 7) {
|
||||
status_idx = key->keyidx == wowlan_status->bigtk[1].id;
|
||||
iwl_mld_update_mcast_rx_seq(key,
|
||||
&wowlan_status->bigtk[status_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
||||
struct iwl_mld *mld,
|
||||
struct iwl_mld_mcast_key_data *key_data,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 cipher)
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
struct ieee80211_key_conf *key_config;
|
||||
struct {
|
||||
struct ieee80211_key_conf conf;
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
} conf = {
|
||||
.conf.cipher = cipher,
|
||||
.conf.keyidx = key_data->id,
|
||||
};
|
||||
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
|
||||
BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_128);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_256);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_AES_CMAC);
|
||||
BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key));
|
||||
|
||||
if (!key_data->len)
|
||||
return;
|
||||
|
||||
switch (cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_GCMP_256:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_TKIP;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
|
||||
conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
memcpy(conf.conf.key, key_data->key, conf.conf.keylen);
|
||||
|
||||
memcpy(key, key_data->key, sizeof(key_data->key));
|
||||
|
||||
key_config = ieee80211_gtk_rekey_add(vif, key_data->id, key,
|
||||
sizeof(key), link_id);
|
||||
key_config = ieee80211_gtk_rekey_add(vif, key_data->id, key_data->key,
|
||||
sizeof(key_data->key), link_id);
|
||||
if (IS_ERR(key_config))
|
||||
return;
|
||||
|
||||
iwl_mld_set_key_rx_seq(key_config, key_data);
|
||||
iwl_mld_update_mcast_rx_seq(key_config, key_data);
|
||||
|
||||
/* The FW holds only one igtk so we keep track of the valid one */
|
||||
if (key_config->keyidx == 4 || key_config->keyidx == 5) {
|
||||
@@ -831,37 +969,78 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
||||
}
|
||||
|
||||
/* Also keep track of the new BIGTK */
|
||||
if ((key_config->keyidx == 6 || key_config->keyidx == 7) &&
|
||||
vif->type == NL80211_IFTYPE_STATION) {
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
rcu_assign_pointer(mld_vif->bigtks[key_config->keyidx - 6], key_config);
|
||||
}
|
||||
if (key_config->keyidx == 6 || key_config->keyidx == 7)
|
||||
iwl_mld_track_bigtk(mld, vif, key_config, true);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_add_all_rekeys(struct ieee80211_vif *vif,
|
||||
iwl_mld_add_all_rekeys(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mld_wowlan_status *wowlan_status,
|
||||
struct iwl_mld_resume_key_iter_data *key_iter_data,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++)
|
||||
iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
|
||||
&wowlan_status->gtk[i],
|
||||
link_conf,
|
||||
key_iter_data->gtk_cipher);
|
||||
iwl_mld_add_mcast_rekey(vif, mld, &wowlan_status->gtk[i],
|
||||
link_conf);
|
||||
|
||||
iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
|
||||
&wowlan_status->igtk,
|
||||
link_conf, key_iter_data->igtk_cipher);
|
||||
iwl_mld_add_mcast_rekey(vif, mld, &wowlan_status->igtk, link_conf);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++)
|
||||
iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
|
||||
&wowlan_status->bigtk[i],
|
||||
link_conf,
|
||||
key_iter_data->bigtk_cipher);
|
||||
iwl_mld_add_mcast_rekey(vif, mld, &wowlan_status->bigtk[i],
|
||||
link_conf);
|
||||
}
|
||||
|
||||
static void iwl_mld_mlo_rekey(struct iwl_mld *mld,
|
||||
struct iwl_mld_wowlan_status *wowlan_status,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_old_mlo_keys *old_keys __free(kfree) = NULL;
|
||||
|
||||
IWL_DEBUG_WOWLAN(mld, "Num of MLO Keys: %d\n", wowlan_status->num_mlo_keys);
|
||||
|
||||
if (!wowlan_status->num_mlo_keys)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < wowlan_status->num_mlo_keys; i++) {
|
||||
struct iwl_mld_wowlan_mlo_key *mlo_key = &wowlan_status->mlo_keys[i];
|
||||
struct ieee80211_key_conf *key;
|
||||
struct ieee80211_key_seq seq;
|
||||
u8 link_id = mlo_key->link_id;
|
||||
|
||||
if (IWL_FW_CHECK(mld, mlo_key->link_id >= IEEE80211_MLD_MAX_NUM_LINKS ||
|
||||
mlo_key->idx >= 8 ||
|
||||
mlo_key->type >= WOWLAN_MLO_GTK_KEY_NUM_TYPES,
|
||||
"Invalid MLO key link_id %d, idx %d, type %d\n",
|
||||
mlo_key->link_id, mlo_key->idx, mlo_key->type))
|
||||
continue;
|
||||
|
||||
if (!(vif->valid_links & BIT(link_id)) ||
|
||||
(vif->active_links & BIT(link_id)))
|
||||
continue;
|
||||
|
||||
IWL_DEBUG_WOWLAN(mld, "Add MLO key id %d, link id %d\n",
|
||||
mlo_key->idx, link_id);
|
||||
|
||||
key = ieee80211_gtk_rekey_add(vif, mlo_key->idx, mlo_key->key,
|
||||
sizeof(mlo_key->key), link_id);
|
||||
|
||||
if (IS_ERR(key))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* mac80211 expects the PN in big-endian
|
||||
* also note that seq is a union of all cipher types
|
||||
* (ccmp, gcmp, cmac, gmac), and they all have the same
|
||||
* pn field (of length 6) so just copy it to ccmp.pn.
|
||||
*/
|
||||
for (int j = 5; j >= 0; j--)
|
||||
seq.ccmp.pn[5 - j] = mlo_key->pn[j];
|
||||
|
||||
/* group keys are non-QoS and use TID 0 */
|
||||
ieee80211_set_key_rx_seq(key, 0, &seq);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -884,23 +1063,19 @@ iwl_mld_update_sec_keys(struct iwl_mld *mld,
|
||||
ieee80211_iter_keys(mld->hw, vif, iwl_mld_resume_keys_iter,
|
||||
&key_iter_data);
|
||||
|
||||
if (key_iter_data.unhandled_cipher)
|
||||
return false;
|
||||
|
||||
IWL_DEBUG_WOWLAN(mld,
|
||||
"Number of installed keys: %d, Number of rekeys: %d\n",
|
||||
key_iter_data.num_keys,
|
||||
IWL_DEBUG_WOWLAN(mld, "Number of rekeys: %d\n",
|
||||
wowlan_status->num_of_gtk_rekeys);
|
||||
|
||||
if (!key_iter_data.num_keys || !wowlan_status->num_of_gtk_rekeys)
|
||||
if (!wowlan_status->num_of_gtk_rekeys)
|
||||
return true;
|
||||
|
||||
iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data,
|
||||
iwl_mld_add_all_rekeys(mld, vif, wowlan_status,
|
||||
link_conf);
|
||||
|
||||
iwl_mld_mlo_rekey(mld, wowlan_status, vif);
|
||||
|
||||
ieee80211_gtk_rekey_notify(vif, link_conf->bssid,
|
||||
(void *)&replay_ctr, GFP_KERNEL);
|
||||
/* TODO: MLO rekey (task=MLO) */
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1179,7 +1354,6 @@ static int iwl_mld_wait_d3_notif(struct iwl_mld *mld,
|
||||
WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
|
||||
};
|
||||
struct iwl_notification_wait wait_d3_notif;
|
||||
enum iwl_d3_status d3_status;
|
||||
int ret;
|
||||
|
||||
if (with_wowlan)
|
||||
@@ -1195,14 +1369,10 @@ static int iwl_mld_wait_d3_notif(struct iwl_mld *mld,
|
||||
iwl_mld_handle_d3_notif,
|
||||
resume_data);
|
||||
|
||||
ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false);
|
||||
if (ret || d3_status != IWL_D3_STATUS_ALIVE) {
|
||||
if (d3_status != IWL_D3_STATUS_ALIVE) {
|
||||
IWL_INFO(mld, "Device was reset during suspend\n");
|
||||
ret = -ENOENT;
|
||||
} else {
|
||||
IWL_ERR(mld, "Transport resume failed\n");
|
||||
}
|
||||
ret = iwl_trans_d3_resume(mld->trans, false);
|
||||
if (ret) {
|
||||
/* Avoid sending commands if the FW is dead */
|
||||
iwl_trans_notify_fw_error(mld->trans);
|
||||
iwl_remove_notification(&mld->notif_wait, &wait_d3_notif);
|
||||
return ret;
|
||||
}
|
||||
@@ -1236,16 +1406,11 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld)
|
||||
|
||||
iwl_mld_low_latency_stop(mld);
|
||||
|
||||
/* This will happen if iwl_mld_supsend failed with FW error */
|
||||
if (mld->trans->state == IWL_TRANS_NO_FW &&
|
||||
test_bit(STATUS_FW_ERROR, &mld->trans->status))
|
||||
return -ENODEV;
|
||||
|
||||
ret = iwl_mld_update_device_power(mld, true);
|
||||
if (ret) {
|
||||
IWL_ERR(mld,
|
||||
"d3 suspend: couldn't send power_device %d\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD,
|
||||
@@ -1253,24 +1418,20 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld)
|
||||
if (ret) {
|
||||
IWL_ERR(mld,
|
||||
"d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iwl_trans_d3_suspend(mld->trans, false, false);
|
||||
ret = iwl_trans_d3_suspend(mld->trans, false);
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret);
|
||||
/* We are going to stop the FW. Avoid sending commands in that flow */
|
||||
iwl_trans_notify_fw_error(mld->trans);
|
||||
} else {
|
||||
/* Async notification might send hcmds, which is not allowed in suspend */
|
||||
iwl_mld_cancel_async_notifications(mld);
|
||||
mld->fw_status.in_d3 = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
mld->trans->state = IWL_TRANS_NO_FW;
|
||||
set_bit(STATUS_FW_ERROR, &mld->trans->status);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1290,15 +1451,12 @@ int iwl_mld_no_wowlan_resume(struct iwl_mld *mld)
|
||||
iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
|
||||
|
||||
ret = iwl_mld_wait_d3_notif(mld, &resume_data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE))
|
||||
return -ENODEV;
|
||||
|
||||
if (ret) {
|
||||
mld->trans->state = IWL_TRANS_NO_FW;
|
||||
set_bit(STATUS_FW_ERROR, &mld->trans->status);
|
||||
return ret;
|
||||
}
|
||||
iwl_mld_low_latency_restart(mld);
|
||||
|
||||
return iwl_mld_update_device_power(mld, false);
|
||||
@@ -1530,7 +1688,8 @@ static void
|
||||
iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld,
|
||||
struct cfg80211_wowlan *wowlan,
|
||||
struct iwl_wowlan_config_cmd *wowlan_config_cmd,
|
||||
struct ieee80211_sta *ap_sta)
|
||||
struct ieee80211_sta *ap_sta,
|
||||
struct ieee80211_bss_conf *link)
|
||||
{
|
||||
wowlan_config_cmd->is_11n_connection =
|
||||
ap_sta->deflink.ht_cap.ht_supported;
|
||||
@@ -1540,6 +1699,9 @@ iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld,
|
||||
if (ap_sta->mfp)
|
||||
wowlan_config_cmd->flags |= IS_11W_ASSOC;
|
||||
|
||||
if (iwl_mld_beacon_protection_enabled(mld, link))
|
||||
wowlan_config_cmd->flags |= HAS_BEACON_PROTECTION;
|
||||
|
||||
if (wowlan->disconnect)
|
||||
wowlan_config_cmd->wakeup_filter |=
|
||||
cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
|
||||
@@ -1737,7 +1899,7 @@ iwl_mld_wowlan_config(struct iwl_mld *mld, struct ieee80211_vif *bss_vif,
|
||||
return ret;
|
||||
|
||||
iwl_mld_set_wowlan_config_cmd(mld, wowlan,
|
||||
&wowlan_config_cmd, ap_sta);
|
||||
&wowlan_config_cmd, ap_sta, link_conf);
|
||||
ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_CONFIGURATION,
|
||||
&wowlan_config_cmd);
|
||||
if (ret)
|
||||
@@ -1807,7 +1969,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld)
|
||||
};
|
||||
int link_id;
|
||||
int ret;
|
||||
bool fw_err = false;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
@@ -1850,7 +2011,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld)
|
||||
ret = iwl_mld_wait_d3_notif(mld, &resume_data, true);
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret);
|
||||
fw_err = true;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1887,11 +2047,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld)
|
||||
goto out;
|
||||
|
||||
err:
|
||||
if (fw_err) {
|
||||
mld->trans->state = IWL_TRANS_NO_FW;
|
||||
set_bit(STATUS_FW_ERROR, &mld->trans->status);
|
||||
}
|
||||
|
||||
mld->fw_status.in_hw_restart = true;
|
||||
ret = 1;
|
||||
out:
|
||||
|
||||
@@ -86,7 +86,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf,
|
||||
|
||||
if (count == 6 && !strcmp(buf, "nolog\n")) {
|
||||
mld->fw_status.do_not_dump_once = true;
|
||||
iwl_trans_suppress_cmd_error_once(mld->trans);
|
||||
mld->trans->suppress_cmd_error_once = true;
|
||||
}
|
||||
|
||||
/* take the return value to make compiler happy - it will
|
||||
@@ -1001,8 +1001,12 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
|
||||
* If not, this is a per-link dir of a MLO vif, add in it the iwlmld
|
||||
* dir.
|
||||
*/
|
||||
if (!mld_link_dir)
|
||||
if (!mld_link_dir) {
|
||||
mld_link_dir = debugfs_create_dir("iwlmld", dir);
|
||||
} else {
|
||||
/* Release the reference from debugfs_lookup */
|
||||
dput(mld_link_dir);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
|
||||
@@ -115,20 +115,12 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
|
||||
|
||||
static void iwl_mld_set_he_support(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_config_cmd *cmd,
|
||||
int cmd_ver)
|
||||
struct iwl_mac_config_cmd *cmd)
|
||||
{
|
||||
if (vif->type == NL80211_IFTYPE_AP) {
|
||||
if (cmd_ver == 2)
|
||||
cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1);
|
||||
else
|
||||
cmd->wifi_gen.he_ap_support = 1;
|
||||
} else {
|
||||
if (cmd_ver == 2)
|
||||
cmd->wifi_gen_v2.he_support = cpu_to_le16(1);
|
||||
else
|
||||
cmd->wifi_gen.he_support = 1;
|
||||
}
|
||||
if (vif->type == NL80211_IFTYPE_AP)
|
||||
cmd->wifi_gen.he_ap_support = 1;
|
||||
else
|
||||
cmd->wifi_gen.he_support = 1;
|
||||
}
|
||||
|
||||
/* fill the common part for all interface types */
|
||||
@@ -140,9 +132,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
unsigned int link_id;
|
||||
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw,
|
||||
WIDE_ID(MAC_CONF_GROUP,
|
||||
MAC_CONFIG_CMD), 0);
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
@@ -169,11 +158,8 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
|
||||
* and enable both when we have MLO.
|
||||
*/
|
||||
if (ieee80211_vif_is_mld(vif)) {
|
||||
iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);
|
||||
if (cmd_ver == 2)
|
||||
cmd->wifi_gen_v2.eht_support = cpu_to_le32(1);
|
||||
else
|
||||
cmd->wifi_gen.eht_support = 1;
|
||||
iwl_mld_set_he_support(mld, vif, cmd);
|
||||
cmd->wifi_gen.eht_support = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -181,7 +167,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
|
||||
if (!link_conf->he_support)
|
||||
continue;
|
||||
|
||||
iwl_mld_set_he_support(mld, vif, cmd, cmd_ver);
|
||||
iwl_mld_set_he_support(mld, vif, cmd);
|
||||
|
||||
/* EHT, if supported, was already set above */
|
||||
break;
|
||||
@@ -451,24 +437,21 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
|
||||
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
|
||||
|
||||
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
|
||||
return -EINVAL;
|
||||
return;
|
||||
|
||||
RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
|
||||
|
||||
iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,
|
||||
mld_vif->fw_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iwl_mld_set_vif_associated(struct iwl_mld *mld,
|
||||
|
||||
@@ -125,8 +125,6 @@ struct iwl_mld_emlsr {
|
||||
* @ap_sta: pointer to AP sta, for easier access to it.
|
||||
* Relevant only for STA vifs.
|
||||
* @authorized: indicates the AP station was set to authorized
|
||||
* @bigtks: BIGTKs of the AP, for beacon protection.
|
||||
* Only valid for STA. (FIXME: needs to be per link)
|
||||
* @num_associated_stas: number of associated STAs. Relevant only for AP mode.
|
||||
* @ap_ibss_active: whether the AP/IBSS was started
|
||||
* @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the
|
||||
@@ -158,7 +156,6 @@ struct iwl_mld_vif {
|
||||
struct iwl_mld_session_protect session_protect;
|
||||
struct ieee80211_sta *ap_sta;
|
||||
bool authorized;
|
||||
struct ieee80211_key_conf __rcu *bigtks[2];
|
||||
u8 num_associated_stas;
|
||||
bool ap_ibss_active;
|
||||
enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround;
|
||||
@@ -227,7 +224,7 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
|
||||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action);
|
||||
int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
void iwl_mld_set_vif_associated(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif);
|
||||
u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld);
|
||||
|
||||
@@ -368,3 +368,41 @@ int iwl_mld_update_sta_keys(struct iwl_mld *mld,
|
||||
&data);
|
||||
return data.err;
|
||||
}
|
||||
|
||||
void iwl_mld_track_bigtk(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_key_conf *key, bool add)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_link *link;
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
if (WARN_ON(key->keyidx < 6 || key->keyidx > 7))
|
||||
return;
|
||||
|
||||
if (WARN_ON(key->link_id < 0))
|
||||
return;
|
||||
|
||||
link = iwl_mld_link_dereference_check(mld_vif, key->link_id);
|
||||
if (WARN_ON(!link))
|
||||
return;
|
||||
|
||||
if (add)
|
||||
rcu_assign_pointer(link->bigtks[key->keyidx - 6], key);
|
||||
else
|
||||
RCU_INIT_POINTER(link->bigtks[key->keyidx - 6], NULL);
|
||||
}
|
||||
|
||||
bool iwl_mld_beacon_protection_enabled(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link)
|
||||
{
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return false;
|
||||
|
||||
return rcu_access_pointer(mld_link->bigtks[0]) ||
|
||||
rcu_access_pointer(mld_link->bigtks[1]);
|
||||
}
|
||||
|
||||
@@ -36,4 +36,11 @@ iwl_mld_cleanup_keys_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
key->hw_key_idx = STA_KEY_IDX_INVALID;
|
||||
}
|
||||
|
||||
void iwl_mld_track_bigtk(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_key_conf *key, bool add);
|
||||
|
||||
bool iwl_mld_beacon_protection_enabled(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link);
|
||||
|
||||
#endif /* __iwl_mld_key_h__ */
|
||||
|
||||
@@ -532,7 +532,8 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
||||
le32_to_cpu(notif->consec_missed_beacons_other_link);
|
||||
struct ieee80211_bss_conf *link_conf =
|
||||
iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
|
||||
u32 bss_param_ch_cnt_link_id;
|
||||
struct ieee80211_bss_conf *other_link;
|
||||
u32 bss_param_ch_cnt_link_id, other_link_fw_id;
|
||||
struct ieee80211_vif *vif;
|
||||
u8 link_id;
|
||||
|
||||
@@ -550,11 +551,6 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
||||
if (WARN_ON(!vif))
|
||||
return;
|
||||
|
||||
mld->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"LinkId_%d_MacType_%d", fw_link_id,
|
||||
iwl_mld_mac80211_iftype_to_fw(vif));
|
||||
|
||||
iwl_dbg_tlv_time_point(&mld->fwrt,
|
||||
IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data);
|
||||
|
||||
@@ -572,8 +568,11 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
||||
if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) {
|
||||
ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
|
||||
|
||||
/* try to switch links, no-op if we don't have MLO */
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
/* Not in EMLSR and we can't hear the link.
|
||||
* Try to switch to a better link. EMLSR case is handled below.
|
||||
*/
|
||||
if (!iwl_mld_emlsr_active(vif))
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
}
|
||||
|
||||
/* no more logic if we're not in EMLSR */
|
||||
@@ -584,6 +583,17 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
||||
if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID)
|
||||
return;
|
||||
|
||||
other_link_fw_id = le32_to_cpu(notif->other_link_id);
|
||||
other_link = iwl_mld_fw_id_to_link_conf(mld, other_link_fw_id);
|
||||
|
||||
if (IWL_FW_CHECK(mld, !other_link, "link doesn't exist for: %d\n",
|
||||
other_link_fw_id))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_EHT(mld,
|
||||
"missed bcn on the other link (link_id=%u): %u\n",
|
||||
other_link->link_id, scnd_lnk_bcn_lost);
|
||||
|
||||
/* Exit EMLSR if we lost more than
|
||||
* IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links
|
||||
* OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link.
|
||||
|
||||
@@ -36,6 +36,7 @@ struct iwl_probe_resp_data {
|
||||
* @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked.
|
||||
* @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two.
|
||||
* This tracks the one IGTK that currently exists in FW.
|
||||
* @bigtks: BIGTKs of the AP. Only valid for STA mode.
|
||||
* @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
|
||||
* @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
|
||||
* @mon_sta: station used for TX injection in monitor interface.
|
||||
@@ -59,6 +60,7 @@ struct iwl_mld_link {
|
||||
struct ieee80211_chanctx_conf __rcu *chan_ctx;
|
||||
bool he_ru_2mhz_block;
|
||||
struct ieee80211_key_conf *igtk;
|
||||
struct ieee80211_key_conf __rcu *bigtks[2];
|
||||
);
|
||||
/* And here fields that survive a fw restart */
|
||||
struct iwl_mld_int_sta bcast_sta;
|
||||
|
||||
@@ -626,7 +626,7 @@ int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw,
|
||||
IEEE80211_VIF_SUPPORTS_CQM_RSSI;
|
||||
}
|
||||
|
||||
if (vif->p2p || iwl_fw_lookup_cmd_ver(mld->fw, PHY_CONTEXT_CMD, 0) < 5)
|
||||
if (vif->p2p)
|
||||
vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW;
|
||||
|
||||
/*
|
||||
@@ -1966,13 +1966,8 @@ iwl_mld_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
||||
iwl_fw_runtime_suspend(&mld->fwrt);
|
||||
|
||||
ret = iwl_mld_wowlan_suspend(mld, wowlan);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
mld->trans->state = IWL_TRANS_NO_FW;
|
||||
set_bit(STATUS_FW_ERROR, &mld->trans->status);
|
||||
}
|
||||
if (ret)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (iwl_mld_no_wowlan_suspend(mld))
|
||||
return 1;
|
||||
@@ -2065,9 +2060,8 @@ static int iwl_mld_set_key_add(struct iwl_mld *mld,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION &&
|
||||
(keyidx == 6 || keyidx == 7))
|
||||
rcu_assign_pointer(mld_vif->bigtks[keyidx - 6], key);
|
||||
if (keyidx == 6 || keyidx == 7)
|
||||
iwl_mld_track_bigtk(mld, vif, key, true);
|
||||
|
||||
/* After exiting from RFKILL, hostapd configures GTK/ITGK before the
|
||||
* AP is started, but those keys can't be sent to the FW before the
|
||||
@@ -2116,9 +2110,8 @@ static void iwl_mld_set_key_remove(struct iwl_mld *mld,
|
||||
sta ? iwl_mld_sta_from_mac80211(sta) : NULL;
|
||||
int keyidx = key->keyidx;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION &&
|
||||
(keyidx == 6 || keyidx == 7))
|
||||
RCU_INIT_POINTER(mld_vif->bigtks[keyidx - 6], NULL);
|
||||
if (keyidx == 6 || keyidx == 7)
|
||||
iwl_mld_track_bigtk(mld, vif, key, false);
|
||||
|
||||
if (mld_sta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
|
||||
(key->cipher == WLAN_CIPHER_SUITE_CCMP ||
|
||||
|
||||
@@ -147,6 +147,7 @@ iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans,
|
||||
*/
|
||||
static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
|
||||
HCMD_NAME(UCODE_ALIVE_NTFY),
|
||||
HCMD_NAME(REPLY_ERROR),
|
||||
HCMD_NAME(INIT_COMPLETE_NOTIF),
|
||||
HCMD_NAME(PHY_CONTEXT_CMD),
|
||||
HCMD_NAME(SCAN_CFG_CMD),
|
||||
@@ -158,12 +159,14 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
|
||||
HCMD_NAME(LEDS_CMD),
|
||||
HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION),
|
||||
HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION),
|
||||
HCMD_NAME(PHY_CONFIGURATION_CMD),
|
||||
HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
|
||||
HCMD_NAME(POWER_TABLE_CMD),
|
||||
HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
|
||||
HCMD_NAME(BEACON_NOTIFICATION),
|
||||
HCMD_NAME(BEACON_TEMPLATE_CMD),
|
||||
HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
|
||||
HCMD_NAME(BT_CONFIG),
|
||||
HCMD_NAME(REDUCE_TX_POWER_CMD),
|
||||
HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
|
||||
HCMD_NAME(MAC_PM_POWER_TABLE),
|
||||
@@ -251,6 +254,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
|
||||
HCMD_NAME(TLC_MNG_CONFIG_CMD),
|
||||
HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),
|
||||
HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
|
||||
HCMD_NAME(SEC_KEY_CMD),
|
||||
HCMD_NAME(ESR_MODE_NOTIF),
|
||||
HCMD_NAME(MONITOR_NOTIF),
|
||||
HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
|
||||
|
||||
@@ -357,38 +357,26 @@ iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
enum iwl_mvm_fw_esr_recommendation action;
|
||||
const struct iwl_esr_mode_notif *notif = NULL;
|
||||
|
||||
if (iwl_fw_lookup_notif_ver(mld_vif->mld->fw, DATA_PATH_GROUP,
|
||||
ESR_MODE_NOTIF, 0) > 1) {
|
||||
notif = (void *)data;
|
||||
action = le32_to_cpu(notif->action);
|
||||
} else {
|
||||
const struct iwl_esr_mode_notif_v1 *notif_v1 = (void *)data;
|
||||
|
||||
action = le32_to_cpu(notif_v1->action);
|
||||
}
|
||||
const struct iwl_esr_mode_notif *notif = (void *)data;
|
||||
enum iwl_mvm_fw_esr_recommendation action = le32_to_cpu(notif->action);
|
||||
|
||||
if (!iwl_mld_vif_has_emlsr_cap(vif))
|
||||
return;
|
||||
|
||||
switch (action) {
|
||||
case ESR_RECOMMEND_LEAVE:
|
||||
if (notif)
|
||||
IWL_DEBUG_INFO(mld_vif->mld,
|
||||
"FW recommend leave reason = 0x%x\n",
|
||||
le32_to_cpu(notif->leave_reason_mask));
|
||||
IWL_DEBUG_INFO(mld_vif->mld,
|
||||
"FW recommend leave reason = 0x%x\n",
|
||||
le32_to_cpu(notif->leave_reason_mask));
|
||||
|
||||
iwl_mld_exit_emlsr(mld_vif->mld, vif,
|
||||
IWL_MLD_EMLSR_EXIT_FW_REQUEST,
|
||||
iwl_mld_get_primary_link(vif));
|
||||
break;
|
||||
case ESR_FORCE_LEAVE:
|
||||
if (notif)
|
||||
IWL_DEBUG_INFO(mld_vif->mld,
|
||||
"FW force leave reason = 0x%x\n",
|
||||
le32_to_cpu(notif->leave_reason_mask));
|
||||
IWL_DEBUG_INFO(mld_vif->mld,
|
||||
"FW force leave reason = 0x%x\n",
|
||||
le32_to_cpu(notif->leave_reason_mask));
|
||||
fallthrough;
|
||||
case ESR_RECOMMEND_ENTER:
|
||||
default:
|
||||
@@ -735,12 +723,6 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
|
||||
u16 max_grade = 0;
|
||||
unsigned long link_id;
|
||||
|
||||
/*
|
||||
* TODO: don't select links that weren't discovered in the last scan
|
||||
* This requires mac80211 (or cfg80211) changes to forward/track when
|
||||
* a BSS was last updated. cfg80211 already tracks this information but
|
||||
* it is not exposed within the kernel.
|
||||
*/
|
||||
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
|
||||
struct ieee80211_bss_conf *link_conf =
|
||||
link_conf_dereference_protected(vif, link_id);
|
||||
|
||||
@@ -333,7 +333,6 @@ CMD_VERSIONS(bt_coex_notif,
|
||||
CMD_VERSIONS(beacon_notification,
|
||||
CMD_VER_ENTRY(6, iwl_extended_beacon_notif))
|
||||
CMD_VERSIONS(emlsr_mode_notif,
|
||||
CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1)
|
||||
CMD_VER_ENTRY(2, iwl_esr_mode_notif))
|
||||
CMD_VERSIONS(emlsr_trans_fail_notif,
|
||||
CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))
|
||||
|
||||
@@ -163,18 +163,32 @@ int iwl_mld_init_sgom(struct iwl_mld *mld)
|
||||
|
||||
static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
|
||||
{
|
||||
union iwl_ppag_table_cmd cmd = {};
|
||||
int ret, len;
|
||||
struct iwl_fw_runtime *fwrt = &mld->fwrt;
|
||||
union iwl_ppag_table_cmd cmd = {
|
||||
.v7.ppag_config_info.table_source = fwrt->ppag_bios_source,
|
||||
.v7.ppag_config_info.table_revision = fwrt->ppag_bios_rev,
|
||||
.v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len);
|
||||
/* Not supporting PPAG table is a valid scenario */
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
"PPAG MODE bits going to be sent: %d\n",
|
||||
fwrt->ppag_flags);
|
||||
|
||||
for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) {
|
||||
for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) {
|
||||
cmd.v7.gain[chain][subband] =
|
||||
fwrt->ppag_chains[chain].subbands[subband];
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
"PPAG table: chain[%d] band[%d]: gain = %d\n",
|
||||
chain, subband, cmd.v7.gain[chain][subband]);
|
||||
}
|
||||
}
|
||||
|
||||
IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
|
||||
ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
|
||||
PER_PLATFORM_ANT_GAIN_CMD),
|
||||
&cmd, len);
|
||||
&cmd, sizeof(cmd.v7));
|
||||
if (ret < 0)
|
||||
IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
|
||||
ret);
|
||||
|
||||
@@ -82,9 +82,6 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct iwl_roc_req cmd = {
|
||||
.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
|
||||
};
|
||||
u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
|
||||
WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
|
||||
u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
|
||||
enum iwl_roc_activity activity;
|
||||
int ret = 0;
|
||||
|
||||
@@ -140,7 +137,7 @@ int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
memcpy(cmd.node_addr, vif->addr, ETH_ALEN);
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
|
||||
&cmd, cmd_len);
|
||||
&cmd);
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "Couldn't send the ROC_CMD\n");
|
||||
return ret;
|
||||
@@ -190,9 +187,6 @@ int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
|
||||
struct iwl_roc_req cmd = {
|
||||
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
|
||||
};
|
||||
u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
|
||||
WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
|
||||
u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
@@ -208,7 +202,7 @@ int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
|
||||
cmd.activity = cpu_to_le32(mld_vif->roc_activity);
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
|
||||
&cmd, cmd_len);
|
||||
&cmd);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n");
|
||||
|
||||
|
||||
@@ -1611,20 +1611,21 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
|
||||
return sta;
|
||||
}
|
||||
|
||||
#define KEY_IDX_LEN 2
|
||||
|
||||
static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
|
||||
struct ieee80211_hdr *hdr,
|
||||
struct ieee80211_rx_status *rx_status,
|
||||
u32 mpdu_status,
|
||||
u32 mpdu_len)
|
||||
{
|
||||
struct iwl_mld_link *link;
|
||||
struct wireless_dev *wdev;
|
||||
struct iwl_mld_sta *mld_sta;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
u8 keyidx;
|
||||
struct ieee80211_key_conf *key;
|
||||
const u8 *frame = (void *)hdr;
|
||||
const u8 *mmie;
|
||||
u8 link_id;
|
||||
|
||||
if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
|
||||
IWL_RX_MPDU_STATUS_SEC_NONE)
|
||||
@@ -1657,21 +1658,30 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
|
||||
return 0;
|
||||
}
|
||||
|
||||
link_id = rx_status->link_valid ? rx_status->link_id : 0;
|
||||
link = rcu_dereference(mld_vif->link[link_id]);
|
||||
if (WARN_ON_ONCE(!link))
|
||||
return -1;
|
||||
|
||||
/* both keys will have the same cipher and MIC length, use
|
||||
* whichever one is available
|
||||
*/
|
||||
key = rcu_dereference(mld_vif->bigtks[0]);
|
||||
key = rcu_dereference(link->bigtks[0]);
|
||||
if (!key) {
|
||||
key = rcu_dereference(mld_vif->bigtks[1]);
|
||||
key = rcu_dereference(link->bigtks[1]);
|
||||
if (!key)
|
||||
goto report;
|
||||
}
|
||||
|
||||
if (mpdu_len < key->icv_len + IEEE80211_GMAC_PN_LEN + KEY_IDX_LEN)
|
||||
/* get the real key ID */
|
||||
if (mpdu_len < key->icv_len)
|
||||
goto report;
|
||||
|
||||
/* get the real key ID */
|
||||
keyidx = frame[mpdu_len - key->icv_len - IEEE80211_GMAC_PN_LEN - KEY_IDX_LEN];
|
||||
mmie = frame + (mpdu_len - key->icv_len);
|
||||
|
||||
/* the position of the key_id in ieee80211_mmie_16 is the same */
|
||||
keyidx = le16_to_cpu(((const struct ieee80211_mmie *) mmie)->key_id);
|
||||
|
||||
/* and if that's the other key, look it up */
|
||||
if (keyidx != key->keyidx) {
|
||||
/* shouldn't happen since firmware checked, but be safe
|
||||
@@ -1680,7 +1690,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
|
||||
if (keyidx != 6 && keyidx != 7)
|
||||
return -1;
|
||||
|
||||
key = rcu_dereference(mld_vif->bigtks[keyidx - 6]);
|
||||
key = rcu_dereference(link->bigtks[keyidx - 6]);
|
||||
if (!key)
|
||||
goto report;
|
||||
}
|
||||
|
||||
@@ -504,9 +504,7 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
|
||||
*/
|
||||
if (scan_status == IWL_MLD_SCAN_REGULAR &&
|
||||
ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP &&
|
||||
gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE &&
|
||||
iwl_fw_lookup_notif_ver(mld->fw, SCAN_GROUP,
|
||||
CHANNEL_SURVEY_NOTIF, 0) >= 1)
|
||||
gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE)
|
||||
flags |= IWL_UMAC_SCAN_GEN_FLAGS2_COLLECT_CHANNEL_STATS;
|
||||
|
||||
return flags;
|
||||
|
||||
@@ -401,11 +401,9 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta)
|
||||
static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
|
||||
const struct iwl_sta_cfg_cmd *cmd)
|
||||
{
|
||||
u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
|
||||
int cmd_len = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) > 1 ?
|
||||
sizeof(*cmd) :
|
||||
sizeof(struct iwl_sta_cfg_cmd_v1);
|
||||
int ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, cmd_len);
|
||||
int ret = iwl_mld_send_cmd_pdu(mld,
|
||||
WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
|
||||
cmd);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
|
||||
return ret;
|
||||
|
||||
@@ -379,11 +379,14 @@ static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
|
||||
|
||||
/* TODO: task=statistics handle CQM notifications */
|
||||
|
||||
if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH)
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
|
||||
if (!iwl_mld_emlsr_active(vif))
|
||||
if (!iwl_mld_emlsr_active(vif)) {
|
||||
/* We're not in EMLSR and our signal is bad,
|
||||
* try to switch link maybe. EMLSR will be handled below.
|
||||
*/
|
||||
if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH)
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We are in EMLSR, check if we need to exit */
|
||||
exit_emlsr_thresh =
|
||||
|
||||
@@ -157,9 +157,9 @@ iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap,
|
||||
static void
|
||||
iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_vht_cap *vht_cap,
|
||||
struct iwl_tlc_config_cmd_v4 *cmd)
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
u16 supp;
|
||||
u32 supp;
|
||||
int i, highest_mcs;
|
||||
u8 max_nss = link_sta->rx_nss;
|
||||
struct ieee80211_vht_cap ieee_vht_cap = {
|
||||
@@ -182,7 +182,7 @@ iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
|
||||
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20)
|
||||
supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
|
||||
|
||||
cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp);
|
||||
cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le32(supp);
|
||||
/* Check if VHT extended NSS indicates that the bandwidth/NSS
|
||||
* configuration is supported - only for MCS 0 since we already
|
||||
* decoded the MCS bits anyway ourselves.
|
||||
@@ -196,7 +196,7 @@ iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta,
|
||||
}
|
||||
}
|
||||
|
||||
static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
|
||||
static u32 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
|
||||
{
|
||||
switch (mcs) {
|
||||
case IEEE80211_HE_MCS_SUPPORT_0_7:
|
||||
@@ -216,7 +216,7 @@ static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs)
|
||||
static void
|
||||
iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
struct iwl_tlc_config_cmd_v4 *cmd)
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
|
||||
u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
|
||||
@@ -245,7 +245,7 @@ iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
|
||||
if (_mcs_80 > _tx_mcs_80)
|
||||
_mcs_80 = _tx_mcs_80;
|
||||
cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] =
|
||||
cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80));
|
||||
cpu_to_le32(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80));
|
||||
|
||||
/* If one side doesn't support - mark both as not supporting */
|
||||
if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
||||
@@ -256,19 +256,19 @@ iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta,
|
||||
if (_mcs_160 > _tx_mcs_160)
|
||||
_mcs_160 = _tx_mcs_160;
|
||||
cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
|
||||
cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160));
|
||||
cpu_to_le32(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160));
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3],
|
||||
static void iwl_mld_set_eht_mcs(__le32 ht_rates[][3],
|
||||
enum IWL_TLC_MCS_PER_BW bw,
|
||||
u8 max_nss, u16 mcs_msk)
|
||||
u8 max_nss, u32 mcs_msk)
|
||||
{
|
||||
if (max_nss >= 2)
|
||||
ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk);
|
||||
ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le32(mcs_msk);
|
||||
|
||||
if (max_nss >= 1)
|
||||
ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk);
|
||||
ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le32(mcs_msk);
|
||||
}
|
||||
|
||||
static const
|
||||
@@ -307,7 +307,7 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_link_sta *link_sta,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap,
|
||||
struct iwl_tlc_config_cmd_v4 *cmd)
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
/* peer RX mcs capa */
|
||||
const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
|
||||
@@ -405,7 +405,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct ieee80211_supported_band *sband,
|
||||
const struct ieee80211_sta_he_cap *own_he_cap,
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap,
|
||||
struct iwl_tlc_config_cmd_v4 *cmd)
|
||||
struct iwl_tlc_config_cmd *cmd)
|
||||
{
|
||||
int i;
|
||||
u16 non_ht_rates = 0;
|
||||
@@ -435,7 +435,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
} else if (ht_cap->ht_supported) {
|
||||
cmd->mode = IWL_TLC_MNG_MODE_HT;
|
||||
cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
|
||||
cpu_to_le16(ht_cap->mcs.rx_mask[0]);
|
||||
cpu_to_le32(ht_cap->mcs.rx_mask[0]);
|
||||
|
||||
/* the station support only a single receive chain */
|
||||
if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
|
||||
@@ -443,10 +443,30 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
0;
|
||||
else
|
||||
cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
|
||||
cpu_to_le16(ht_cap->mcs.rx_mask[1]);
|
||||
cpu_to_le32(ht_cap->mcs.rx_mask[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_mld_convert_tlc_cmd_to_v4(struct iwl_tlc_config_cmd *cmd,
|
||||
struct iwl_tlc_config_cmd_v4 *cmd_v4)
|
||||
{
|
||||
/* Copy everything until ht_rates */
|
||||
memcpy(cmd_v4, cmd, offsetof(struct iwl_tlc_config_cmd, ht_rates));
|
||||
|
||||
/* Convert ht_rates from __le32 to __le16 */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates) != ARRAY_SIZE(cmd->ht_rates));
|
||||
BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates[0]) != ARRAY_SIZE(cmd->ht_rates[0]));
|
||||
|
||||
for (int nss = 0; nss < ARRAY_SIZE(cmd->ht_rates); nss++)
|
||||
for (int bw = 0; bw < ARRAY_SIZE(cmd->ht_rates[nss]); bw++)
|
||||
cmd_v4->ht_rates[nss][bw] =
|
||||
cpu_to_le16(le32_to_cpu(cmd->ht_rates[nss][bw]));
|
||||
|
||||
/* Copy the rest */
|
||||
cmd_v4->max_mpdu_len = cmd->max_mpdu_len;
|
||||
cmd_v4->max_tx_op = cmd->max_tx_op;
|
||||
}
|
||||
|
||||
static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_link_sta *link_sta,
|
||||
@@ -458,7 +478,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
|
||||
ieee80211_get_he_iftype_cap_vif(sband, vif);
|
||||
const struct ieee80211_sta_eht_cap *own_eht_cap =
|
||||
ieee80211_get_eht_iftype_cap_vif(sband, vif);
|
||||
struct iwl_tlc_config_cmd_v4 cmd = {
|
||||
struct iwl_tlc_config_cmd cmd = {
|
||||
/* For AP mode, use 20 MHz until the STA is authorized */
|
||||
.max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
|
||||
iwl_mld_fw_bw_from_sta_bw(link_sta) :
|
||||
@@ -470,6 +490,11 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
|
||||
.max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
|
||||
};
|
||||
int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
|
||||
u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD);
|
||||
u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
|
||||
struct iwl_tlc_config_cmd_v4 cmd_v4;
|
||||
void *cmd_ptr;
|
||||
u8 cmd_size;
|
||||
int ret;
|
||||
|
||||
if (fw_sta_id < 0)
|
||||
@@ -481,14 +506,26 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
|
||||
own_he_cap, own_eht_cap,
|
||||
&cmd);
|
||||
|
||||
if (cmd_ver == 5) {
|
||||
cmd_ptr = &cmd;
|
||||
cmd_size = sizeof(cmd);
|
||||
} else if (cmd_ver == 4) {
|
||||
iwl_mld_convert_tlc_cmd_to_v4(&cmd, &cmd_v4);
|
||||
cmd_ptr = &cmd_v4;
|
||||
cmd_size = sizeof(cmd_v4);
|
||||
} else {
|
||||
IWL_ERR(mld, "Unsupported TLC config cmd version %d\n",
|
||||
cmd_ver);
|
||||
return;
|
||||
}
|
||||
|
||||
IWL_DEBUG_RATE(mld,
|
||||
"TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n",
|
||||
cmd.sta_id, cmd.max_ch_width, cmd.mode);
|
||||
|
||||
/* Send async since this can be called within a RCU-read section */
|
||||
ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP,
|
||||
TLC_MNG_CONFIG_CMD),
|
||||
CMD_ASYNC, &cmd);
|
||||
ret = iwl_mld_send_cmd_with_flags_pdu(mld, cmd_id, CMD_ASYNC, cmd_ptr,
|
||||
cmd_size);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret);
|
||||
}
|
||||
|
||||
@@ -253,93 +253,6 @@ static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,
|
||||
swap(data->primary, data->secondary);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function receives the LB link id and checks if eSR should be
|
||||
* enabled or disabled (due to BT coex)
|
||||
*/
|
||||
bool
|
||||
iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
s32 link_rssi,
|
||||
bool primary)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
bool have_wifi_loss_rate =
|
||||
iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
|
||||
BT_PROFILE_NOTIFICATION, 0) > 4 ||
|
||||
iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP,
|
||||
PROFILE_NOTIF, 0) >= 1;
|
||||
u8 wifi_loss_mid_high_rssi;
|
||||
u8 wifi_loss_low_rssi;
|
||||
u8 wifi_loss_rate;
|
||||
|
||||
if (iwl_fw_lookup_notif_ver(mvm->fw, BT_COEX_GROUP,
|
||||
PROFILE_NOTIF, 0) >= 1) {
|
||||
/* For now, we consider 2.4 GHz band / ANT_A only */
|
||||
wifi_loss_mid_high_rssi =
|
||||
mvm->last_bt_wifi_loss.wifi_loss_mid_high_rssi[PHY_BAND_24][0];
|
||||
wifi_loss_low_rssi =
|
||||
mvm->last_bt_wifi_loss.wifi_loss_low_rssi[PHY_BAND_24][0];
|
||||
} else {
|
||||
wifi_loss_mid_high_rssi = mvm->last_bt_notif.wifi_loss_mid_high_rssi;
|
||||
wifi_loss_low_rssi = mvm->last_bt_notif.wifi_loss_low_rssi;
|
||||
}
|
||||
|
||||
if (wifi_loss_low_rssi == BT_OFF)
|
||||
return true;
|
||||
|
||||
if (primary)
|
||||
return false;
|
||||
|
||||
/* The feature is not supported */
|
||||
if (!have_wifi_loss_rate)
|
||||
return true;
|
||||
|
||||
|
||||
/*
|
||||
* In case we don't know the RSSI - take the lower wifi loss,
|
||||
* so we will more likely enter eSR, and if RSSI is low -
|
||||
* we will get an update on this and exit eSR.
|
||||
*/
|
||||
if (!link_rssi)
|
||||
wifi_loss_rate = wifi_loss_mid_high_rssi;
|
||||
|
||||
else if (mvmvif->esr_active)
|
||||
/* RSSI needs to get really low to disable eSR... */
|
||||
wifi_loss_rate =
|
||||
link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ?
|
||||
wifi_loss_low_rssi :
|
||||
wifi_loss_mid_high_rssi;
|
||||
else
|
||||
/* ...And really high before we enable it back */
|
||||
wifi_loss_rate =
|
||||
link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ?
|
||||
wifi_loss_low_rssi :
|
||||
wifi_loss_mid_high_rssi;
|
||||
|
||||
return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH;
|
||||
}
|
||||
|
||||
void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
int link_id)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id];
|
||||
|
||||
if (!ieee80211_vif_is_mld(vif) ||
|
||||
!iwl_mvm_vif_from_mac80211(vif)->authorized ||
|
||||
WARN_ON(!link))
|
||||
return;
|
||||
|
||||
if (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif,
|
||||
(s8)link->beacon_stats.avg_signal,
|
||||
link_id == iwl_mvm_get_primary_link(vif)))
|
||||
/* In case we decided to exit eSR - stay with the primary */
|
||||
iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_COEX,
|
||||
iwl_mvm_get_primary_link(vif));
|
||||
}
|
||||
|
||||
static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_bt_iterator_data *data,
|
||||
@@ -385,8 +298,6 @@ static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm,
|
||||
return;
|
||||
}
|
||||
|
||||
iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);
|
||||
|
||||
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2))
|
||||
min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC;
|
||||
else
|
||||
@@ -525,32 +436,6 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
||||
iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id);
|
||||
}
|
||||
|
||||
/* must be called under rcu_read_lock */
|
||||
static void iwl_mvm_bt_coex_notif_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm *mvm = _data;
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
unsigned int link_id;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
for_each_vif_active_link(vif, link_conf, link_id) {
|
||||
struct ieee80211_chanctx_conf *chanctx_conf =
|
||||
rcu_dereference_check(link_conf->chanctx_conf,
|
||||
lockdep_is_held(&mvm->mutex));
|
||||
|
||||
if ((!chanctx_conf ||
|
||||
chanctx_conf->def.chan->band != NL80211_BAND_2GHZ))
|
||||
continue;
|
||||
|
||||
iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct iwl_bt_iterator_data data = {
|
||||
@@ -654,22 +539,6 @@ void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm *mvm,
|
||||
iwl_mvm_bt_coex_notif_handle(mvm);
|
||||
}
|
||||
|
||||
void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb)
|
||||
{
|
||||
const struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
mvm->last_bt_wifi_loss = *notif;
|
||||
|
||||
ieee80211_iterate_active_interfaces(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_bt_coex_notif_iterator,
|
||||
mvm);
|
||||
}
|
||||
|
||||
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum ieee80211_rssi_event_data rssi_event)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2013-2014, 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2013-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2015 Intel Deutschland GmbH
|
||||
*/
|
||||
#ifndef __MVM_CONSTANTS_H
|
||||
@@ -11,15 +11,7 @@
|
||||
#include "fw-api.h"
|
||||
|
||||
#define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM 20
|
||||
#define IWL_MVM_BT_COEX_DISABLE_ESR_THRESH 69
|
||||
#define IWL_MVM_BT_COEX_ENABLE_ESR_THRESH 63
|
||||
#define IWL_MVM_BT_COEX_WIFI_LOSS_THRESH 0
|
||||
#define IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC 30
|
||||
#define IWL_MVM_TPT_COUNT_WINDOW_SEC 5
|
||||
#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5
|
||||
#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH 15
|
||||
#define IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 11
|
||||
#define IWL_MVM_LOW_RSSI_MLO_SCAN_THRESH -72
|
||||
|
||||
#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
|
||||
#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
|
||||
@@ -129,14 +121,4 @@
|
||||
#define IWL_MVM_MIN_BEACON_INTERVAL_TU 16
|
||||
#define IWL_MVM_AUTO_EML_ENABLE true
|
||||
|
||||
#define IWL_MVM_HIGH_RSSI_THRESH_20MHZ -67
|
||||
#define IWL_MVM_LOW_RSSI_THRESH_20MHZ -71
|
||||
#define IWL_MVM_HIGH_RSSI_THRESH_40MHZ -64
|
||||
#define IWL_MVM_LOW_RSSI_THRESH_40MHZ -67
|
||||
#define IWL_MVM_HIGH_RSSI_THRESH_80MHZ -61
|
||||
#define IWL_MVM_LOW_RSSI_THRESH_80MHZ -74
|
||||
#define IWL_MVM_HIGH_RSSI_THRESH_160MHZ -58
|
||||
#define IWL_MVM_LOW_RSSI_THRESH_160MHZ -61
|
||||
|
||||
#define IWL_MVM_ENTER_ESR_TPT_THRESH 400
|
||||
#endif /* __MVM_CONSTANTS_H */
|
||||
|
||||
@@ -928,6 +928,10 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
|
||||
if (ap_sta->mfp)
|
||||
wowlan_config_cmd->flags |= IS_11W_ASSOC;
|
||||
|
||||
if (rcu_access_pointer(mvmvif->bcn_prot.keys[0]) ||
|
||||
rcu_access_pointer(mvmvif->bcn_prot.keys[1]))
|
||||
wowlan_config_cmd->flags |= HAS_BEACON_PROTECTION;
|
||||
|
||||
if (iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_CONFIGURATION, 0) < 6) {
|
||||
/* Query the last used seqno and set it */
|
||||
int ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
|
||||
@@ -1238,15 +1242,14 @@ static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
|
||||
}
|
||||
|
||||
static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan,
|
||||
bool test)
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
struct iwl_mvm_vif *mvmvif = NULL;
|
||||
struct ieee80211_sta *ap_sta = NULL;
|
||||
struct iwl_mvm_vif_link_info *mvm_link;
|
||||
struct iwl_d3_manager_config d3_cfg_cmd_data = {
|
||||
struct iwl_d3_manager_config d3_cfg_cmd = {
|
||||
/*
|
||||
* Program the minimum sleep time to 10 seconds, as many
|
||||
* platforms have issues processing a wakeup signal while
|
||||
@@ -1254,23 +1257,14 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
|
||||
*/
|
||||
.min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
|
||||
};
|
||||
struct iwl_host_cmd d3_cfg_cmd = {
|
||||
.id = D3_CONFIG_CMD,
|
||||
.flags = CMD_WANT_SKB,
|
||||
.data[0] = &d3_cfg_cmd_data,
|
||||
.len[0] = sizeof(d3_cfg_cmd_data),
|
||||
};
|
||||
int ret;
|
||||
int len __maybe_unused;
|
||||
bool unified_image = fw_has_capa(&mvm->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
|
||||
|
||||
if (!wowlan) {
|
||||
/*
|
||||
* mac80211 shouldn't get here, but for D3 test
|
||||
* it doesn't warrant a warning
|
||||
*/
|
||||
WARN_ON(!test);
|
||||
/* mac80211 shouldn't get here */
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1278,10 +1272,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
|
||||
if (IS_ERR_OR_NULL(vif))
|
||||
return 1;
|
||||
|
||||
ret = iwl_mvm_block_esr_sync(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
|
||||
@@ -1351,7 +1341,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
if (mvm->d3_wake_sysassert)
|
||||
d3_cfg_cmd_data.wakeup_flags |=
|
||||
d3_cfg_cmd.wakeup_flags |=
|
||||
cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR);
|
||||
#endif
|
||||
|
||||
@@ -1364,21 +1354,14 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
|
||||
iwl_fw_dbg_stop_restart_recording(&mvm->fwrt, NULL, true);
|
||||
|
||||
/* must be last -- this switches firmware state */
|
||||
ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
|
||||
ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, 0, sizeof(d3_cfg_cmd),
|
||||
&d3_cfg_cmd);
|
||||
if (ret)
|
||||
goto out;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt);
|
||||
if (len >= sizeof(u32)) {
|
||||
mvm->d3_test_pme_ptr =
|
||||
le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
|
||||
}
|
||||
#endif
|
||||
iwl_free_resp(&d3_cfg_cmd);
|
||||
|
||||
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
|
||||
|
||||
ret = iwl_trans_d3_suspend(mvm->trans, test, !unified_image);
|
||||
ret = iwl_trans_d3_suspend(mvm->trans, !unified_image);
|
||||
out:
|
||||
if (ret < 0) {
|
||||
iwl_mvm_free_nd(mvm);
|
||||
@@ -1401,7 +1384,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
||||
iwl_fw_runtime_suspend(&mvm->fwrt);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return __iwl_mvm_suspend(hw, wowlan, false);
|
||||
return __iwl_mvm_suspend(hw, wowlan);
|
||||
}
|
||||
|
||||
struct iwl_multicast_key_data {
|
||||
@@ -1794,63 +1777,8 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
|
||||
struct iwl_mvm_d3_gtk_iter_data {
|
||||
struct iwl_mvm *mvm;
|
||||
struct iwl_wowlan_status_data *status;
|
||||
u32 gtk_cipher, igtk_cipher, bigtk_cipher;
|
||||
bool unhandled_cipher, igtk_support, bigtk_support;
|
||||
int num_keys;
|
||||
};
|
||||
|
||||
static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key,
|
||||
void *_data)
|
||||
{
|
||||
struct iwl_mvm_d3_gtk_iter_data *data = _data;
|
||||
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
|
||||
|
||||
if (link_id >= 0 && key->link_id >= 0 && link_id != key->link_id)
|
||||
return;
|
||||
|
||||
if (data->unhandled_cipher)
|
||||
return;
|
||||
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
/* ignore WEP completely, nothing to do */
|
||||
return;
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP_256:
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
/* we support these */
|
||||
data->gtk_cipher = key->cipher;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
|
||||
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
/* we support these */
|
||||
if (data->igtk_support &&
|
||||
(key->keyidx == 4 || key->keyidx == 5)) {
|
||||
data->igtk_cipher = key->cipher;
|
||||
} else if (data->bigtk_support &&
|
||||
(key->keyidx == 6 || key->keyidx == 7)) {
|
||||
data->bigtk_cipher = key->cipher;
|
||||
} else {
|
||||
data->unhandled_cipher = true;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* everything else - disconnect from AP */
|
||||
data->unhandled_cipher = true;
|
||||
return;
|
||||
}
|
||||
|
||||
data->num_keys++;
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mvm_d3_set_igtk_bigtk_ipn(const struct iwl_multicast_key_data *key,
|
||||
struct ieee80211_key_seq *seq, u32 cipher)
|
||||
@@ -1896,9 +1824,6 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
|
||||
if (link_id >= 0 && key->link_id >= 0 && link_id != key->link_id)
|
||||
return;
|
||||
|
||||
if (data->unhandled_cipher)
|
||||
return;
|
||||
|
||||
switch (key->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
case WLAN_CIPHER_SUITE_WEP104:
|
||||
@@ -1947,52 +1872,24 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
|
||||
|
||||
static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mvm *mvm, u32 gtk_cipher)
|
||||
struct iwl_mvm *mvm)
|
||||
{
|
||||
int i, j;
|
||||
struct ieee80211_key_conf *key;
|
||||
DEFINE_RAW_FLEX(struct ieee80211_key_conf, conf, key,
|
||||
WOWLAN_KEY_MAX_SIZE);
|
||||
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
|
||||
u8 key_data[WOWLAN_KEY_MAX_SIZE];
|
||||
|
||||
conf->cipher = gtk_cipher;
|
||||
|
||||
BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
|
||||
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < WLAN_KEY_LEN_CCMP);
|
||||
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < WLAN_KEY_LEN_GCMP_256);
|
||||
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < WLAN_KEY_LEN_TKIP);
|
||||
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < sizeof(status->gtk[0].key));
|
||||
|
||||
switch (gtk_cipher) {
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP:
|
||||
conf->keylen = WLAN_KEY_LEN_CCMP;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_GCMP_256:
|
||||
conf->keylen = WLAN_KEY_LEN_GCMP_256;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
conf->keylen = WLAN_KEY_LEN_TKIP;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status->gtk); i++) {
|
||||
if (!status->gtk[i].len)
|
||||
continue;
|
||||
|
||||
conf->keyidx = status->gtk[i].id;
|
||||
IWL_DEBUG_WOWLAN(mvm,
|
||||
"Received from FW GTK cipher %d, key index %d\n",
|
||||
conf->cipher, conf->keyidx);
|
||||
memcpy(conf->key, status->gtk[i].key,
|
||||
sizeof(status->gtk[i].key));
|
||||
memcpy(key_data, status->gtk[i].key, sizeof(status->gtk[i].key));
|
||||
"Received from FW GTK: key index %d\n",
|
||||
status->gtk[i].id);
|
||||
|
||||
key = ieee80211_gtk_rekey_add(vif, status->gtk[i].id, key_data,
|
||||
sizeof(key_data), link_id);
|
||||
key = ieee80211_gtk_rekey_add(vif, status->gtk[i].id,
|
||||
status->gtk[i].key,
|
||||
sizeof(status->gtk[i].key),
|
||||
link_id);
|
||||
if (IS_ERR(key)) {
|
||||
/* FW may send also the old keys */
|
||||
if (PTR_ERR(key) == -EALREADY)
|
||||
@@ -2015,53 +1912,26 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
|
||||
|
||||
static bool
|
||||
iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status,
|
||||
struct ieee80211_vif *vif, u32 cipher,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_multicast_key_data *key_data)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
DEFINE_RAW_FLEX(struct ieee80211_key_conf, conf, key,
|
||||
WOWLAN_KEY_MAX_SIZE);
|
||||
struct ieee80211_key_conf *key_config;
|
||||
struct ieee80211_key_seq seq;
|
||||
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
|
||||
u8 key[WOWLAN_KEY_MAX_SIZE];
|
||||
s8 keyidx = key_data->id;
|
||||
|
||||
conf->cipher = cipher;
|
||||
conf->keyidx = keyidx;
|
||||
|
||||
if (!key_data->len)
|
||||
return true;
|
||||
|
||||
iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, conf->cipher);
|
||||
|
||||
switch (cipher) {
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
|
||||
conf->keylen = WLAN_KEY_LEN_BIP_GMAC_128;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
|
||||
conf->keylen = WLAN_KEY_LEN_BIP_GMAC_256;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
conf->keylen = WLAN_KEY_LEN_AES_CMAC;
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
|
||||
conf->keylen = WLAN_KEY_LEN_BIP_CMAC_256;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < sizeof(key_data->key));
|
||||
memcpy(conf->key, key_data->key, conf->keylen);
|
||||
|
||||
memcpy(key, key_data->key, sizeof(key_data->key));
|
||||
|
||||
key_config = ieee80211_gtk_rekey_add(vif, keyidx, key, sizeof(key),
|
||||
link_id);
|
||||
key_config = ieee80211_gtk_rekey_add(vif, keyidx, key_data->key,
|
||||
sizeof(key_data->key), link_id);
|
||||
if (IS_ERR(key_config)) {
|
||||
/* FW may send also the old keys */
|
||||
return PTR_ERR(key_config) == -EALREADY;
|
||||
}
|
||||
|
||||
iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, key_config->cipher);
|
||||
ieee80211_set_key_rx_seq(key_config, 0, &seq);
|
||||
|
||||
if (keyidx == 4 || keyidx == 5) {
|
||||
@@ -2115,27 +1985,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
|
||||
|
||||
if (!status || !vif->bss_conf.bssid)
|
||||
return false;
|
||||
|
||||
if (iwl_mvm_lookup_wowlan_status_ver(mvm) > 6 ||
|
||||
iwl_fw_lookup_notif_ver(mvm->fw, PROT_OFFLOAD_GROUP,
|
||||
WOWLAN_INFO_NOTIFICATION,
|
||||
0))
|
||||
gtkdata.igtk_support = true;
|
||||
|
||||
if (iwl_fw_lookup_notif_ver(mvm->fw, PROT_OFFLOAD_GROUP,
|
||||
WOWLAN_INFO_NOTIFICATION,
|
||||
0) >= 3)
|
||||
gtkdata.bigtk_support = true;
|
||||
|
||||
/* find last GTK that we used initially, if any */
|
||||
ieee80211_iter_keys(mvm->hw, vif,
|
||||
iwl_mvm_d3_find_last_keys, >kdata);
|
||||
/* not trying to keep connections with MFP/unhandled ciphers */
|
||||
if (gtkdata.unhandled_cipher)
|
||||
return false;
|
||||
if (!gtkdata.num_keys)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* invalidate all other GTKs that might still exist and update
|
||||
* the one that we used
|
||||
@@ -2149,17 +1998,15 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
|
||||
IWL_DEBUG_WOWLAN(mvm, "num of GTK rekeying %d\n",
|
||||
status->num_of_gtk_rekeys);
|
||||
|
||||
if (!iwl_mvm_gtk_rekey(status, vif, mvm, gtkdata.gtk_cipher))
|
||||
if (!iwl_mvm_gtk_rekey(status, vif, mvm))
|
||||
return false;
|
||||
|
||||
if (!iwl_mvm_d3_igtk_bigtk_rekey_add(status, vif,
|
||||
gtkdata.igtk_cipher,
|
||||
&status->igtk))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status->bigtk); i++) {
|
||||
if (!iwl_mvm_d3_igtk_bigtk_rekey_add(status, vif,
|
||||
gtkdata.bigtk_cipher,
|
||||
&status->bigtk[i]))
|
||||
return false;
|
||||
}
|
||||
@@ -2168,7 +2015,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
|
||||
(void *)&replay_ctr, GFP_KERNEL);
|
||||
}
|
||||
|
||||
out:
|
||||
if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP,
|
||||
WOWLAN_GET_STATUSES,
|
||||
IWL_FW_CMD_VER_UNKNOWN) < 10) {
|
||||
@@ -2236,7 +2082,7 @@ static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status,
|
||||
}
|
||||
|
||||
static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status,
|
||||
struct iwl_wowlan_igtk_status *data)
|
||||
struct iwl_wowlan_igtk_status_v1 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -2260,7 +2106,7 @@ static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status,
|
||||
}
|
||||
|
||||
static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status,
|
||||
const struct iwl_wowlan_igtk_status *data)
|
||||
const struct iwl_wowlan_igtk_status_v1 *data)
|
||||
{
|
||||
int data_idx, status_idx = 0;
|
||||
|
||||
@@ -2291,7 +2137,7 @@ static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status,
|
||||
}
|
||||
|
||||
static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_wowlan_info_notif *data,
|
||||
struct iwl_wowlan_info_notif_v5 *data,
|
||||
struct iwl_wowlan_status_data *status,
|
||||
u32 len)
|
||||
{
|
||||
@@ -2727,7 +2573,6 @@ enum iwl_d3_notif {
|
||||
/* manage d3 resume data */
|
||||
struct iwl_d3_data {
|
||||
struct iwl_wowlan_status_data *status;
|
||||
bool test;
|
||||
u32 d3_end_flags;
|
||||
u32 notif_expected; /* bitmap - see &enum iwl_d3_notif */
|
||||
u32 notif_received; /* bitmap - see &enum iwl_d3_notif */
|
||||
@@ -2919,18 +2764,11 @@ iwl_mvm_choose_query_wakeup_reasons(struct iwl_mvm *mvm,
|
||||
|
||||
if (mvm->net_detect) {
|
||||
iwl_mvm_query_netdetect_reasons(mvm, vif, d3_data);
|
||||
} else {
|
||||
bool keep = iwl_mvm_query_wakeup_reasons(mvm, vif,
|
||||
d3_data->status);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
if (keep)
|
||||
mvm->keep_vif = vif;
|
||||
#endif
|
||||
|
||||
return keep;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return iwl_mvm_query_wakeup_reasons(mvm, vif,
|
||||
d3_data->status);
|
||||
}
|
||||
|
||||
#define IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT (IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET | \
|
||||
@@ -3069,7 +2907,7 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,
|
||||
iwl_mvm_parse_wowlan_info_notif_v3(mvm, notif,
|
||||
d3_data->status, len);
|
||||
} else if (wowlan_info_ver == 5) {
|
||||
struct iwl_wowlan_info_notif *notif =
|
||||
struct iwl_wowlan_info_notif_v5 *notif =
|
||||
(void *)pkt->data;
|
||||
|
||||
iwl_mvm_parse_wowlan_info_notif(mvm, notif,
|
||||
@@ -3143,10 +2981,9 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,
|
||||
return d3_data->notif_received == d3_data->notif_expected;
|
||||
}
|
||||
|
||||
static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
|
||||
static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm)
|
||||
{
|
||||
int ret;
|
||||
enum iwl_d3_status d3_status;
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = D0I3_END_CMD,
|
||||
.flags = CMD_WANT_SKB,
|
||||
@@ -3154,15 +2991,10 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
|
||||
bool reset = fw_has_capa(&mvm->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
|
||||
|
||||
ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test, !reset);
|
||||
ret = iwl_trans_d3_resume(mvm->trans, !reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (d3_status != IWL_D3_STATUS_ALIVE) {
|
||||
IWL_INFO(mvm, "Device was reset during suspend\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* We should trigger resume flow using command only for 22000 family
|
||||
* AX210 and above don't need the command since they have
|
||||
@@ -3207,7 +3039,7 @@ static int iwl_mvm_d3_notif_wait(struct iwl_mvm *mvm,
|
||||
ARRAY_SIZE(d3_resume_notif),
|
||||
iwl_mvm_wait_d3_notif, d3_data);
|
||||
|
||||
ret = iwl_mvm_resume_firmware(mvm, d3_data->test);
|
||||
ret = iwl_mvm_resume_firmware(mvm);
|
||||
if (ret) {
|
||||
iwl_remove_notification(&mvm->notif_wait, &wait_d3_notif);
|
||||
return ret;
|
||||
@@ -3227,13 +3059,12 @@ static inline bool iwl_mvm_d3_resume_notif_based(struct iwl_mvm *mvm)
|
||||
D3_END_NOTIFICATION, 0);
|
||||
}
|
||||
|
||||
static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
|
||||
static int __iwl_mvm_resume(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
int ret = 1;
|
||||
struct iwl_mvm_nd_results results = {};
|
||||
struct iwl_d3_data d3_data = {
|
||||
.test = test,
|
||||
.notif_expected =
|
||||
IWL_D3_NOTIF_WOWLAN_INFO |
|
||||
IWL_D3_NOTIF_D3_END_NOTIF,
|
||||
@@ -3271,7 +3102,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
|
||||
|
||||
rt_status = iwl_mvm_check_rt_status(mvm, vif);
|
||||
if (rt_status != FW_ALIVE) {
|
||||
set_bit(STATUS_FW_ERROR, &mvm->trans->status);
|
||||
iwl_trans_notify_fw_error(mvm->trans);
|
||||
if (rt_status == FW_ERROR) {
|
||||
IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n");
|
||||
iwl_mvm_dump_nic_error_log(mvm);
|
||||
@@ -3298,13 +3129,11 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
ret = iwl_mvm_resume_firmware(mvm, test);
|
||||
ret = iwl_mvm_resume_firmware(mvm);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN);
|
||||
|
||||
/* when reset is required we can't send these following commands */
|
||||
if (d3_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)
|
||||
goto query_wakeup_reasons;
|
||||
@@ -3346,7 +3175,7 @@ out:
|
||||
kfree(d3_data.status);
|
||||
iwl_mvm_free_nd(mvm);
|
||||
|
||||
if (!d3_data.test && !mvm->net_detect)
|
||||
if (!mvm->net_detect)
|
||||
ieee80211_iterate_active_interfaces_mtx(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_d3_disconnect_iter,
|
||||
@@ -3385,7 +3214,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
|
||||
ret = __iwl_mvm_resume(mvm, false);
|
||||
ret = __iwl_mvm_resume(mvm);
|
||||
|
||||
iwl_mvm_resume_tcm(mvm);
|
||||
|
||||
@@ -3420,7 +3249,7 @@ void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
|
||||
IWL_ERR(mvm,
|
||||
"fast suspend: couldn't send D3_CONFIG_CMD %d\n", ret);
|
||||
|
||||
ret = iwl_trans_d3_suspend(mvm->trans, false, false);
|
||||
ret = iwl_trans_d3_suspend(mvm->trans, false);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "fast suspend: trans_d3_suspend failed %d\n", ret);
|
||||
}
|
||||
@@ -3443,7 +3272,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
|
||||
|
||||
rt_status = iwl_mvm_check_rt_status(mvm, NULL);
|
||||
if (rt_status != FW_ALIVE) {
|
||||
set_bit(STATUS_FW_ERROR, &mvm->trans->status);
|
||||
iwl_trans_notify_fw_error(mvm->trans);
|
||||
if (rt_status == FW_ERROR) {
|
||||
IWL_ERR(mvm,
|
||||
"iwl_mvm_check_rt_status failed, device is gone during suspend\n");
|
||||
@@ -3455,7 +3284,6 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
|
||||
&iwl_dump_desc_assert,
|
||||
false, 0);
|
||||
}
|
||||
mvm->trans->state = IWL_TRANS_NO_FW;
|
||||
ret = -ENODEV;
|
||||
|
||||
goto out;
|
||||
@@ -3473,125 +3301,3 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct iwl_mvm *mvm = inode->i_private;
|
||||
int err;
|
||||
|
||||
if (mvm->d3_test_active)
|
||||
return -EBUSY;
|
||||
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
iwl_mvm_pause_tcm(mvm, true);
|
||||
|
||||
iwl_fw_runtime_suspend(&mvm->fwrt);
|
||||
|
||||
/* start pseudo D3 */
|
||||
rtnl_lock();
|
||||
wiphy_lock(mvm->hw->wiphy);
|
||||
err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
|
||||
wiphy_unlock(mvm->hw->wiphy);
|
||||
rtnl_unlock();
|
||||
if (err > 0)
|
||||
err = -EINVAL;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mvm->d3_test_active = true;
|
||||
mvm->keep_vif = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm *mvm = file->private_data;
|
||||
unsigned long end = jiffies + 60 * HZ;
|
||||
u32 pme_asserted;
|
||||
|
||||
while (true) {
|
||||
/* read pme_ptr if available */
|
||||
if (mvm->d3_test_pme_ptr) {
|
||||
pme_asserted = iwl_trans_read_mem32(mvm->trans,
|
||||
mvm->d3_test_pme_ptr);
|
||||
if (pme_asserted)
|
||||
break;
|
||||
}
|
||||
|
||||
if (msleep_interruptible(100))
|
||||
break;
|
||||
|
||||
if (time_is_before_jiffies(end)) {
|
||||
IWL_ERR(mvm,
|
||||
"ending pseudo-D3 with timeout after ~60 seconds\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
/* skip the one we keep connection on */
|
||||
if (_data == vif)
|
||||
return;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION)
|
||||
ieee80211_connection_loss(vif);
|
||||
}
|
||||
|
||||
static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct iwl_mvm *mvm = inode->i_private;
|
||||
bool unified_image = fw_has_capa(&mvm->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
|
||||
|
||||
mvm->d3_test_active = false;
|
||||
|
||||
iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);
|
||||
|
||||
rtnl_lock();
|
||||
wiphy_lock(mvm->hw->wiphy);
|
||||
__iwl_mvm_resume(mvm, true);
|
||||
wiphy_unlock(mvm->hw->wiphy);
|
||||
rtnl_unlock();
|
||||
|
||||
iwl_mvm_resume_tcm(mvm);
|
||||
|
||||
iwl_fw_runtime_resume(&mvm->fwrt);
|
||||
|
||||
iwl_abort_notification_waits(&mvm->notif_wait);
|
||||
if (!unified_image) {
|
||||
int remaining_time = 10;
|
||||
|
||||
ieee80211_restart_hw(mvm->hw);
|
||||
|
||||
/* wait for restart and disconnect all interfaces */
|
||||
while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
|
||||
remaining_time > 0) {
|
||||
remaining_time--;
|
||||
msleep(1000);
|
||||
}
|
||||
|
||||
if (remaining_time == 0)
|
||||
IWL_ERR(mvm, "Timed out waiting for HW restart!\n");
|
||||
}
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations iwl_dbgfs_d3_test_ops = {
|
||||
.open = iwl_mvm_d3_test_open,
|
||||
.read = iwl_mvm_d3_test_read,
|
||||
.release = iwl_mvm_d3_test_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -762,96 +762,6 @@ static ssize_t iwl_dbgfs_max_tx_op_read(struct file *file,
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_int_mlo_scan_write(struct ieee80211_vif *vif,
|
||||
char *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
u32 action;
|
||||
int ret;
|
||||
|
||||
if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(buf, 0, &action))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
if (!action) {
|
||||
ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
|
||||
} else if (action == 1) {
|
||||
ret = iwl_mvm_int_mlo_scan(mvm, vif);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return ret ?: count;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_esr_disable_reason_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ieee80211_vif *vif = file->private_data;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
unsigned long esr_mask;
|
||||
char *buf;
|
||||
int bufsz, pos, i;
|
||||
ssize_t rv;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
esr_mask = mvmvif->esr_disable_reason;
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
bufsz = hweight32(esr_mask) * 32 + 40;
|
||||
buf = kmalloc(bufsz, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pos = scnprintf(buf, bufsz, "EMLSR state: '0x%lx'\nreasons:\n",
|
||||
esr_mask);
|
||||
for_each_set_bit(i, &esr_mask, BITS_PER_LONG)
|
||||
pos += scnprintf(buf + pos, bufsz - pos, " - %s\n",
|
||||
iwl_get_esr_state_string(BIT(i)));
|
||||
|
||||
rv = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
kfree(buf);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_esr_disable_reason_write(struct ieee80211_vif *vif,
|
||||
char *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
u32 reason;
|
||||
u8 block;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%u %hhu", &reason, &block);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (hweight16(reason) != 1 || !(reason & IWL_MVM_BLOCK_ESR_REASONS))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
if (block)
|
||||
iwl_mvm_block_esr(mvm, vif, reason,
|
||||
iwl_mvm_get_primary_link(vif));
|
||||
else
|
||||
iwl_mvm_unblock_esr(mvm, vif, reason);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
|
||||
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
|
||||
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
|
||||
@@ -884,8 +794,6 @@ 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);
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(max_tx_op, 10);
|
||||
MVM_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32);
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(esr_disable_reason, 32);
|
||||
|
||||
void iwl_mvm_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
@@ -916,8 +824,6 @@ void iwl_mvm_vif_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
MVM_DEBUGFS_ADD_FILE_VIF(max_tx_op, mvmvif->dbgfs_dir, 0600);
|
||||
debugfs_create_bool("ftm_unprotected", 0200, mvmvif->dbgfs_dir,
|
||||
&mvmvif->ftm_unprotected);
|
||||
MVM_DEBUGFS_ADD_FILE_VIF(int_mlo_scan, mvmvif->dbgfs_dir, 0200);
|
||||
MVM_DEBUGFS_ADD_FILE_VIF(esr_disable_reason, mvmvif->dbgfs_dir, 0600);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
|
||||
mvmvif == mvm->bf_allowed_vif)
|
||||
|
||||
@@ -1134,7 +1134,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
|
||||
|
||||
if (count == 6 && !strcmp(buf, "nolog\n")) {
|
||||
set_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status);
|
||||
iwl_trans_suppress_cmd_error_once(mvm->trans);
|
||||
mvm->trans->suppress_cmd_error_once = true;
|
||||
}
|
||||
|
||||
/* take the return value to make compiler happy - it will fail anyway */
|
||||
@@ -2159,7 +2159,6 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm)
|
||||
MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, 0400);
|
||||
debugfs_create_bool("d3_wake_sysassert", 0600, mvm->debugfs_dir,
|
||||
&mvm->d3_wake_sysassert);
|
||||
debugfs_create_u32("last_netdetect_scans", 0400, mvm->debugfs_dir,
|
||||
|
||||
@@ -837,7 +837,7 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
|
||||
.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
|
||||
};
|
||||
|
||||
if (!mvm->trans->ltr_enabled)
|
||||
if (!iwl_trans_is_ltr_enabled(mvm->trans))
|
||||
return 0;
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
|
||||
|
||||
@@ -5,50 +5,6 @@
|
||||
#include "mvm.h"
|
||||
#include "time-event.h"
|
||||
|
||||
#define HANDLE_ESR_REASONS(HOW) \
|
||||
HOW(BLOCKED_PREVENTION) \
|
||||
HOW(BLOCKED_WOWLAN) \
|
||||
HOW(BLOCKED_TPT) \
|
||||
HOW(BLOCKED_FW) \
|
||||
HOW(BLOCKED_NON_BSS) \
|
||||
HOW(BLOCKED_ROC) \
|
||||
HOW(BLOCKED_TMP_NON_BSS) \
|
||||
HOW(EXIT_MISSED_BEACON) \
|
||||
HOW(EXIT_LOW_RSSI) \
|
||||
HOW(EXIT_COEX) \
|
||||
HOW(EXIT_BANDWIDTH) \
|
||||
HOW(EXIT_CSA) \
|
||||
HOW(EXIT_LINK_USAGE)
|
||||
|
||||
static const char *const iwl_mvm_esr_states_names[] = {
|
||||
#define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x,
|
||||
HANDLE_ESR_REASONS(NAME_ENTRY)
|
||||
};
|
||||
|
||||
const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state)
|
||||
{
|
||||
int offs = ilog2(state);
|
||||
|
||||
if (offs >= ARRAY_SIZE(iwl_mvm_esr_states_names) ||
|
||||
!iwl_mvm_esr_states_names[offs])
|
||||
return "UNKNOWN";
|
||||
|
||||
return iwl_mvm_esr_states_names[offs];
|
||||
}
|
||||
|
||||
static void iwl_mvm_print_esr_state(struct iwl_mvm *mvm, u32 mask)
|
||||
{
|
||||
#define NAME_FMT(x) "%s"
|
||||
#define NAME_PR(x) (mask & IWL_MVM_ESR_##x) ? "[" #x "]" : "",
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"EMLSR state = " HANDLE_ESR_REASONS(NAME_FMT)
|
||||
" (0x%x)\n",
|
||||
HANDLE_ESR_REASONS(NAME_PR)
|
||||
mask);
|
||||
#undef NAME_FMT
|
||||
#undef NAME_PR
|
||||
}
|
||||
|
||||
static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
enum iwl_ctxt_action action)
|
||||
@@ -114,65 +70,6 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
|
||||
}
|
||||
|
||||
struct iwl_mvm_esr_iter_data {
|
||||
struct ieee80211_vif *vif;
|
||||
unsigned int link_id;
|
||||
bool lift_block;
|
||||
};
|
||||
|
||||
static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_esr_iter_data *data = _data;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
int link_id;
|
||||
|
||||
if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
for_each_mvm_vif_valid_link(mvmvif, link_id) {
|
||||
struct iwl_mvm_vif_link_info *link_info =
|
||||
mvmvif->link[link_id];
|
||||
if (vif == data->vif && link_id == data->link_id)
|
||||
continue;
|
||||
if (link_info->active)
|
||||
data->lift_block = false;
|
||||
}
|
||||
}
|
||||
|
||||
int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
unsigned int link_id, bool active)
|
||||
{
|
||||
/* An active link of a non-station vif blocks EMLSR. Upon activation
|
||||
* block EMLSR on the bss vif. Upon deactivation, check if this link
|
||||
* was the last non-station link active, and if so unblock the bss vif
|
||||
*/
|
||||
struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
|
||||
struct iwl_mvm_esr_iter_data data = {
|
||||
.vif = vif,
|
||||
.link_id = link_id,
|
||||
.lift_block = true,
|
||||
};
|
||||
|
||||
if (IS_ERR_OR_NULL(bss_vif))
|
||||
return 0;
|
||||
|
||||
if (active)
|
||||
return iwl_mvm_block_esr_sync(mvm, bss_vif,
|
||||
IWL_MVM_ESR_BLOCKED_NON_BSS);
|
||||
|
||||
ieee80211_iterate_active_interfaces(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_esr_vif_iterator, &data);
|
||||
if (data.lift_block) {
|
||||
mutex_lock(&mvm->mutex);
|
||||
iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 changes, bool active)
|
||||
@@ -388,452 +285,6 @@ int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct iwl_mvm_rssi_to_grade {
|
||||
s8 rssi[2];
|
||||
u16 grade;
|
||||
};
|
||||
|
||||
#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
|
||||
{ \
|
||||
.rssi = {_lb, _hb_uhb}, \
|
||||
.grade = _grade \
|
||||
}
|
||||
|
||||
/*
|
||||
* This array must be sorted by increasing RSSI for proper functionality.
|
||||
* The grades are actually estimated throughput, represented as fixed-point
|
||||
* with a scale factor of 1/10.
|
||||
*/
|
||||
static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map[] = {
|
||||
RSSI_TO_GRADE_LINE(-85, -89, 177),
|
||||
RSSI_TO_GRADE_LINE(-83, -86, 344),
|
||||
RSSI_TO_GRADE_LINE(-82, -85, 516),
|
||||
RSSI_TO_GRADE_LINE(-80, -83, 688),
|
||||
RSSI_TO_GRADE_LINE(-77, -79, 1032),
|
||||
RSSI_TO_GRADE_LINE(-73, -76, 1376),
|
||||
RSSI_TO_GRADE_LINE(-70, -74, 1548),
|
||||
RSSI_TO_GRADE_LINE(-69, -72, 1750),
|
||||
RSSI_TO_GRADE_LINE(-65, -68, 2064),
|
||||
RSSI_TO_GRADE_LINE(-61, -66, 2294),
|
||||
RSSI_TO_GRADE_LINE(-58, -61, 2580),
|
||||
RSSI_TO_GRADE_LINE(-55, -58, 2868),
|
||||
RSSI_TO_GRADE_LINE(-46, -55, 3098),
|
||||
RSSI_TO_GRADE_LINE(-43, -54, 3442)
|
||||
};
|
||||
|
||||
#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
|
||||
|
||||
#define DEFAULT_CHAN_LOAD_LB 30
|
||||
#define DEFAULT_CHAN_LOAD_HB 15
|
||||
#define DEFAULT_CHAN_LOAD_UHB 0
|
||||
|
||||
/* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
|
||||
#define SCALE_FACTOR 256
|
||||
|
||||
/* Convert a percentage from [0,100] to [0,255] */
|
||||
#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100)
|
||||
|
||||
static unsigned int
|
||||
iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
enum nl80211_chan_width chan_width =
|
||||
link_conf->chanreq.oper.width;
|
||||
int mhz = nl80211_chan_width_to_mhz(chan_width);
|
||||
unsigned int n_subchannels, n_punctured, puncturing_penalty;
|
||||
|
||||
if (WARN_ONCE(mhz < 20 || mhz > 320,
|
||||
"Invalid channel width : (%d)\n", mhz))
|
||||
return SCALE_FACTOR;
|
||||
|
||||
/* No puncturing, no penalty */
|
||||
if (mhz < 80)
|
||||
return SCALE_FACTOR;
|
||||
|
||||
/* total number of subchannels */
|
||||
n_subchannels = mhz / 20;
|
||||
/* how many of these are punctured */
|
||||
n_punctured = hweight16(link_conf->chanreq.oper.punctured);
|
||||
|
||||
puncturing_penalty = n_punctured * SCALE_FACTOR / n_subchannels;
|
||||
return SCALE_FACTOR - puncturing_penalty;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
struct ieee80211_vif *vif = link_conf->vif;
|
||||
struct iwl_mvm_vif_link_info *mvm_link =
|
||||
iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id];
|
||||
const struct element *bss_load_elem;
|
||||
const struct ieee80211_bss_load_elem *bss_load;
|
||||
enum nl80211_band band = link_conf->chanreq.oper.chan->band;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
unsigned int chan_load;
|
||||
u32 chan_load_by_us;
|
||||
|
||||
rcu_read_lock();
|
||||
if (ieee80211_vif_link_active(vif, link_conf->link_id))
|
||||
ies = rcu_dereference(link_conf->bss->beacon_ies);
|
||||
else
|
||||
ies = rcu_dereference(link_conf->bss->ies);
|
||||
|
||||
if (ies)
|
||||
bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
|
||||
ies->data, ies->len);
|
||||
else
|
||||
bss_load_elem = NULL;
|
||||
|
||||
/* If there isn't BSS Load element, take the defaults */
|
||||
if (!bss_load_elem ||
|
||||
bss_load_elem->datalen != sizeof(*bss_load)) {
|
||||
rcu_read_unlock();
|
||||
switch (band) {
|
||||
case NL80211_BAND_2GHZ:
|
||||
chan_load = DEFAULT_CHAN_LOAD_LB;
|
||||
break;
|
||||
case NL80211_BAND_5GHZ:
|
||||
chan_load = DEFAULT_CHAN_LOAD_HB;
|
||||
break;
|
||||
case NL80211_BAND_6GHZ:
|
||||
chan_load = DEFAULT_CHAN_LOAD_UHB;
|
||||
break;
|
||||
default:
|
||||
chan_load = 0;
|
||||
break;
|
||||
}
|
||||
/* The defaults are given in percentage */
|
||||
return NORMALIZE_PERCENT_TO_255(chan_load);
|
||||
}
|
||||
|
||||
bss_load = (const void *)bss_load_elem->data;
|
||||
/* Channel util is in range 0-255 */
|
||||
chan_load = bss_load->channel_util;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!mvm_link || !mvm_link->active)
|
||||
return chan_load;
|
||||
|
||||
if (WARN_ONCE(!mvm_link->phy_ctxt,
|
||||
"Active link (%u) without phy ctxt assigned!\n",
|
||||
link_conf->link_id))
|
||||
return chan_load;
|
||||
|
||||
/* channel load by us is given in percentage */
|
||||
chan_load_by_us =
|
||||
NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us);
|
||||
|
||||
/* Use only values that firmware sends that can possibly be valid */
|
||||
if (chan_load_by_us <= chan_load)
|
||||
chan_load -= chan_load_by_us;
|
||||
|
||||
return chan_load;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf);
|
||||
}
|
||||
|
||||
/* This function calculates the grade of a link. Returns 0 in error case */
|
||||
VISIBLE_IF_IWLWIFI_KUNIT
|
||||
unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
enum nl80211_band band;
|
||||
int i, rssi_idx;
|
||||
s32 link_rssi;
|
||||
unsigned int grade = MAX_GRADE;
|
||||
|
||||
if (WARN_ON_ONCE(!link_conf))
|
||||
return 0;
|
||||
|
||||
band = link_conf->chanreq.oper.chan->band;
|
||||
if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
|
||||
band != NL80211_BAND_5GHZ &&
|
||||
band != NL80211_BAND_6GHZ,
|
||||
"Invalid band (%u)\n", band))
|
||||
return 0;
|
||||
|
||||
link_rssi = MBM_TO_DBM(link_conf->bss->signal);
|
||||
/*
|
||||
* For 6 GHz the RSSI of the beacons is lower than
|
||||
* the RSSI of the data.
|
||||
*/
|
||||
if (band == NL80211_BAND_6GHZ)
|
||||
link_rssi += 4;
|
||||
|
||||
rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
|
||||
|
||||
/* No valid RSSI - take the lowest grade */
|
||||
if (!link_rssi)
|
||||
link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
|
||||
|
||||
/* Get grade based on RSSI */
|
||||
for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
|
||||
const struct iwl_mvm_rssi_to_grade *line =
|
||||
&rssi_to_grade_map[i];
|
||||
|
||||
if (link_rssi > line->rssi[rssi_idx])
|
||||
continue;
|
||||
grade = line->grade;
|
||||
break;
|
||||
}
|
||||
|
||||
/* apply the channel load and puncturing factors */
|
||||
grade = grade * iwl_mvm_get_chan_load_factor(link_conf) / SCALE_FACTOR;
|
||||
grade = grade * iwl_mvm_get_puncturing_factor(link_conf) / SCALE_FACTOR;
|
||||
return grade;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade);
|
||||
|
||||
static
|
||||
u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
|
||||
struct iwl_mvm_link_sel_data *data,
|
||||
unsigned long usable_links,
|
||||
u8 *best_link_idx)
|
||||
{
|
||||
u8 n_data = 0;
|
||||
u16 max_grade = 0;
|
||||
unsigned long link_id;
|
||||
|
||||
/* TODO: don't select links that weren't discovered in the last scan */
|
||||
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
|
||||
struct ieee80211_bss_conf *link_conf =
|
||||
link_conf_dereference_protected(vif, link_id);
|
||||
|
||||
if (WARN_ON_ONCE(!link_conf))
|
||||
continue;
|
||||
|
||||
data[n_data].link_id = link_id;
|
||||
data[n_data].chandef = &link_conf->chanreq.oper;
|
||||
data[n_data].signal = link_conf->bss->signal / 100;
|
||||
data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
|
||||
|
||||
if (data[n_data].grade > max_grade) {
|
||||
max_grade = data[n_data].grade;
|
||||
*best_link_idx = n_data;
|
||||
}
|
||||
n_data++;
|
||||
}
|
||||
|
||||
return n_data;
|
||||
}
|
||||
|
||||
struct iwl_mvm_bw_to_rssi_threshs {
|
||||
s8 low;
|
||||
s8 high;
|
||||
};
|
||||
|
||||
#define BW_TO_RSSI_THRESHOLDS(_bw) \
|
||||
[IWL_PHY_CHANNEL_MODE ## _bw] = { \
|
||||
.low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ, \
|
||||
.high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ \
|
||||
}
|
||||
|
||||
s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
bool low)
|
||||
{
|
||||
const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = {
|
||||
BW_TO_RSSI_THRESHOLDS(20),
|
||||
BW_TO_RSSI_THRESHOLDS(40),
|
||||
BW_TO_RSSI_THRESHOLDS(80),
|
||||
BW_TO_RSSI_THRESHOLDS(160)
|
||||
/* 320 MHz has the same thresholds as 20 MHz */
|
||||
};
|
||||
const struct iwl_mvm_bw_to_rssi_threshs *threshs;
|
||||
u8 chan_width = iwl_mvm_get_channel_width(chandef);
|
||||
|
||||
if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
|
||||
chandef->chan->band != NL80211_BAND_5GHZ &&
|
||||
chandef->chan->band != NL80211_BAND_6GHZ))
|
||||
return S8_MAX;
|
||||
|
||||
/* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
|
||||
if (chan_width == IWL_PHY_CHANNEL_MODE320)
|
||||
chan_width = IWL_PHY_CHANNEL_MODE20;
|
||||
|
||||
threshs = &bw_to_rssi_threshs_map[chan_width];
|
||||
|
||||
return low ? threshs->low : threshs->high;
|
||||
}
|
||||
|
||||
static u32
|
||||
iwl_mvm_esr_disallowed_with_link(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
const struct iwl_mvm_link_sel_data *link,
|
||||
bool primary)
|
||||
{
|
||||
struct wiphy *wiphy = mvm->hw->wiphy;
|
||||
struct ieee80211_bss_conf *conf;
|
||||
enum iwl_mvm_esr_state ret = 0;
|
||||
s8 thresh;
|
||||
|
||||
conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
|
||||
if (WARN_ON_ONCE(!conf))
|
||||
return false;
|
||||
|
||||
/* BT Coex effects eSR mode only if one of the links is on LB */
|
||||
if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
|
||||
(!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link->signal,
|
||||
primary)))
|
||||
ret |= IWL_MVM_ESR_EXIT_COEX;
|
||||
|
||||
thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef,
|
||||
false);
|
||||
|
||||
if (link->signal < thresh)
|
||||
ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
|
||||
|
||||
if (conf->csa_active)
|
||||
ret |= IWL_MVM_ESR_EXIT_CSA;
|
||||
|
||||
if (ret) {
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Link %d is not allowed for esr\n",
|
||||
link->link_id);
|
||||
iwl_mvm_print_esr_state(mvm, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
VISIBLE_IF_IWLWIFI_KUNIT
|
||||
bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
|
||||
const struct iwl_mvm_link_sel_data *a,
|
||||
const struct iwl_mvm_link_sel_data *b)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
enum iwl_mvm_esr_state ret = 0;
|
||||
|
||||
/* Per-link considerations */
|
||||
if (iwl_mvm_esr_disallowed_with_link(mvm, vif, a, true) ||
|
||||
iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false))
|
||||
return false;
|
||||
|
||||
if (a->chandef->chan->band == b->chandef->chan->band ||
|
||||
a->chandef->width != b->chandef->width)
|
||||
ret |= IWL_MVM_ESR_EXIT_BANDWIDTH;
|
||||
|
||||
if (ret) {
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Links %d and %d are not a valid pair for EMLSR\n",
|
||||
a->link_id, b->link_id);
|
||||
iwl_mvm_print_esr_state(mvm, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
|
||||
|
||||
/*
|
||||
* Returns the combined eSR grade of two given links.
|
||||
* Returns 0 if eSR is not allowed with these 2 links.
|
||||
*/
|
||||
static
|
||||
unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif,
|
||||
const struct iwl_mvm_link_sel_data *a,
|
||||
const struct iwl_mvm_link_sel_data *b,
|
||||
u8 *primary_id)
|
||||
{
|
||||
struct ieee80211_bss_conf *primary_conf;
|
||||
struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
|
||||
unsigned int primary_load;
|
||||
|
||||
lockdep_assert_wiphy(wiphy);
|
||||
|
||||
/* a is always primary, b is always secondary */
|
||||
if (b->grade > a->grade)
|
||||
swap(a, b);
|
||||
|
||||
*primary_id = a->link_id;
|
||||
|
||||
if (!iwl_mvm_mld_valid_link_pair(vif, a, b))
|
||||
return 0;
|
||||
|
||||
primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
|
||||
|
||||
if (WARN_ON_ONCE(!primary_conf))
|
||||
return 0;
|
||||
|
||||
primary_load = iwl_mvm_get_chan_load(primary_conf);
|
||||
|
||||
return a->grade +
|
||||
((b->grade * primary_load) / SCALE_FACTOR);
|
||||
}
|
||||
|
||||
void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
struct iwl_mvm_link_sel_data *best_link;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
u32 max_active_links = iwl_mvm_max_active_links(mvm, vif);
|
||||
u16 usable_links = ieee80211_vif_usable_links(vif);
|
||||
u8 best, primary_link, best_in_pair, n_data;
|
||||
u16 max_esr_grade = 0, new_active_links;
|
||||
|
||||
lockdep_assert_wiphy(mvm->hw->wiphy);
|
||||
|
||||
if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif))
|
||||
return;
|
||||
|
||||
if (!IWL_MVM_AUTO_EML_ENABLE)
|
||||
return;
|
||||
|
||||
/* The logic below is a simple version that doesn't suit more than 2
|
||||
* links
|
||||
*/
|
||||
WARN_ON_ONCE(max_active_links > 2);
|
||||
|
||||
n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links,
|
||||
&best);
|
||||
|
||||
if (WARN(!n_data, "Couldn't find a valid grade for any link!\n"))
|
||||
return;
|
||||
|
||||
best_link = &data[best];
|
||||
primary_link = best_link->link_id;
|
||||
new_active_links = BIT(best_link->link_id);
|
||||
|
||||
/* eSR is not supported/blocked, or only one usable link */
|
||||
if (max_active_links == 1 || !iwl_mvm_vif_has_esr_cap(mvm, vif) ||
|
||||
mvmvif->esr_disable_reason || n_data == 1)
|
||||
goto set_active;
|
||||
|
||||
for (u8 a = 0; a < n_data; a++)
|
||||
for (u8 b = a + 1; b < n_data; b++) {
|
||||
u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
|
||||
&data[b],
|
||||
&best_in_pair);
|
||||
|
||||
if (esr_grade <= max_esr_grade)
|
||||
continue;
|
||||
|
||||
max_esr_grade = esr_grade;
|
||||
primary_link = best_in_pair;
|
||||
new_active_links = BIT(data[a].link_id) |
|
||||
BIT(data[b].link_id);
|
||||
}
|
||||
|
||||
/* No valid pair was found, go with the best link */
|
||||
if (hweight16(new_active_links) <= 1)
|
||||
goto set_active;
|
||||
|
||||
/* For equal grade - prefer EMLSR */
|
||||
if (best_link->grade > max_esr_grade) {
|
||||
primary_link = best_link->link_id;
|
||||
new_active_links = BIT(best_link->link_id);
|
||||
}
|
||||
set_active:
|
||||
IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n",
|
||||
new_active_links, primary_link);
|
||||
ieee80211_set_active_links_async(vif, new_active_links);
|
||||
mvmvif->link_selection_res = new_active_links;
|
||||
mvmvif->link_selection_primary = primary_link;
|
||||
}
|
||||
|
||||
u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
@@ -856,266 +307,6 @@ u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
|
||||
return __ffs(vif->active_links);
|
||||
}
|
||||
|
||||
/*
|
||||
* For non-MLO/single link, this will return the deflink/single active link,
|
||||
* respectively
|
||||
*/
|
||||
u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
|
||||
{
|
||||
switch (hweight16(vif->active_links)) {
|
||||
case 0:
|
||||
return 0;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
fallthrough;
|
||||
case 1:
|
||||
return __ffs(vif->active_links);
|
||||
case 2:
|
||||
return __ffs(vif->active_links & ~BIT(link_id));
|
||||
}
|
||||
}
|
||||
|
||||
/* Reasons that can cause esr prevention */
|
||||
#define IWL_MVM_ESR_PREVENT_REASONS IWL_MVM_ESR_EXIT_MISSED_BEACON
|
||||
#define IWL_MVM_PREVENT_ESR_TIMEOUT (HZ * 400)
|
||||
#define IWL_MVM_ESR_PREVENT_SHORT (HZ * 300)
|
||||
#define IWL_MVM_ESR_PREVENT_LONG (HZ * 600)
|
||||
|
||||
static bool iwl_mvm_check_esr_prevention(struct iwl_mvm *mvm,
|
||||
struct iwl_mvm_vif *mvmvif,
|
||||
enum iwl_mvm_esr_state reason)
|
||||
{
|
||||
bool timeout_expired = time_after(jiffies,
|
||||
mvmvif->last_esr_exit.ts +
|
||||
IWL_MVM_PREVENT_ESR_TIMEOUT);
|
||||
unsigned long delay;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
/* Only handle reasons that can cause prevention */
|
||||
if (!(reason & IWL_MVM_ESR_PREVENT_REASONS))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Reset the counter if more than 400 seconds have passed between one
|
||||
* exit and the other, or if we exited due to a different reason.
|
||||
* Will also reset the counter after the long prevention is done.
|
||||
*/
|
||||
if (timeout_expired || mvmvif->last_esr_exit.reason != reason) {
|
||||
mvmvif->exit_same_reason_count = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
mvmvif->exit_same_reason_count++;
|
||||
if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
|
||||
mvmvif->exit_same_reason_count > 3))
|
||||
return false;
|
||||
|
||||
mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION;
|
||||
|
||||
/*
|
||||
* For the second exit, use a short prevention, and for the third one,
|
||||
* use a long prevention.
|
||||
*/
|
||||
delay = mvmvif->exit_same_reason_count == 2 ?
|
||||
IWL_MVM_ESR_PREVENT_SHORT :
|
||||
IWL_MVM_ESR_PREVENT_LONG;
|
||||
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
|
||||
delay / HZ, mvmvif->exit_same_reason_count,
|
||||
iwl_get_esr_state_string(reason), reason);
|
||||
|
||||
wiphy_delayed_work_queue(mvm->hw->wiphy,
|
||||
&mvmvif->prevent_esr_done_wk, delay);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ)
|
||||
|
||||
/* API to exit eSR mode */
|
||||
void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason,
|
||||
u8 link_to_keep)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
u16 new_active_links;
|
||||
bool prevented;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (!IWL_MVM_AUTO_EML_ENABLE)
|
||||
return;
|
||||
|
||||
/* Nothing to do */
|
||||
if (!mvmvif->esr_active)
|
||||
return;
|
||||
|
||||
if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mvmvif->authorized))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
|
||||
link_to_keep = __ffs(vif->active_links);
|
||||
|
||||
new_active_links = BIT(link_to_keep);
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
|
||||
iwl_get_esr_state_string(reason), reason,
|
||||
vif->active_links, new_active_links);
|
||||
|
||||
ieee80211_set_active_links_async(vif, new_active_links);
|
||||
|
||||
/* Prevent EMLSR if needed */
|
||||
prevented = iwl_mvm_check_esr_prevention(mvm, mvmvif, reason);
|
||||
|
||||
/* Remember why and when we exited EMLSR */
|
||||
mvmvif->last_esr_exit.ts = jiffies;
|
||||
mvmvif->last_esr_exit.reason = reason;
|
||||
|
||||
/*
|
||||
* If EMLSR is prevented now - don't try to get back to EMLSR.
|
||||
* If we exited due to a blocking event, we will try to get back to
|
||||
* EMLSR when the corresponding unblocking event will happen.
|
||||
*/
|
||||
if (prevented || reason & IWL_MVM_BLOCK_ESR_REASONS)
|
||||
return;
|
||||
|
||||
/* If EMLSR is not blocked - try enabling it again in 30 seconds */
|
||||
wiphy_delayed_work_queue(mvm->hw->wiphy,
|
||||
&mvmvif->mlo_int_scan_wk,
|
||||
round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME));
|
||||
}
|
||||
|
||||
void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason,
|
||||
u8 link_to_keep)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (!IWL_MVM_AUTO_EML_ENABLE)
|
||||
return;
|
||||
|
||||
/* This should be called only with disable reasons */
|
||||
if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
|
||||
return;
|
||||
|
||||
if (mvmvif->esr_disable_reason & reason)
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Blocking EMLSR mode. reason = %s (0x%x)\n",
|
||||
iwl_get_esr_state_string(reason), reason);
|
||||
|
||||
mvmvif->esr_disable_reason |= reason;
|
||||
|
||||
iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
|
||||
|
||||
iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
|
||||
}
|
||||
|
||||
int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason)
|
||||
{
|
||||
int primary_link = iwl_mvm_get_primary_link(vif);
|
||||
int ret;
|
||||
|
||||
if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif))
|
||||
return 0;
|
||||
|
||||
/* This should be called only with blocking reasons */
|
||||
if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
|
||||
return 0;
|
||||
|
||||
/* leave ESR immediately, not only async with iwl_mvm_block_esr() */
|
||||
ret = ieee80211_set_active_links(vif, BIT(primary_link));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
/* only additionally block for consistency and to avoid concurrency */
|
||||
iwl_mvm_block_esr(mvm, vif, reason, primary_link);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
bool need_new_sel = time_after(jiffies, mvmvif->last_esr_exit.ts +
|
||||
IWL_MVM_TRIGGER_LINK_SEL_TIME);
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized ||
|
||||
mvmvif->esr_active)
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n");
|
||||
|
||||
/* If we exited due to an EXIT reason, and the exit was in less than
|
||||
* 30 seconds, then a MLO scan was scheduled already.
|
||||
*/
|
||||
if (!need_new_sel &&
|
||||
!(mvmvif->last_esr_exit.reason & IWL_MVM_BLOCK_ESR_REASONS)) {
|
||||
IWL_DEBUG_INFO(mvm, "Wait for MLO scan\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If EMLSR was blocked for more than 30 seconds, or the last link
|
||||
* selection decided to not enter EMLSR, trigger a new scan.
|
||||
*/
|
||||
if (need_new_sel || hweight16(mvmvif->link_selection_res) < 2) {
|
||||
IWL_DEBUG_INFO(mvm, "Trigger MLO scan\n");
|
||||
wiphy_delayed_work_queue(mvm->hw->wiphy,
|
||||
&mvmvif->mlo_int_scan_wk, 0);
|
||||
/*
|
||||
* If EMLSR was blocked for less than 30 seconds, and the last link
|
||||
* selection decided to use EMLSR, activate EMLSR using the previous
|
||||
* link selection result.
|
||||
*/
|
||||
} else {
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Use the latest link selection result: 0x%x\n",
|
||||
mvmvif->link_selection_res);
|
||||
ieee80211_set_active_links_async(vif,
|
||||
mvmvif->link_selection_res);
|
||||
}
|
||||
}
|
||||
|
||||
void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (!IWL_MVM_AUTO_EML_ENABLE)
|
||||
return;
|
||||
|
||||
/* This should be called only with disable reasons */
|
||||
if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
|
||||
return;
|
||||
|
||||
/* No Change */
|
||||
if (!(mvmvif->esr_disable_reason & reason))
|
||||
return;
|
||||
|
||||
mvmvif->esr_disable_reason &= ~reason;
|
||||
|
||||
IWL_DEBUG_INFO(mvm,
|
||||
"Unblocking EMLSR mode. reason = %s (0x%x)\n",
|
||||
iwl_get_esr_state_string(reason), reason);
|
||||
iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
|
||||
|
||||
if (!mvmvif->esr_disable_reason)
|
||||
iwl_mvm_esr_unblocked(mvm, vif);
|
||||
}
|
||||
|
||||
void iwl_mvm_init_link(struct iwl_mvm_vif_link_info *link)
|
||||
{
|
||||
link->bcast_sta.sta_id = IWL_INVALID_STA;
|
||||
|
||||
@@ -1586,13 +1586,11 @@ iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm,
|
||||
u32 id = le32_to_cpu(mb->link_id);
|
||||
union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt };
|
||||
u32 mac_type;
|
||||
int link_id;
|
||||
u8 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
|
||||
MISSED_BEACONS_NOTIFICATION,
|
||||
0);
|
||||
u8 new_notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, MAC_CONF_GROUP,
|
||||
MISSED_BEACONS_NOTIF, 0);
|
||||
struct ieee80211_bss_conf *bss_conf;
|
||||
|
||||
/* If the firmware uses the new notification (from MAC_CONF_GROUP),
|
||||
* refer to that notification's version.
|
||||
@@ -1617,16 +1615,10 @@ iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm,
|
||||
if (!vif)
|
||||
return;
|
||||
|
||||
bss_conf = &vif->bss_conf;
|
||||
link_id = bss_conf->link_id;
|
||||
mac_type = iwl_mvm_get_mac_type(vif);
|
||||
|
||||
IWL_DEBUG_INFO(mvm, "missed beacon mac_type=%u,\n", mac_type);
|
||||
|
||||
mvm->trans->dbg.dump_file_name_ext_valid = true;
|
||||
snprintf(mvm->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME,
|
||||
"MacId_%d_MacType_%d", id, mac_type);
|
||||
|
||||
rx_missed_bcon = le32_to_cpu(mb->consec_missed_beacons);
|
||||
rx_missed_bcon_since_rx =
|
||||
le32_to_cpu(mb->consec_missed_beacons_since_last_rx);
|
||||
@@ -1644,41 +1636,11 @@ iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm,
|
||||
"missed_beacons:%d, missed_beacons_since_rx:%d\n",
|
||||
rx_missed_bcon, rx_missed_bcon_since_rx);
|
||||
}
|
||||
} else if (link_id >= 0 && hweight16(vif->active_links) > 1) {
|
||||
u32 bss_param_ch_cnt_link_id =
|
||||
bss_conf->bss_param_ch_cnt_link_id;
|
||||
u32 scnd_lnk_bcn_lost = 0;
|
||||
|
||||
if (notif_ver >= 5 &&
|
||||
!IWL_FW_CHECK(mvm,
|
||||
le32_to_cpu(mb->other_link_id) == IWL_MVM_FW_LINK_ID_INVALID,
|
||||
"No data for other link id but we are in EMLSR active_links: 0x%x\n",
|
||||
vif->active_links))
|
||||
scnd_lnk_bcn_lost =
|
||||
le32_to_cpu(mb->consec_missed_beacons_other_link);
|
||||
|
||||
/* Exit EMLSR if we lost more than
|
||||
* IWL_MVM_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links
|
||||
* OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH on any link.
|
||||
* OR more than IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
|
||||
* and the link's bss_param_ch_count has changed.
|
||||
*/
|
||||
if ((rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
|
||||
scnd_lnk_bcn_lost >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
|
||||
rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH ||
|
||||
(bss_param_ch_cnt_link_id != link_id &&
|
||||
rx_missed_bcon >= IWL_MVM_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED))
|
||||
iwl_mvm_exit_esr(mvm, vif,
|
||||
IWL_MVM_ESR_EXIT_MISSED_BEACON,
|
||||
iwl_mvm_get_primary_link(vif));
|
||||
} else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD) {
|
||||
if (!iwl_mvm_has_new_tx_api(mvm))
|
||||
ieee80211_beacon_loss(vif);
|
||||
else
|
||||
ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC);
|
||||
|
||||
/* try to switch links, no-op if we don't have MLO */
|
||||
iwl_mvm_int_mlo_scan(mvm, vif);
|
||||
}
|
||||
|
||||
iwl_dbg_tlv_time_point(&mvm->fwrt,
|
||||
|
||||
@@ -1425,12 +1425,6 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw, bool suspend)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
/* Stop internal MLO scan, if running */
|
||||
mutex_lock(&mvm->mutex);
|
||||
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
wiphy_work_cancel(mvm->hw->wiphy, &mvm->trig_link_selection_wk);
|
||||
wiphy_work_flush(mvm->hw->wiphy, &mvm->async_handlers_wiphy_wk);
|
||||
flush_work(&mvm->async_handlers_wk);
|
||||
flush_work(&mvm->add_stream_wk);
|
||||
@@ -1715,57 +1709,6 @@ static int iwl_mvm_alloc_bcast_mcast_sta(struct iwl_mvm *mvm,
|
||||
IWL_STA_MULTICAST);
|
||||
}
|
||||
|
||||
static void iwl_mvm_prevent_esr_done_wk(struct wiphy *wiphy,
|
||||
struct wiphy_work *wk)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif =
|
||||
container_of(wk, struct iwl_mvm_vif, prevent_esr_done_wk.work);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
struct ieee80211_vif *vif =
|
||||
container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
|
||||
|
||||
guard(mvm)(mvm);
|
||||
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_PREVENTION);
|
||||
}
|
||||
|
||||
static void iwl_mvm_mlo_int_scan_wk(struct wiphy *wiphy, struct wiphy_work *wk)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = container_of(wk, struct iwl_mvm_vif,
|
||||
mlo_int_scan_wk.work);
|
||||
struct ieee80211_vif *vif =
|
||||
container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
|
||||
|
||||
guard(mvm)(mvmvif->mvm);
|
||||
iwl_mvm_int_mlo_scan(mvmvif->mvm, vif);
|
||||
}
|
||||
|
||||
static void iwl_mvm_unblock_esr_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif =
|
||||
container_of(wk, struct iwl_mvm_vif, unblock_esr_tpt_wk);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
struct ieee80211_vif *vif =
|
||||
container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
|
||||
|
||||
guard(mvm)(mvm);
|
||||
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_TPT);
|
||||
}
|
||||
|
||||
static void iwl_mvm_unblock_esr_tmp_non_bss(struct wiphy *wiphy,
|
||||
struct wiphy_work *wk)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif =
|
||||
container_of(wk, struct iwl_mvm_vif,
|
||||
unblock_esr_tmp_non_bss_wk.work);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
struct ieee80211_vif *vif =
|
||||
container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_TMP_NON_BSS);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif)
|
||||
{
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
@@ -1777,18 +1720,6 @@ void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif)
|
||||
|
||||
INIT_DELAYED_WORK(&mvmvif->csa_work,
|
||||
iwl_mvm_channel_switch_disconnect_wk);
|
||||
|
||||
wiphy_delayed_work_init(&mvmvif->prevent_esr_done_wk,
|
||||
iwl_mvm_prevent_esr_done_wk);
|
||||
|
||||
wiphy_delayed_work_init(&mvmvif->mlo_int_scan_wk,
|
||||
iwl_mvm_mlo_int_scan_wk);
|
||||
|
||||
wiphy_work_init(&mvmvif->unblock_esr_tpt_wk,
|
||||
iwl_mvm_unblock_esr_tpt);
|
||||
|
||||
wiphy_delayed_work_init(&mvmvif->unblock_esr_tmp_non_bss_wk,
|
||||
iwl_mvm_unblock_esr_tmp_non_bss);
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
|
||||
@@ -1926,16 +1857,6 @@ void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
|
||||
flush_work(&mvm->roc_done_wk);
|
||||
}
|
||||
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->prevent_esr_done_wk);
|
||||
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->mlo_int_scan_wk);
|
||||
|
||||
wiphy_work_cancel(mvm->hw->wiphy, &mvmvif->unblock_esr_tpt_wk);
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->unblock_esr_tmp_non_bss_wk);
|
||||
|
||||
cancel_delayed_work_sync(&mvmvif->csa_work);
|
||||
}
|
||||
|
||||
@@ -4008,21 +3929,6 @@ 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);
|
||||
|
||||
memset(&mvmvif->last_esr_exit, 0,
|
||||
sizeof(mvmvif->last_esr_exit));
|
||||
|
||||
iwl_mvm_block_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_TPT, 0);
|
||||
|
||||
/* Block until FW notif will arrive */
|
||||
iwl_mvm_block_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_FW, 0);
|
||||
|
||||
/* when client is authorized (AP station marked as such),
|
||||
* try to enable the best link(s).
|
||||
*/
|
||||
if (vif->type == NL80211_IFTYPE_STATION &&
|
||||
!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
|
||||
iwl_mvm_select_links(mvm, vif);
|
||||
}
|
||||
|
||||
mvm_sta->authorized = true;
|
||||
@@ -4070,16 +3976,6 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
|
||||
|
||||
/* disable beacon filtering */
|
||||
iwl_mvm_disable_beacon_filter(mvm, vif);
|
||||
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->prevent_esr_done_wk);
|
||||
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->mlo_int_scan_wk);
|
||||
|
||||
wiphy_work_cancel(mvm->hw->wiphy, &mvmvif->unblock_esr_tpt_wk);
|
||||
wiphy_delayed_work_cancel(mvm->hw->wiphy,
|
||||
&mvmvif->unblock_esr_tmp_non_bss_wk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -4920,7 +4816,6 @@ 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 ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
|
||||
u32 lmac_id;
|
||||
int ret;
|
||||
|
||||
@@ -4933,13 +4828,6 @@ int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
*/
|
||||
flush_work(&mvm->roc_done_wk);
|
||||
|
||||
if (!IS_ERR_OR_NULL(bss_vif)) {
|
||||
ret = iwl_mvm_block_esr_sync(mvm, bss_vif,
|
||||
IWL_MVM_ESR_BLOCKED_ROC);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
guard(mvm)(mvm);
|
||||
|
||||
switch (vif->type) {
|
||||
@@ -5604,9 +5492,9 @@ static void iwl_mvm_csa_block_txqs(void *data, struct ieee80211_sta *sta)
|
||||
}
|
||||
|
||||
#define IWL_MAX_CSA_BLOCK_TX 1500
|
||||
int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw)
|
||||
static int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw)
|
||||
{
|
||||
struct ieee80211_vif *csa_vif;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
@@ -5724,9 +5612,9 @@ int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_pre_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw)
|
||||
int iwl_mvm_mac_pre_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
|
||||
@@ -340,20 +340,6 @@ static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
/* update EMLSR mode */
|
||||
if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
|
||||
int ret;
|
||||
|
||||
ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id,
|
||||
true);
|
||||
/*
|
||||
* Don't activate this link if failed to exit EMLSR in
|
||||
* the BSS interface
|
||||
*/
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
guard(mvm)(mvm);
|
||||
return __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);
|
||||
}
|
||||
@@ -472,10 +458,6 @@ static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
||||
iwl_mvm_add_link(mvm, vif, link_conf);
|
||||
}
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
/* update EMLSR mode */
|
||||
if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
|
||||
iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -684,25 +666,6 @@ static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw,
|
||||
&callbacks);
|
||||
}
|
||||
|
||||
static bool iwl_mvm_esr_bw_criteria(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
struct ieee80211_bss_conf *other_link;
|
||||
int link_id;
|
||||
|
||||
/* Exit EMLSR if links don't have equal bandwidths */
|
||||
for_each_vif_active_link(vif, other_link, link_id) {
|
||||
if (link_id == link_conf->link_id)
|
||||
continue;
|
||||
if (link_conf->chanreq.oper.width ==
|
||||
other_link->chanreq.oper.width)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
@@ -737,14 +700,6 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm,
|
||||
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
|
||||
}
|
||||
|
||||
if ((changes & BSS_CHANGED_BANDWIDTH) &&
|
||||
ieee80211_vif_link_active(vif, link_conf->link_id) &&
|
||||
mvmvif->esr_active &&
|
||||
!iwl_mvm_esr_bw_criteria(mvm, vif, link_conf))
|
||||
iwl_mvm_exit_esr(mvm, vif,
|
||||
IWL_MVM_ESR_EXIT_BANDWIDTH,
|
||||
iwl_mvm_get_primary_link(vif));
|
||||
|
||||
/* if associated, maybe puncturing changed - we'll check later */
|
||||
if (vif->cfg.assoc)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS;
|
||||
@@ -879,11 +834,6 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm,
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "failed to update power mode\n");
|
||||
}
|
||||
|
||||
if (changes & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM) &&
|
||||
ieee80211_vif_is_mld(vif) && mvmvif->authorized)
|
||||
wiphy_delayed_work_queue(mvm->hw->wiphy,
|
||||
&mvmvif->mlo_int_scan_wk, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1239,91 +1189,6 @@ iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
return NEG_TTLM_RES_ACCEPT;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
if (mvmvif->esr_active) {
|
||||
u8 primary = iwl_mvm_get_primary_link(vif);
|
||||
int selected;
|
||||
|
||||
/* prefer primary unless quiet CSA on it */
|
||||
if (chsw->link_id == primary && chsw->block_tx)
|
||||
selected = iwl_mvm_get_other_link(vif, primary);
|
||||
else
|
||||
selected = primary;
|
||||
|
||||
/*
|
||||
* remembers to tell the firmware that this link can't tx
|
||||
* Note that this logic seems to be unrelated to esr, but it
|
||||
* really is needed only when esr is active. When we have a
|
||||
* single link, the firmware will handle all this on its own.
|
||||
* In multi-link scenarios, we can learn about the CSA from
|
||||
* another link and this logic is too complex for the firmware
|
||||
* to track.
|
||||
* Since we want to de-activate the link that got a CSA, we
|
||||
* need to tell the firmware not to send any frame on that link
|
||||
* as the firmware may not be aware that link is under a CSA
|
||||
* with mode=1 (no Tx allowed).
|
||||
*/
|
||||
if (chsw->block_tx && mvmvif->link[chsw->link_id])
|
||||
mvmvif->link[chsw->link_id]->csa_block_tx = true;
|
||||
|
||||
iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
/*
|
||||
* If we've not kept the link active that's doing the CSA
|
||||
* then we don't need to do anything else, just return.
|
||||
*/
|
||||
if (selected != chsw->link_id)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
}
|
||||
|
||||
ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT (5 * HZ)
|
||||
|
||||
static void iwl_mvm_mld_prep_add_interface(struct ieee80211_hw *hw,
|
||||
enum nl80211_iftype type)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
|
||||
struct iwl_mvm_vif *mvmvif;
|
||||
int ret;
|
||||
|
||||
IWL_DEBUG_MAC80211(mvm, "prep_add_interface: type=%u\n",
|
||||
type);
|
||||
|
||||
if (IS_ERR_OR_NULL(bss_vif) ||
|
||||
!(type == NL80211_IFTYPE_AP ||
|
||||
type == NL80211_IFTYPE_P2P_GO ||
|
||||
type == NL80211_IFTYPE_P2P_CLIENT))
|
||||
return;
|
||||
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(bss_vif);
|
||||
ret = iwl_mvm_block_esr_sync(mvm, bss_vif,
|
||||
IWL_MVM_ESR_BLOCKED_TMP_NON_BSS);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
wiphy_delayed_work_queue(mvmvif->mvm->hw->wiphy,
|
||||
&mvmvif->unblock_esr_tmp_non_bss_wk,
|
||||
IWL_MVM_MLD_UNBLOCK_ESR_NON_BSS_TIMEOUT);
|
||||
}
|
||||
|
||||
const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
|
||||
.tx = iwl_mvm_mac_tx,
|
||||
.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
|
||||
@@ -1377,7 +1242,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
|
||||
.tx_last_beacon = iwl_mvm_tx_last_beacon,
|
||||
|
||||
.channel_switch = iwl_mvm_channel_switch,
|
||||
.pre_channel_switch = iwl_mvm_mld_mac_pre_channel_switch,
|
||||
.pre_channel_switch = iwl_mvm_mac_pre_channel_switch,
|
||||
.post_channel_switch = iwl_mvm_post_channel_switch,
|
||||
.abort_channel_switch = iwl_mvm_abort_channel_switch,
|
||||
.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,
|
||||
@@ -1418,5 +1283,4 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
|
||||
.change_sta_links = iwl_mvm_mld_change_sta_links,
|
||||
.can_activate_links = iwl_mvm_mld_can_activate_links,
|
||||
.can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm,
|
||||
.prep_add_interface = iwl_mvm_mld_prep_add_interface,
|
||||
};
|
||||
|
||||
@@ -852,8 +852,6 @@ int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
|
||||
iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, link_id);
|
||||
}
|
||||
kfree(mvm_sta->mpdu_counters);
|
||||
mvm_sta->mpdu_counters = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -347,68 +347,6 @@ struct iwl_mvm_vif_link_info {
|
||||
u32 average_beacon_energy;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_mvm_esr_state - defines reasons for which the EMLSR is exited or
|
||||
* blocked.
|
||||
* The low 16 bits are used for blocking reasons, and the 16 higher bits
|
||||
* are used for exit reasons.
|
||||
* For the blocking reasons - use iwl_mvm_(un)block_esr(), and for the exit
|
||||
* reasons - use iwl_mvm_exit_esr().
|
||||
*
|
||||
* Note: new reasons shall be added to HANDLE_ESR_REASONS as well (for logs)
|
||||
*
|
||||
* @IWL_MVM_ESR_BLOCKED_PREVENTION: Prevent EMLSR to avoid entering and exiting
|
||||
* in a loop.
|
||||
* @IWL_MVM_ESR_BLOCKED_WOWLAN: WOWLAN is preventing the enablement of EMLSR
|
||||
* @IWL_MVM_ESR_BLOCKED_TPT: block EMLSR when there is not enough traffic
|
||||
* @IWL_MVM_ESR_BLOCKED_FW: FW didn't recommended/forced exit from EMLSR
|
||||
* @IWL_MVM_ESR_BLOCKED_NON_BSS: An active non-BSS interface's link is
|
||||
* preventing EMLSR
|
||||
* @IWL_MVM_ESR_BLOCKED_ROC: remain-on-channel is preventing EMLSR
|
||||
* @IWL_MVM_ESR_BLOCKED_TMP_NON_BSS: An expected active non-BSS interface's link
|
||||
* is preventing EMLSR. This is a temporary blocking that is set when there
|
||||
* is an indication that a non-BSS interface is to be added.
|
||||
* @IWL_MVM_ESR_EXIT_MISSED_BEACON: exited EMLSR due to missed beacons
|
||||
* @IWL_MVM_ESR_EXIT_LOW_RSSI: link is deactivated/not allowed for EMLSR
|
||||
* due to low RSSI.
|
||||
* @IWL_MVM_ESR_EXIT_COEX: link is deactivated/not allowed for EMLSR
|
||||
* due to BT Coex.
|
||||
* @IWL_MVM_ESR_EXIT_BANDWIDTH: Bandwidths of primary and secondry links
|
||||
* preventing the enablement of EMLSR
|
||||
* @IWL_MVM_ESR_EXIT_CSA: CSA happened, so exit EMLSR
|
||||
* @IWL_MVM_ESR_EXIT_LINK_USAGE: Exit EMLSR due to low tpt on secondary link
|
||||
*/
|
||||
enum iwl_mvm_esr_state {
|
||||
IWL_MVM_ESR_BLOCKED_PREVENTION = 0x1,
|
||||
IWL_MVM_ESR_BLOCKED_WOWLAN = 0x2,
|
||||
IWL_MVM_ESR_BLOCKED_TPT = 0x4,
|
||||
IWL_MVM_ESR_BLOCKED_FW = 0x8,
|
||||
IWL_MVM_ESR_BLOCKED_NON_BSS = 0x10,
|
||||
IWL_MVM_ESR_BLOCKED_ROC = 0x20,
|
||||
IWL_MVM_ESR_BLOCKED_TMP_NON_BSS = 0x40,
|
||||
IWL_MVM_ESR_EXIT_MISSED_BEACON = 0x10000,
|
||||
IWL_MVM_ESR_EXIT_LOW_RSSI = 0x20000,
|
||||
IWL_MVM_ESR_EXIT_COEX = 0x40000,
|
||||
IWL_MVM_ESR_EXIT_BANDWIDTH = 0x80000,
|
||||
IWL_MVM_ESR_EXIT_CSA = 0x100000,
|
||||
IWL_MVM_ESR_EXIT_LINK_USAGE = 0x200000,
|
||||
};
|
||||
|
||||
#define IWL_MVM_BLOCK_ESR_REASONS 0xffff
|
||||
|
||||
const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state);
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_esr_exit - details of the last exit from EMLSR mode.
|
||||
* @reason: The reason for the last exit from EMLSR.
|
||||
* &iwl_mvm_prevent_esr_reasons. Will be 0 before exiting EMLSR.
|
||||
* @ts: the time stamp of the last time we existed EMLSR.
|
||||
*/
|
||||
struct iwl_mvm_esr_exit {
|
||||
unsigned long ts;
|
||||
enum iwl_mvm_esr_state reason;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
|
||||
* @mvm: pointer back to the mvm struct
|
||||
@@ -443,7 +381,6 @@ struct iwl_mvm_esr_exit {
|
||||
* @deflink: default link data for use in non-MLO
|
||||
* @link: link data for each link in MLO
|
||||
* @esr_active: indicates eSR mode is active
|
||||
* @esr_disable_reason: a bitmap of &enum iwl_mvm_esr_state
|
||||
* @pm_enabled: indicates powersave is enabled
|
||||
* @link_selection_res: bitmap of active links as it was decided in the last
|
||||
* link selection. Valid only for a MLO vif after assoc. 0 if there wasn't
|
||||
@@ -451,15 +388,6 @@ struct iwl_mvm_esr_exit {
|
||||
* @link_selection_primary: primary link selected by link selection
|
||||
* @primary_link: primary link in eSR. Valid only for an associated MLD vif,
|
||||
* and in eSR mode. Valid only for a STA.
|
||||
* @last_esr_exit: Details of the last exit from EMLSR.
|
||||
* @exit_same_reason_count: The number of times we exited due to the specified
|
||||
* @last_esr_exit::reason, only counting exits due to
|
||||
* &IWL_MVM_ESR_PREVENT_REASONS.
|
||||
* @prevent_esr_done_wk: work that should be done when esr prevention ends.
|
||||
* @mlo_int_scan_wk: work for the internal MLO scan.
|
||||
* @unblock_esr_tpt_wk: work for unblocking EMLSR when tpt is high enough.
|
||||
* @unblock_esr_tmp_non_bss_wk: work for removing the
|
||||
* IWL_MVM_ESR_BLOCKED_TMP_NON_BSS blocking for EMLSR.
|
||||
* @roc_activity: currently running ROC activity for this vif (or
|
||||
* ROC_NUM_ACTIVITIES if no activity is running).
|
||||
* @session_prot_connection_loss: the connection was lost due to session
|
||||
@@ -515,7 +443,6 @@ struct iwl_mvm_vif {
|
||||
u8 authorized:1;
|
||||
bool ps_disabled;
|
||||
|
||||
u32 esr_disable_reason;
|
||||
u32 ap_beacon_time;
|
||||
bool bf_enabled;
|
||||
bool ba_enabled;
|
||||
@@ -591,12 +518,6 @@ struct iwl_mvm_vif {
|
||||
u16 link_selection_res;
|
||||
u8 link_selection_primary;
|
||||
u8 primary_link;
|
||||
struct iwl_mvm_esr_exit last_esr_exit;
|
||||
u8 exit_same_reason_count;
|
||||
struct wiphy_delayed_work prevent_esr_done_wk;
|
||||
struct wiphy_delayed_work mlo_int_scan_wk;
|
||||
struct wiphy_work unblock_esr_tpt_wk;
|
||||
struct wiphy_delayed_work unblock_esr_tmp_non_bss_wk;
|
||||
|
||||
struct iwl_mvm_vif_link_info deflink;
|
||||
struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
@@ -622,7 +543,6 @@ enum iwl_scan_status {
|
||||
IWL_MVM_SCAN_REGULAR = BIT(0),
|
||||
IWL_MVM_SCAN_SCHED = BIT(1),
|
||||
IWL_MVM_SCAN_NETDETECT = BIT(2),
|
||||
IWL_MVM_SCAN_INT_MLO = BIT(3),
|
||||
|
||||
IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8),
|
||||
IWL_MVM_SCAN_STOPPING_SCHED = BIT(9),
|
||||
@@ -635,8 +555,6 @@ enum iwl_scan_status {
|
||||
IWL_MVM_SCAN_STOPPING_SCHED,
|
||||
IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT |
|
||||
IWL_MVM_SCAN_STOPPING_NETDETECT,
|
||||
IWL_MVM_SCAN_INT_MLO_MASK = IWL_MVM_SCAN_INT_MLO |
|
||||
IWL_MVM_SCAN_STOPPING_INT_MLO,
|
||||
|
||||
IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT,
|
||||
IWL_MVM_SCAN_MASK = 0xff,
|
||||
@@ -1017,8 +935,6 @@ struct iwl_mvm {
|
||||
/* For async rx handlers that require the wiphy lock */
|
||||
struct wiphy_work async_handlers_wiphy_wk;
|
||||
|
||||
struct wiphy_work trig_link_selection_wk;
|
||||
|
||||
struct work_struct roc_done_wk;
|
||||
|
||||
unsigned long init_status;
|
||||
@@ -1203,20 +1119,13 @@ struct iwl_mvm {
|
||||
u8 offload_tid;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
bool d3_wake_sysassert;
|
||||
bool d3_test_active;
|
||||
u32 d3_test_pme_ptr;
|
||||
struct ieee80211_vif *keep_vif;
|
||||
u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
wait_queue_head_t rx_sync_waitq;
|
||||
|
||||
/* BT-Coex - only one of those will be used */
|
||||
union {
|
||||
struct iwl_bt_coex_prof_old_notif last_bt_notif;
|
||||
struct iwl_bt_coex_profile_notif last_bt_wifi_loss;
|
||||
};
|
||||
struct iwl_bt_coex_prof_old_notif last_bt_notif;
|
||||
struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
|
||||
|
||||
u8 bt_tx_prio;
|
||||
@@ -2099,9 +2008,7 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
|
||||
u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif);
|
||||
u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id);
|
||||
|
||||
struct iwl_mvm_link_sel_data {
|
||||
u8 link_id;
|
||||
@@ -2111,13 +2018,6 @@ struct iwl_mvm_link_sel_data {
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
|
||||
unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
|
||||
bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
|
||||
const struct iwl_mvm_link_sel_data *a,
|
||||
const struct iwl_mvm_link_sel_data *b);
|
||||
|
||||
s8 iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif);
|
||||
|
||||
extern const struct iwl_hcmd_arr iwl_mvm_groups[];
|
||||
extern const unsigned int iwl_mvm_groups_size;
|
||||
#endif
|
||||
@@ -2201,7 +2101,6 @@ int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify);
|
||||
int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_scan_timeout_wk(struct work_struct *work);
|
||||
int iwl_mvm_int_mlo_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
|
||||
void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb);
|
||||
|
||||
@@ -2327,8 +2226,6 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
|
||||
int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm);
|
||||
void iwl_mvm_rx_bt_coex_old_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb);
|
||||
void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb);
|
||||
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum ieee80211_rssi_event_data);
|
||||
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
|
||||
@@ -2929,9 +2826,10 @@ void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
|
||||
int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw);
|
||||
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 iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw);
|
||||
int iwl_mvm_mac_pre_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *chsw);
|
||||
|
||||
void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
@@ -2988,30 +2886,6 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm,
|
||||
|
||||
/* EMLSR */
|
||||
bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
|
||||
void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason,
|
||||
u8 link_to_keep);
|
||||
int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason);
|
||||
void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason);
|
||||
void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
enum iwl_mvm_esr_state reason,
|
||||
u8 link_to_keep);
|
||||
s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
bool low);
|
||||
void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
int link_id);
|
||||
bool
|
||||
iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
s32 link_rssi,
|
||||
bool primary);
|
||||
int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
unsigned int link_id, bool active);
|
||||
|
||||
void
|
||||
iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
|
||||
@@ -143,24 +143,6 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
|
||||
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
|
||||
}
|
||||
|
||||
static void iwl_mvm_rx_esr_mode_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb)
|
||||
{
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
struct iwl_esr_mode_notif *notif = (void *)pkt->data;
|
||||
struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm);
|
||||
|
||||
/* FW recommendations is only for entering EMLSR */
|
||||
if (IS_ERR_OR_NULL(vif) || iwl_mvm_vif_from_mac80211(vif)->esr_active)
|
||||
return;
|
||||
|
||||
if (le32_to_cpu(notif->action) == ESR_RECOMMEND_ENTER)
|
||||
iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_FW);
|
||||
else
|
||||
iwl_mvm_block_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_FW,
|
||||
iwl_mvm_get_primary_link(vif));
|
||||
}
|
||||
|
||||
static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb)
|
||||
{
|
||||
@@ -345,9 +327,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
|
||||
RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_old_notif,
|
||||
RX_HANDLER_ASYNC_LOCKED_WIPHY,
|
||||
struct iwl_bt_coex_prof_old_notif),
|
||||
RX_HANDLER_GRP(BT_COEX_GROUP, PROFILE_NOTIF, iwl_mvm_rx_bt_coex_notif,
|
||||
RX_HANDLER_ASYNC_LOCKED_WIPHY,
|
||||
struct iwl_bt_coex_profile_notif),
|
||||
RX_HANDLER_NO_SIZE(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif,
|
||||
RX_HANDLER_ASYNC_LOCKED),
|
||||
RX_HANDLER_NO_SIZE(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics,
|
||||
@@ -457,11 +436,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
|
||||
RX_HANDLER_ASYNC_UNLOCKED,
|
||||
struct iwl_channel_switch_error_notif),
|
||||
|
||||
RX_HANDLER_GRP(DATA_PATH_GROUP, ESR_MODE_NOTIF,
|
||||
iwl_mvm_rx_esr_mode_notif,
|
||||
RX_HANDLER_ASYNC_LOCKED_WIPHY,
|
||||
struct iwl_esr_mode_notif),
|
||||
|
||||
RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF,
|
||||
iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED,
|
||||
struct iwl_datapath_monitor_notif),
|
||||
@@ -661,7 +635,6 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = {
|
||||
HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD),
|
||||
HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
|
||||
HCMD_NAME(SEC_KEY_CMD),
|
||||
HCMD_NAME(ESR_MODE_NOTIF),
|
||||
HCMD_NAME(MONITOR_NOTIF),
|
||||
HCMD_NAME(THERMAL_DUAL_CHAIN_REQUEST),
|
||||
HCMD_NAME(BEACON_FILTER_IN_NOTIF),
|
||||
@@ -1220,29 +1193,6 @@ static const struct iwl_mei_ops mei_ops = {
|
||||
.nic_stolen = iwl_mvm_mei_nic_stolen,
|
||||
};
|
||||
|
||||
static void iwl_mvm_find_link_selection_vif(void *_data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
|
||||
if (ieee80211_vif_is_mld(vif) && mvmvif->authorized)
|
||||
iwl_mvm_select_links(mvmvif->mvm, vif);
|
||||
}
|
||||
|
||||
static void iwl_mvm_trig_link_selection(struct wiphy *wiphy,
|
||||
struct wiphy_work *wk)
|
||||
{
|
||||
struct iwl_mvm *mvm =
|
||||
container_of(wk, struct iwl_mvm, trig_link_selection_wk);
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
ieee80211_iterate_active_interfaces(mvm->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_find_link_selection_vif,
|
||||
NULL);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
static struct iwl_op_mode *
|
||||
iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
|
||||
const struct iwl_fw *fw, struct dentry *dbgfs_dir)
|
||||
@@ -1411,9 +1361,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
|
||||
wiphy_work_init(&mvm->async_handlers_wiphy_wk,
|
||||
iwl_mvm_async_handlers_wiphy_wk);
|
||||
|
||||
wiphy_work_init(&mvm->trig_link_selection_wk,
|
||||
iwl_mvm_trig_link_selection);
|
||||
|
||||
init_waitqueue_head(&mvm->rx_sync_waitq);
|
||||
|
||||
mvm->queue_sync_state = 0;
|
||||
|
||||
@@ -563,7 +563,6 @@ static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig,
|
||||
int thold = bss_conf->cqm_rssi_thold;
|
||||
int hyst = bss_conf->cqm_rssi_hyst;
|
||||
int last_event;
|
||||
s8 exit_esr_thresh;
|
||||
|
||||
if (sig == 0) {
|
||||
IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
|
||||
@@ -619,27 +618,6 @@ static void iwl_mvm_update_link_sig(struct ieee80211_vif *vif, int sig,
|
||||
sig,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
/* ESR recalculation */
|
||||
if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
|
||||
return;
|
||||
|
||||
/* We're not in EMLSR and our signal is bad, try to switch link maybe */
|
||||
if (sig < IWL_MVM_LOW_RSSI_MLO_SCAN_THRESH && !mvmvif->esr_active) {
|
||||
iwl_mvm_int_mlo_scan(mvm, vif);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We are in EMLSR, check if we need to exit */
|
||||
exit_esr_thresh =
|
||||
iwl_mvm_get_esr_rssi_thresh(mvm,
|
||||
&bss_conf->chanreq.oper,
|
||||
true);
|
||||
|
||||
if (sig < exit_esr_thresh)
|
||||
iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_LOW_RSSI,
|
||||
iwl_mvm_get_other_link(vif,
|
||||
bss_conf->link_id));
|
||||
}
|
||||
|
||||
static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
|
||||
@@ -914,10 +892,6 @@ iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm,
|
||||
link_info->beacon_stats.avg_signal =
|
||||
-le32_to_cpu(link_stats->beacon_average_energy);
|
||||
|
||||
if (link_info->phy_ctxt &&
|
||||
link_info->phy_ctxt->channel->band == NL80211_BAND_2GHZ)
|
||||
iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id);
|
||||
|
||||
/* make sure that beacon statistics don't go backwards with TCM
|
||||
* request to clear statistics
|
||||
*/
|
||||
@@ -956,111 +930,6 @@ iwl_mvm_stat_iterator_all_links(struct iwl_mvm *mvm,
|
||||
}
|
||||
}
|
||||
|
||||
#define SEC_LINK_MIN_PERC 10
|
||||
#define SEC_LINK_MIN_TX 3000
|
||||
#define SEC_LINK_MIN_RX 400
|
||||
|
||||
/* Accept a ~20% short window to avoid issues due to jitter */
|
||||
#define IWL_MVM_TPT_MIN_COUNT_WINDOW (IWL_MVM_TPT_COUNT_WINDOW_SEC * HZ * 4 / 5)
|
||||
|
||||
static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
|
||||
struct iwl_mvm_vif *mvmvif;
|
||||
struct iwl_mvm_sta *mvmsta;
|
||||
unsigned long total_tx = 0, total_rx = 0;
|
||||
unsigned long sec_link_tx = 0, sec_link_rx = 0;
|
||||
u8 sec_link_tx_perc, sec_link_rx_perc;
|
||||
u8 sec_link;
|
||||
bool skip = false;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (IS_ERR_OR_NULL(bss_vif))
|
||||
return;
|
||||
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(bss_vif);
|
||||
|
||||
if (!mvmvif->esr_active || !mvmvif->ap_sta)
|
||||
return;
|
||||
|
||||
mvmsta = iwl_mvm_sta_from_mac80211(mvmvif->ap_sta);
|
||||
/* We only count for the AP sta in a MLO connection */
|
||||
if (!mvmsta->mpdu_counters)
|
||||
return;
|
||||
|
||||
/* Get the FW ID of the secondary link */
|
||||
sec_link = iwl_mvm_get_other_link(bss_vif,
|
||||
iwl_mvm_get_primary_link(bss_vif));
|
||||
if (WARN_ON(!mvmvif->link[sec_link]))
|
||||
return;
|
||||
sec_link = mvmvif->link[sec_link]->fw_link_id;
|
||||
|
||||
/* Sum up RX and TX MPDUs from the different queues/links */
|
||||
for (int q = 0; q < mvm->trans->info.num_rxqs; q++) {
|
||||
spin_lock_bh(&mvmsta->mpdu_counters[q].lock);
|
||||
|
||||
/* The link IDs that doesn't exist will contain 0 */
|
||||
for (int link = 0; link < IWL_FW_MAX_LINK_ID; link++) {
|
||||
total_tx += mvmsta->mpdu_counters[q].per_link[link].tx;
|
||||
total_rx += mvmsta->mpdu_counters[q].per_link[link].rx;
|
||||
}
|
||||
|
||||
sec_link_tx += mvmsta->mpdu_counters[q].per_link[sec_link].tx;
|
||||
sec_link_rx += mvmsta->mpdu_counters[q].per_link[sec_link].rx;
|
||||
|
||||
/*
|
||||
* In EMLSR we have statistics every 5 seconds, so we can reset
|
||||
* the counters upon every statistics notification.
|
||||
* The FW sends the notification regularly, but it will be
|
||||
* misaligned at the start. Skipping the measurement if it is
|
||||
* short will synchronize us.
|
||||
*/
|
||||
if (jiffies - mvmsta->mpdu_counters[q].window_start <
|
||||
IWL_MVM_TPT_MIN_COUNT_WINDOW)
|
||||
skip = true;
|
||||
mvmsta->mpdu_counters[q].window_start = jiffies;
|
||||
memset(mvmsta->mpdu_counters[q].per_link, 0,
|
||||
sizeof(mvmsta->mpdu_counters[q].per_link));
|
||||
|
||||
spin_unlock_bh(&mvmsta->mpdu_counters[q].lock);
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
IWL_DEBUG_INFO(mvm, "MPDU statistics window was short\n");
|
||||
return;
|
||||
}
|
||||
|
||||
IWL_DEBUG_INFO(mvm, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n",
|
||||
total_tx, total_rx);
|
||||
|
||||
/* If we don't have enough MPDUs - exit EMLSR */
|
||||
if (total_tx < IWL_MVM_ENTER_ESR_TPT_THRESH &&
|
||||
total_rx < IWL_MVM_ENTER_ESR_TPT_THRESH) {
|
||||
iwl_mvm_block_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_TPT,
|
||||
iwl_mvm_get_primary_link(bss_vif));
|
||||
return;
|
||||
}
|
||||
|
||||
IWL_DEBUG_INFO(mvm, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n",
|
||||
sec_link, sec_link_tx, sec_link_rx);
|
||||
|
||||
/* Calculate the percentage of the secondary link TX/RX */
|
||||
sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
|
||||
sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0;
|
||||
|
||||
/*
|
||||
* The TX/RX percentage is checked only if it exceeds the required
|
||||
* minimum. In addition, RX is checked only if the TX check failed.
|
||||
*/
|
||||
if ((total_tx > SEC_LINK_MIN_TX &&
|
||||
sec_link_tx_perc < SEC_LINK_MIN_PERC) ||
|
||||
(total_rx > SEC_LINK_MIN_RX &&
|
||||
sec_link_rx_perc < SEC_LINK_MIN_PERC))
|
||||
iwl_mvm_exit_esr(mvm, bss_vif, IWL_MVM_ESR_EXIT_LINK_USAGE,
|
||||
iwl_mvm_get_primary_link(bss_vif));
|
||||
}
|
||||
|
||||
void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb)
|
||||
{
|
||||
@@ -1088,8 +957,6 @@ void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm,
|
||||
ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter,
|
||||
average_energy);
|
||||
iwl_mvm_handle_per_phy_stats(mvm, stats->per_phy);
|
||||
|
||||
iwl_mvm_update_esr_mode_tpt(mvm);
|
||||
}
|
||||
|
||||
void iwl_mvm_handle_rx_system_oper_part1_stats(struct iwl_mvm *mvm,
|
||||
|
||||
@@ -332,6 +332,7 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key;
|
||||
u32 len = le16_to_cpu(desc->mpdu_len);
|
||||
const u8 *frame = (void *)hdr;
|
||||
const u8 *mmie;
|
||||
|
||||
if ((status & IWL_RX_MPDU_STATUS_SEC_MASK) == IWL_RX_MPDU_STATUS_SEC_NONE)
|
||||
return 0;
|
||||
@@ -375,11 +376,15 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta,
|
||||
goto report;
|
||||
}
|
||||
|
||||
if (len < key->icv_len + IEEE80211_GMAC_PN_LEN + 2)
|
||||
if (len < key->icv_len)
|
||||
goto report;
|
||||
|
||||
/* get the real key ID */
|
||||
keyid = frame[len - key->icv_len - IEEE80211_GMAC_PN_LEN - 2];
|
||||
mmie = frame + (len - key->icv_len);
|
||||
|
||||
/* the position of the key_id in ieee80211_mmie_16 is the same */
|
||||
keyid = le16_to_cpu(((const struct ieee80211_mmie *) mmie)->key_id);
|
||||
|
||||
/* and if that's the other key, look it up */
|
||||
if (keyid != key->keyidx) {
|
||||
/*
|
||||
@@ -2099,7 +2104,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
|
||||
struct ieee80211_sta *sta = NULL;
|
||||
struct sk_buff *skb;
|
||||
u8 crypt_len = 0;
|
||||
u8 sta_id = le32_get_bits(desc->status, IWL_RX_MPDU_STATUS_STA_ID);
|
||||
size_t desc_size;
|
||||
struct iwl_mvm_rx_phy_data phy_data = {};
|
||||
u32 format;
|
||||
@@ -2246,6 +2250,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
|
||||
rcu_read_lock();
|
||||
|
||||
if (desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) {
|
||||
u8 sta_id = le32_get_bits(desc->status,
|
||||
IWL_RX_MPDU_STATUS_STA_ID);
|
||||
|
||||
if (!WARN_ON_ONCE(sta_id >= mvm->fw->ucode_capa.num_stations)) {
|
||||
struct ieee80211_link_sta *link_sta;
|
||||
|
||||
@@ -2373,16 +2380,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
|
||||
|
||||
iwl_mvm_agg_rx_received(mvm, reorder_data, baid);
|
||||
}
|
||||
|
||||
if (ieee80211_is_data(hdr->frame_control)) {
|
||||
u8 sub_frame_idx = desc->amsdu_info &
|
||||
IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
|
||||
|
||||
/* 0 means not an A-MSDU, and 1 means a new A-MSDU */
|
||||
if (!sub_frame_idx || sub_frame_idx == 1)
|
||||
iwl_mvm_count_mpdu(mvmsta, sta_id, 1, false,
|
||||
queue);
|
||||
}
|
||||
}
|
||||
|
||||
/* management stuff on default queue */
|
||||
|
||||
@@ -1392,8 +1392,6 @@ static u32 iwl_mvm_scan_umac_ooc_priority(int type)
|
||||
{
|
||||
if (type == IWL_MVM_SCAN_REGULAR)
|
||||
return IWL_SCAN_PRIORITY_EXT_6;
|
||||
if (type == IWL_MVM_SCAN_INT_MLO)
|
||||
return IWL_SCAN_PRIORITY_EXT_4;
|
||||
|
||||
return IWL_SCAN_PRIORITY_EXT_2;
|
||||
}
|
||||
@@ -3220,7 +3218,6 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_umac_scan_complete *notif = (void *)pkt->data;
|
||||
u32 uid = __le32_to_cpu(notif->uid);
|
||||
bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED);
|
||||
bool select_links = false;
|
||||
|
||||
mvm->mei_scan_filter.is_mei_limited_scan = false;
|
||||
|
||||
@@ -3267,13 +3264,6 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
|
||||
} else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) {
|
||||
ieee80211_sched_scan_stopped(mvm->hw);
|
||||
mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
|
||||
} else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_INT_MLO) {
|
||||
IWL_DEBUG_SCAN(mvm, "Internal MLO scan completed\n");
|
||||
/*
|
||||
* Other scan types won't necessarily scan for the MLD links channels.
|
||||
* Therefore, only select links after successful internal scan.
|
||||
*/
|
||||
select_links = notif->status == IWL_SCAN_OFFLOAD_COMPLETED;
|
||||
}
|
||||
|
||||
mvm->scan_status &= ~mvm->scan_uid_status[uid];
|
||||
@@ -3286,9 +3276,6 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
|
||||
mvm->last_ebs_successful = false;
|
||||
|
||||
mvm->scan_uid_status[uid] = 0;
|
||||
|
||||
if (select_links)
|
||||
wiphy_work_queue(mvm->hw->wiphy, &mvm->trig_link_selection_wk);
|
||||
}
|
||||
|
||||
void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
|
||||
@@ -3483,11 +3470,6 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
|
||||
mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
|
||||
mvm->scan_uid_status[uid] = 0;
|
||||
}
|
||||
uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_INT_MLO);
|
||||
if (uid >= 0) {
|
||||
IWL_DEBUG_SCAN(mvm, "Internal MLO scan aborted\n");
|
||||
mvm->scan_uid_status[uid] = 0;
|
||||
}
|
||||
|
||||
uid = iwl_mvm_scan_uid_by_status(mvm,
|
||||
IWL_MVM_SCAN_STOPPING_REGULAR);
|
||||
@@ -3583,89 +3565,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_int_mlo_scan_start(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel **channels,
|
||||
size_t n_channels)
|
||||
{
|
||||
struct cfg80211_scan_request *req = NULL;
|
||||
struct ieee80211_scan_ies ies = {};
|
||||
size_t size, i;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
IWL_DEBUG_SCAN(mvm, "Starting Internal MLO scan: n_channels=%zu\n",
|
||||
n_channels);
|
||||
|
||||
if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) ||
|
||||
hweight16(vif->valid_links) == 1)
|
||||
return -EINVAL;
|
||||
|
||||
size = struct_size(req, channels, n_channels);
|
||||
req = kzalloc(size, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
/* set the requested channels */
|
||||
for (i = 0; i < n_channels; i++)
|
||||
req->channels[i] = channels[i];
|
||||
|
||||
req->n_channels = n_channels;
|
||||
|
||||
/* set the rates */
|
||||
for (i = 0; i < NUM_NL80211_BANDS; i++)
|
||||
if (mvm->hw->wiphy->bands[i])
|
||||
req->rates[i] =
|
||||
(1 << mvm->hw->wiphy->bands[i]->n_bitrates) - 1;
|
||||
|
||||
req->wdev = ieee80211_vif_to_wdev(vif);
|
||||
req->wiphy = mvm->hw->wiphy;
|
||||
req->scan_start = jiffies;
|
||||
req->tsf_report_link_id = -1;
|
||||
|
||||
ret = _iwl_mvm_single_scan_start(mvm, vif, req, &ies,
|
||||
IWL_MVM_SCAN_INT_MLO);
|
||||
kfree(req);
|
||||
|
||||
IWL_DEBUG_SCAN(mvm, "Internal MLO scan: ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mvm_int_mlo_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
unsigned long usable_links = ieee80211_vif_usable_links(vif);
|
||||
size_t n_channels = 0;
|
||||
u8 link_id;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (mvm->scan_status & IWL_MVM_SCAN_INT_MLO) {
|
||||
IWL_DEBUG_SCAN(mvm, "Internal MLO scan is already running\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
channels[n_channels++] = link_conf->chanreq.oper.chan;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!n_channels)
|
||||
return -EINVAL;
|
||||
|
||||
return iwl_mvm_int_mlo_scan_start(mvm, vif, channels, n_channels);
|
||||
}
|
||||
|
||||
static int iwl_mvm_chanidx_from_phy(struct iwl_mvm *mvm,
|
||||
enum nl80211_band band,
|
||||
u16 phy_chan_num)
|
||||
|
||||
@@ -1835,18 +1835,6 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
|
||||
iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant);
|
||||
|
||||
/* MPDUs are counted only when EMLSR is possible */
|
||||
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
|
||||
!sta->tdls && ieee80211_vif_is_mld(vif)) {
|
||||
mvm_sta->mpdu_counters =
|
||||
kcalloc(mvm->trans->info.num_rxqs,
|
||||
sizeof(*mvm_sta->mpdu_counters),
|
||||
GFP_KERNEL);
|
||||
if (mvm_sta->mpdu_counters)
|
||||
for (int q = 0; q < mvm->trans->info.num_rxqs; q++)
|
||||
spin_lock_init(&mvm_sta->mpdu_counters[q].lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4328,80 +4316,3 @@ void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm,
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "Failed to cancel the channel switch\n");
|
||||
}
|
||||
|
||||
static int iwl_mvm_fw_sta_id_to_fw_link_id(struct iwl_mvm_vif *mvmvif,
|
||||
u8 fw_sta_id)
|
||||
{
|
||||
struct ieee80211_link_sta *link_sta =
|
||||
rcu_dereference(mvmvif->mvm->fw_id_to_link_sta[fw_sta_id]);
|
||||
struct iwl_mvm_vif_link_info *link;
|
||||
|
||||
if (WARN_ON_ONCE(!link_sta))
|
||||
return -EINVAL;
|
||||
|
||||
link = mvmvif->link[link_sta->link_id];
|
||||
|
||||
if (WARN_ON_ONCE(!link))
|
||||
return -EINVAL;
|
||||
|
||||
return link->fw_link_id;
|
||||
}
|
||||
|
||||
#define IWL_MVM_TPT_COUNT_WINDOW (IWL_MVM_TPT_COUNT_WINDOW_SEC * HZ)
|
||||
|
||||
void iwl_mvm_count_mpdu(struct iwl_mvm_sta *mvm_sta, u8 fw_sta_id, u32 count,
|
||||
bool tx, int queue)
|
||||
{
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvm_sta->vif);
|
||||
struct iwl_mvm *mvm = mvmvif->mvm;
|
||||
struct iwl_mvm_tpt_counter *queue_counter;
|
||||
struct iwl_mvm_mpdu_counter *link_counter;
|
||||
u32 total_mpdus = 0;
|
||||
int fw_link_id;
|
||||
|
||||
/* Count only for a BSS sta, and only when EMLSR is possible */
|
||||
if (!mvm_sta->mpdu_counters)
|
||||
return;
|
||||
|
||||
/* Map sta id to link id */
|
||||
fw_link_id = iwl_mvm_fw_sta_id_to_fw_link_id(mvmvif, fw_sta_id);
|
||||
if (fw_link_id < 0)
|
||||
return;
|
||||
|
||||
queue_counter = &mvm_sta->mpdu_counters[queue];
|
||||
link_counter = &queue_counter->per_link[fw_link_id];
|
||||
|
||||
spin_lock_bh(&queue_counter->lock);
|
||||
|
||||
if (tx)
|
||||
link_counter->tx += count;
|
||||
else
|
||||
link_counter->rx += count;
|
||||
|
||||
/*
|
||||
* When not in EMLSR, the window and the decision to enter EMLSR are
|
||||
* handled during counting, when in EMLSR - in the statistics flow
|
||||
*/
|
||||
if (mvmvif->esr_active)
|
||||
goto out;
|
||||
|
||||
if (time_is_before_jiffies(queue_counter->window_start +
|
||||
IWL_MVM_TPT_COUNT_WINDOW)) {
|
||||
memset(queue_counter->per_link, 0,
|
||||
sizeof(queue_counter->per_link));
|
||||
queue_counter->window_start = jiffies;
|
||||
|
||||
IWL_DEBUG_INFO(mvm, "MPDU counters are cleared\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < IWL_FW_MAX_LINK_ID; i++)
|
||||
total_mpdus += tx ? queue_counter->per_link[i].tx :
|
||||
queue_counter->per_link[i].rx;
|
||||
|
||||
if (total_mpdus > IWL_MVM_ENTER_ESR_TPT_THRESH)
|
||||
wiphy_work_queue(mvmvif->mvm->hw->wiphy,
|
||||
&mvmvif->unblock_esr_tpt_wk);
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&queue_counter->lock);
|
||||
}
|
||||
|
||||
@@ -344,24 +344,6 @@ struct iwl_mvm_link_sta {
|
||||
u8 avg_energy;
|
||||
};
|
||||
|
||||
struct iwl_mvm_mpdu_counter {
|
||||
u32 tx;
|
||||
u32 rx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_tpt_counter - per-queue MPDU counter
|
||||
*
|
||||
* @lock: Needed to protect the counters when modified from statistics.
|
||||
* @per_link: per-link counters.
|
||||
* @window_start: timestamp of the counting-window start
|
||||
*/
|
||||
struct iwl_mvm_tpt_counter {
|
||||
spinlock_t lock;
|
||||
struct iwl_mvm_mpdu_counter per_link[IWL_FW_MAX_LINK_ID];
|
||||
unsigned long window_start;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_sta - representation of a station in the driver
|
||||
* @vif: the interface the station belongs to
|
||||
@@ -409,7 +391,6 @@ struct iwl_mvm_tpt_counter {
|
||||
* @link: per link sta entries. For non-MLO only link[0] holds data. For MLO,
|
||||
* link[0] points to deflink and link[link_id] is allocated when new link
|
||||
* sta is added.
|
||||
* @mpdu_counters: RX/TX MPDUs counters for each queue.
|
||||
*
|
||||
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
|
||||
* in the structure for use by driver. This structure is placed in that
|
||||
@@ -449,8 +430,6 @@ struct iwl_mvm_sta {
|
||||
|
||||
struct iwl_mvm_link_sta deflink;
|
||||
struct iwl_mvm_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
|
||||
struct iwl_mvm_tpt_counter *mpdu_counters;
|
||||
};
|
||||
|
||||
u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data);
|
||||
@@ -533,9 +512,6 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
|
||||
void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_rx_cmd_buffer *rxb);
|
||||
|
||||
void iwl_mvm_count_mpdu(struct iwl_mvm_sta *mvm_sta, u8 fw_sta_id, u32 count,
|
||||
bool tx, int queue);
|
||||
|
||||
/* AMPDU */
|
||||
int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
||||
int tid, u16 ssn, bool start, u16 buf_size, u16 timeout);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
iwlmvm-tests-y += module.o links.o hcmd.o
|
||||
iwlmvm-tests-y += module.o hcmd.o
|
||||
|
||||
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmvm-tests.o
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* KUnit tests for channel helper functions
|
||||
*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
*/
|
||||
#include <net/mac80211.h>
|
||||
#include "../mvm.h"
|
||||
#include <kunit/test.h>
|
||||
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
||||
static struct wiphy wiphy = {
|
||||
.mtx = __MUTEX_INITIALIZER(wiphy.mtx),
|
||||
};
|
||||
|
||||
static struct ieee80211_hw hw = {
|
||||
.wiphy = &wiphy,
|
||||
};
|
||||
|
||||
static struct ieee80211_channel chan_5ghz = {
|
||||
.band = NL80211_BAND_5GHZ,
|
||||
};
|
||||
|
||||
static struct ieee80211_channel chan_6ghz = {
|
||||
.band = NL80211_BAND_6GHZ,
|
||||
};
|
||||
|
||||
static struct ieee80211_channel chan_2ghz = {
|
||||
.band = NL80211_BAND_2GHZ,
|
||||
};
|
||||
|
||||
static struct cfg80211_chan_def chandef_a = {};
|
||||
|
||||
static struct cfg80211_chan_def chandef_b = {};
|
||||
|
||||
static struct iwl_mvm_phy_ctxt ctx = {};
|
||||
|
||||
static struct iwl_mvm_vif_link_info mvm_link = {
|
||||
.phy_ctxt = &ctx,
|
||||
.active = true
|
||||
};
|
||||
|
||||
static struct cfg80211_bss bss = {};
|
||||
|
||||
static struct ieee80211_bss_conf link_conf = {.bss = &bss};
|
||||
|
||||
static const struct iwl_fw_cmd_version entry = {
|
||||
.group = LEGACY_GROUP,
|
||||
.cmd = BT_PROFILE_NOTIFICATION,
|
||||
.notif_ver = 4
|
||||
};
|
||||
|
||||
static struct iwl_fw fw = {
|
||||
.ucode_capa = {
|
||||
.n_cmd_versions = 1,
|
||||
.cmd_versions = &entry,
|
||||
},
|
||||
};
|
||||
|
||||
static struct iwl_mvm mvm = {
|
||||
.hw = &hw,
|
||||
.fw = &fw,
|
||||
};
|
||||
|
||||
static const struct link_grading_case {
|
||||
const char *desc;
|
||||
const struct cfg80211_chan_def chandef;
|
||||
s32 signal;
|
||||
s16 channel_util;
|
||||
int chan_load_by_us;
|
||||
unsigned int grade;
|
||||
} link_grading_cases[] = {
|
||||
{
|
||||
.desc = "UHB, RSSI below range, no factors",
|
||||
.chandef = {
|
||||
.chan = &chan_6ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -100,
|
||||
.grade = 177,
|
||||
},
|
||||
{
|
||||
.desc = "LB, RSSI in range, no factors",
|
||||
.chandef = {
|
||||
.chan = &chan_2ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -84,
|
||||
.grade = 344,
|
||||
},
|
||||
{
|
||||
.desc = "HB, RSSI above range, no factors",
|
||||
.chandef = {
|
||||
.chan = &chan_5ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -50,
|
||||
.grade = 3442,
|
||||
},
|
||||
{
|
||||
.desc = "HB, BSS Load IE (20 percent), inactive link, no puncturing factor",
|
||||
.chandef = {
|
||||
.chan = &chan_5ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -66,
|
||||
.channel_util = 51,
|
||||
.grade = 1836,
|
||||
},
|
||||
{
|
||||
.desc = "LB, BSS Load IE (20 percent), active link, chan_load_by_us=10 percent. No puncturing factor",
|
||||
.chandef = {
|
||||
.chan = &chan_2ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -61,
|
||||
.channel_util = 51,
|
||||
.chan_load_by_us = 10,
|
||||
.grade = 2061,
|
||||
},
|
||||
{
|
||||
.desc = "UHB, BSS Load IE (40 percent), active link, chan_load_by_us=50 (invalid) percent. No puncturing factor",
|
||||
.chandef = {
|
||||
.chan = &chan_6ghz,
|
||||
.width = NL80211_CHAN_WIDTH_20,
|
||||
},
|
||||
.signal = -66,
|
||||
.channel_util = 102,
|
||||
.chan_load_by_us = 50,
|
||||
.grade = 1552,
|
||||
},
|
||||
{ .desc = "HB, 80 MHz, no channel load factor, punctured percentage 0",
|
||||
.chandef = {
|
||||
.chan = &chan_5ghz,
|
||||
.width = NL80211_CHAN_WIDTH_80,
|
||||
.punctured = 0x0000
|
||||
},
|
||||
.signal = -72,
|
||||
.grade = 1750,
|
||||
},
|
||||
{ .desc = "HB, 160 MHz, no channel load factor, punctured percentage 25",
|
||||
.chandef = {
|
||||
.chan = &chan_5ghz,
|
||||
.width = NL80211_CHAN_WIDTH_160,
|
||||
.punctured = 0x3
|
||||
},
|
||||
.signal = -72,
|
||||
.grade = 1312,
|
||||
},
|
||||
{ .desc = "UHB, 320 MHz, no channel load factor, punctured percentage 12.5 (2/16)",
|
||||
.chandef = {
|
||||
.chan = &chan_6ghz,
|
||||
.width = NL80211_CHAN_WIDTH_320,
|
||||
.punctured = 0x3
|
||||
},
|
||||
.signal = -72,
|
||||
.grade = 1806,
|
||||
},
|
||||
{ .desc = "HB, 160 MHz, channel load 20, channel load by us 10, punctured percentage 25",
|
||||
.chandef = {
|
||||
.chan = &chan_5ghz,
|
||||
.width = NL80211_CHAN_WIDTH_160,
|
||||
.punctured = 0x3
|
||||
},
|
||||
.channel_util = 51,
|
||||
.chan_load_by_us = 10,
|
||||
.signal = -72,
|
||||
.grade = 1179,
|
||||
},
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc)
|
||||
|
||||
static void setup_link_conf(struct kunit *test)
|
||||
{
|
||||
const struct link_grading_case *params = test->param_value;
|
||||
size_t vif_size = sizeof(struct ieee80211_vif) +
|
||||
sizeof(struct iwl_mvm_vif);
|
||||
struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL);
|
||||
struct ieee80211_bss_load_elem *bss_load;
|
||||
struct element *element;
|
||||
size_t ies_size = sizeof(struct cfg80211_bss_ies) + sizeof(*bss_load) + sizeof(element);
|
||||
struct cfg80211_bss_ies *ies;
|
||||
struct iwl_mvm_vif *mvmvif;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, vif);
|
||||
|
||||
mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
if (params->chan_load_by_us > 0) {
|
||||
ctx.channel_load_by_us = params->chan_load_by_us;
|
||||
mvmvif->link[0] = &mvm_link;
|
||||
}
|
||||
|
||||
link_conf.vif = vif;
|
||||
link_conf.chanreq.oper = params->chandef;
|
||||
bss.signal = DBM_TO_MBM(params->signal);
|
||||
|
||||
ies = kunit_kzalloc(test, ies_size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ies);
|
||||
ies->len = sizeof(*bss_load) + sizeof(struct element);
|
||||
|
||||
element = (void *)ies->data;
|
||||
element->datalen = sizeof(*bss_load);
|
||||
element->id = 11;
|
||||
|
||||
bss_load = (void *)element->data;
|
||||
bss_load->channel_util = params->channel_util;
|
||||
|
||||
rcu_assign_pointer(bss.ies, ies);
|
||||
rcu_assign_pointer(bss.beacon_ies, ies);
|
||||
}
|
||||
|
||||
static void test_link_grading(struct kunit *test)
|
||||
{
|
||||
const struct link_grading_case *params = test->param_value;
|
||||
unsigned int ret;
|
||||
|
||||
setup_link_conf(test);
|
||||
|
||||
rcu_read_lock();
|
||||
ret = iwl_mvm_get_link_grade(&link_conf);
|
||||
rcu_read_unlock();
|
||||
|
||||
KUNIT_EXPECT_EQ(test, ret, params->grade);
|
||||
|
||||
kunit_kfree(test, link_conf.vif);
|
||||
RCU_INIT_POINTER(bss.ies, NULL);
|
||||
}
|
||||
|
||||
static struct kunit_case link_grading_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite link_grading = {
|
||||
.name = "iwlmvm-link-grading",
|
||||
.test_cases = link_grading_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(link_grading);
|
||||
|
||||
static const struct valid_link_pair_case {
|
||||
const char *desc;
|
||||
bool bt;
|
||||
struct ieee80211_channel *chan_a;
|
||||
struct ieee80211_channel *chan_b;
|
||||
enum nl80211_chan_width cw_a;
|
||||
enum nl80211_chan_width cw_b;
|
||||
s32 sig_a;
|
||||
s32 sig_b;
|
||||
bool csa_a;
|
||||
bool valid;
|
||||
} valid_link_pair_cases[] = {
|
||||
{
|
||||
.desc = "HB + UHB, valid.",
|
||||
.chan_a = &chan_6ghz,
|
||||
.chan_b = &chan_5ghz,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "LB + HB, no BT.",
|
||||
.chan_a = &chan_2ghz,
|
||||
.chan_b = &chan_5ghz,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "LB + HB, with BT.",
|
||||
.bt = true,
|
||||
.chan_a = &chan_2ghz,
|
||||
.chan_b = &chan_5ghz,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "Same band",
|
||||
.chan_a = &chan_2ghz,
|
||||
.chan_b = &chan_2ghz,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: LB, 20 MHz, low",
|
||||
.chan_a = &chan_2ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_20,
|
||||
.sig_a = -68,
|
||||
.chan_b = &chan_5ghz,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 20 MHz, high",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_20,
|
||||
.sig_a = -66,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_20,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 40 MHz, low",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_40,
|
||||
.sig_a = -65,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_40,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 40 MHz, high",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_40,
|
||||
.sig_a = -63,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_40,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 80 MHz, low",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_80,
|
||||
.sig_a = -62,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_80,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 80 MHz, high",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_80,
|
||||
.sig_a = -60,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_80,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: UHB, 160 MHz, low",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_160,
|
||||
.sig_a = -59,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_160,
|
||||
.valid = false,
|
||||
},
|
||||
{
|
||||
.desc = "RSSI: HB, 160 MHz, high",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_160,
|
||||
.sig_a = -5,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_160,
|
||||
.valid = true,
|
||||
},
|
||||
{
|
||||
.desc = "CSA active",
|
||||
.chan_a = &chan_6ghz,
|
||||
.cw_a = NL80211_CHAN_WIDTH_160,
|
||||
.sig_a = -5,
|
||||
.chan_b = &chan_5ghz,
|
||||
.cw_b = NL80211_CHAN_WIDTH_160,
|
||||
.valid = false,
|
||||
/* same as previous entry with valid=true except for CSA */
|
||||
.csa_a = true,
|
||||
},
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc)
|
||||
|
||||
static void test_valid_link_pair(struct kunit *test)
|
||||
{
|
||||
const struct valid_link_pair_case *params = test->param_value;
|
||||
size_t vif_size = sizeof(struct ieee80211_vif) +
|
||||
sizeof(struct iwl_mvm_vif);
|
||||
struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL);
|
||||
struct iwl_trans *trans = kunit_kzalloc(test, sizeof(struct iwl_trans),
|
||||
GFP_KERNEL);
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm_link_sel_data link_a = {
|
||||
.chandef = &chandef_a,
|
||||
.link_id = 1,
|
||||
.signal = params->sig_a,
|
||||
};
|
||||
struct iwl_mvm_link_sel_data link_b = {
|
||||
.chandef = &chandef_b,
|
||||
.link_id = 5,
|
||||
.signal = params->sig_b,
|
||||
};
|
||||
struct ieee80211_bss_conf *conf;
|
||||
bool result;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, vif);
|
||||
KUNIT_ASSERT_NOT_NULL(test, trans);
|
||||
|
||||
chandef_a.chan = params->chan_a;
|
||||
chandef_b.chan = params->chan_b;
|
||||
|
||||
chandef_a.width = params->cw_a ?: NL80211_CHAN_WIDTH_20;
|
||||
chandef_b.width = params->cw_b ?: NL80211_CHAN_WIDTH_20;
|
||||
|
||||
mvm.trans = trans;
|
||||
|
||||
mvm.last_bt_notif.wifi_loss_low_rssi = params->bt;
|
||||
mvmvif->mvm = &mvm;
|
||||
|
||||
conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, conf);
|
||||
conf->chanreq.oper = chandef_a;
|
||||
conf->csa_active = params->csa_a;
|
||||
vif->link_conf[link_a.link_id] = (void __rcu *)conf;
|
||||
|
||||
conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, conf);
|
||||
conf->chanreq.oper = chandef_b;
|
||||
vif->link_conf[link_b.link_id] = (void __rcu *)conf;
|
||||
|
||||
wiphy_lock(&wiphy);
|
||||
result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
|
||||
wiphy_unlock(&wiphy);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, result, params->valid);
|
||||
|
||||
kunit_kfree(test, vif);
|
||||
kunit_kfree(test, trans);
|
||||
}
|
||||
|
||||
static struct kunit_case valid_link_pair_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(test_valid_link_pair, valid_link_pair_gen_params),
|
||||
{},
|
||||
};
|
||||
|
||||
static struct kunit_suite valid_link_pair = {
|
||||
.name = "iwlmvm-valid-link-pair",
|
||||
.test_cases = valid_link_pair_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(valid_link_pair);
|
||||
@@ -47,7 +47,6 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
|
||||
|
||||
static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
|
||||
struct ieee80211_vif *vif = mvm->p2p_device_vif;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
@@ -125,8 +124,6 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
|
||||
iwl_mvm_rm_aux_sta(mvm);
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(bss_vif))
|
||||
iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_ROC);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1769,9 +1769,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
|
||||
IWL_DEBUG_TX_REPLY(mvm,
|
||||
"Next reclaimed packet:%d\n",
|
||||
next_reclaimed);
|
||||
if (tid < IWL_MAX_TID_COUNT)
|
||||
iwl_mvm_count_mpdu(mvmsta, sta_id, 1,
|
||||
true, 0);
|
||||
} else {
|
||||
IWL_DEBUG_TX_REPLY(mvm,
|
||||
"NDP - don't update next_reclaimed\n");
|
||||
@@ -2150,13 +2147,10 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
|
||||
ba_res->tx_rate, false);
|
||||
}
|
||||
|
||||
if (mvmsta) {
|
||||
if (mvmsta)
|
||||
iwl_mvm_tx_airtime(mvm, mvmsta,
|
||||
le32_to_cpu(ba_res->wireless_time));
|
||||
|
||||
iwl_mvm_count_mpdu(mvmsta, sta_id,
|
||||
le16_to_cpu(ba_res->txed), true, 0);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,6 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
|
||||
if (WARN_ON(mvm->d3_test_active))
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Synchronous commands from this op-mode must hold
|
||||
* the mutex, this ensures we don't try to send two
|
||||
@@ -79,11 +74,6 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
|
||||
if (WARN_ON(mvm->d3_test_active))
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Only synchronous commands can wait for status,
|
||||
* we use WANT_SKB so the caller can't.
|
||||
|
||||
@@ -1179,16 +1179,11 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
static void iwl_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct iwl_trans *trans = pci_get_drvdata(pdev);
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
if (!trans)
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&trans_pcie->me_recheck_wk);
|
||||
|
||||
iwl_drv_stop(trans->drv);
|
||||
|
||||
iwl_trans_pcie_free(trans);
|
||||
iwl_pcie_gen1_2_remove(trans);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@@ -1260,7 +1255,7 @@ static int _iwl_pci_resume(struct device *device, bool restore)
|
||||
* won't really know how to recover.
|
||||
*/
|
||||
iwl_pcie_prepare_card_hw(trans);
|
||||
iwl_finish_nic_init(trans);
|
||||
iwl_trans_activate_nic(trans);
|
||||
iwl_op_mode_device_powered_off(trans->op_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -400,6 +400,11 @@ struct iwl_pcie_txqs {
|
||||
* @me_recheck_wk: worker to recheck WiAMT/CSME presence
|
||||
* @invalid_tx_cmd: invalid TX command buffer
|
||||
* @wait_command_queue: wait queue for sync commands
|
||||
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
|
||||
* The user should use iwl_trans_{alloc,free}_tx_cmd.
|
||||
* @dev_cmd_pool_name: name for the TX command allocation pool
|
||||
* @pm_support: set to true in start_hw if link pm is supported
|
||||
* @ltr_enabled: set to true if the LTR is enabled
|
||||
*/
|
||||
struct iwl_trans_pcie {
|
||||
struct iwl_rxq *rxq;
|
||||
@@ -506,6 +511,12 @@ struct iwl_trans_pcie {
|
||||
struct iwl_dma_ptr invalid_tx_cmd;
|
||||
|
||||
wait_queue_head_t wait_command_queue;
|
||||
|
||||
struct kmem_cache *dev_cmd_pool;
|
||||
char dev_cmd_pool_name[50];
|
||||
|
||||
bool pm_support;
|
||||
bool ltr_enabled;
|
||||
};
|
||||
|
||||
static inline struct iwl_trans_pcie *
|
||||
@@ -783,6 +794,23 @@ static inline u16 iwl_txq_gen1_tfd_tb_get_len(struct iwl_trans *trans,
|
||||
return le16_to_cpu(tb->hi_n_len) >> 4;
|
||||
}
|
||||
|
||||
static inline struct iwl_device_tx_cmd *
|
||||
iwl_pcie_gen1_2_alloc_tx_cmd(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
return kmem_cache_zalloc(trans_pcie->dev_cmd_pool, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static inline void
|
||||
iwl_pcie_gen1_2_free_tx_cmd(struct iwl_trans *trans,
|
||||
struct iwl_device_tx_cmd *dev_cmd)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
kmem_cache_free(trans_pcie->dev_cmd_pool, dev_cmd);
|
||||
}
|
||||
|
||||
void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
||||
struct sk_buff_head *skbs, bool is_flush);
|
||||
void iwl_pcie_set_q_ptrs(struct iwl_trans *trans, int txq_id, int ptr);
|
||||
@@ -818,6 +846,8 @@ static inline void _iwl_disable_interrupts(struct iwl_trans *trans)
|
||||
trans_pcie->fh_init_mask);
|
||||
iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD,
|
||||
trans_pcie->hw_init_mask);
|
||||
trans_pcie->fh_mask = 0;
|
||||
trans_pcie->hw_mask = 0;
|
||||
}
|
||||
IWL_DEBUG_ISR(trans, "Disabled interrupts\n");
|
||||
}
|
||||
@@ -1000,6 +1030,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
|
||||
} else {
|
||||
iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD,
|
||||
trans_pcie->fh_init_mask);
|
||||
trans_pcie->fh_mask = 0;
|
||||
iwl_enable_hw_int_msk_msix(trans,
|
||||
MSIX_HW_INT_CAUSES_REG_RF_KILL);
|
||||
}
|
||||
@@ -1047,7 +1078,7 @@ static inline void iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) { }
|
||||
void iwl_pcie_rx_allocator_work(struct work_struct *data);
|
||||
|
||||
/* common trans ops for all generations transports */
|
||||
void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans);
|
||||
void iwl_pcie_gen1_2_op_mode_enter(struct iwl_trans *trans);
|
||||
int _iwl_trans_pcie_start_hw(struct iwl_trans *trans);
|
||||
int iwl_trans_pcie_start_hw(struct iwl_trans *trans);
|
||||
void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans);
|
||||
@@ -1064,9 +1095,8 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
|
||||
const struct iwl_dump_sanitize_ops *sanitize_ops,
|
||||
void *sanitize_ctx);
|
||||
int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
|
||||
enum iwl_d3_status *status,
|
||||
bool test, bool reset);
|
||||
int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset);
|
||||
bool reset);
|
||||
int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool reset);
|
||||
void iwl_trans_pci_interrupts(struct iwl_trans *trans, bool enable);
|
||||
void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans);
|
||||
void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
|
||||
@@ -1081,6 +1111,7 @@ int iwl_pci_gen1_2_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent,
|
||||
const struct iwl_mac_cfg *mac_cfg,
|
||||
u8 __iomem *hw_base, u32 hw_rev);
|
||||
void iwl_pcie_gen1_2_remove(struct iwl_trans *trans);
|
||||
|
||||
/* transport gen 1 exported functions */
|
||||
void iwl_trans_pcie_fw_alive(struct iwl_trans *trans);
|
||||
@@ -1105,6 +1136,7 @@ int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans,
|
||||
struct iwl_dma_ptr *ptr, size_t size);
|
||||
void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr);
|
||||
void iwl_pcie_apply_destination(struct iwl_trans *trans);
|
||||
int iwl_pcie_gen1_2_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
/* transport gen 2 exported functions */
|
||||
int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
|
||||
@@ -1124,4 +1156,17 @@ int iwl_trans_pcie_copy_imr(struct iwl_trans *trans,
|
||||
int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue,
|
||||
struct iwl_trans_rxq_dma_data *data);
|
||||
|
||||
static inline bool iwl_pcie_gen1_is_pm_supported(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
return trans_pcie->pm_support;
|
||||
}
|
||||
|
||||
static inline bool iwl_pcie_gen1_2_is_ltr_enabled(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
return trans_pcie->ltr_enabled;
|
||||
}
|
||||
#endif /* __iwl_trans_int_pcie_h__ */
|
||||
|
||||
@@ -47,7 +47,7 @@ int iwl_pcie_gen2_apm_init(struct iwl_trans *trans)
|
||||
|
||||
iwl_pcie_apm_config(trans);
|
||||
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -25,13 +25,52 @@
|
||||
#include "fw/dbg.h"
|
||||
#include "fw/api/tx.h"
|
||||
#include "fw/acpi.h"
|
||||
#include "fw/api/tx.h"
|
||||
#include "mei/iwl-mei.h"
|
||||
#include "internal.h"
|
||||
#include "iwl-fh.h"
|
||||
#include "pcie/iwl-context-info-v2.h"
|
||||
#include "pcie/utils.h"
|
||||
|
||||
#define IWL_HOST_MON_BLOCK_PEMON 0x00
|
||||
#define IWL_HOST_MON_BLOCK_HIPM 0x22
|
||||
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_VEC0 0x00
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_VEC1 0x01
|
||||
#define IWL_HOST_MON_BLOCK_PEMON_WFPM 0x06
|
||||
|
||||
static void iwl_dump_host_monitor_block(struct iwl_trans *trans,
|
||||
u32 block, u32 vec, u32 iter)
|
||||
{
|
||||
int i;
|
||||
|
||||
IWL_ERR(trans, "Host monitor block 0x%x vector 0x%x\n", block, vec);
|
||||
iwl_write32(trans, CSR_MONITOR_CFG_REG, (block << 8) | vec);
|
||||
for (i = 0; i < iter; i++)
|
||||
IWL_ERR(trans, " value [iter %d]: 0x%08x\n",
|
||||
i, iwl_read32(trans, CSR_MONITOR_STATUS_REG));
|
||||
}
|
||||
|
||||
static void iwl_pcie_dump_host_monitor(struct iwl_trans *trans)
|
||||
{
|
||||
switch (trans->mac_cfg->device_family) {
|
||||
case IWL_DEVICE_FAMILY_22000:
|
||||
case IWL_DEVICE_FAMILY_AX210:
|
||||
IWL_ERR(trans, "CSR_RESET = 0x%x\n",
|
||||
iwl_read32(trans, CSR_RESET));
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC0, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC1, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
|
||||
IWL_HOST_MON_BLOCK_PEMON_WFPM, 15);
|
||||
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_HIPM,
|
||||
IWL_HOST_MON_BLOCK_PEMON_VEC0, 1);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* extended range in FW SRAM */
|
||||
#define IWL_FW_MEM_EXTENDED_START 0x40000
|
||||
#define IWL_FW_MEM_EXTENDED_END 0x57FFF
|
||||
@@ -175,13 +214,13 @@ void iwl_pcie_apm_config(struct iwl_trans *trans)
|
||||
iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_DISABLED);
|
||||
|
||||
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
|
||||
trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
|
||||
trans_pcie->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
|
||||
|
||||
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
|
||||
trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
|
||||
trans_pcie->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
|
||||
IWL_DEBUG_POWER(trans, "L1 %sabled - LTR %sabled\n",
|
||||
(lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
|
||||
trans->ltr_enabled ? "En" : "Dis");
|
||||
trans_pcie->ltr_enabled ? "En" : "Dis");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -228,7 +267,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
|
||||
if (trans->mac_cfg->base->pll_cfg)
|
||||
iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL);
|
||||
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -301,7 +340,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
|
||||
ret = iwl_trans_pcie_sw_reset(trans, true);
|
||||
|
||||
if (!ret)
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
|
||||
if (WARN_ON(ret)) {
|
||||
/* Release XTAL ON request */
|
||||
@@ -1397,17 +1436,10 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq)
|
||||
}
|
||||
|
||||
static void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
|
||||
bool test, bool reset)
|
||||
bool reset)
|
||||
{
|
||||
iwl_disable_interrupts(trans);
|
||||
|
||||
/*
|
||||
* in testing mode, the host stays awake and the
|
||||
* hardware won't be reset (not even partially)
|
||||
*/
|
||||
if (test)
|
||||
return;
|
||||
|
||||
iwl_pcie_disable_ict(trans);
|
||||
|
||||
iwl_pcie_synchronize_irqs(trans);
|
||||
@@ -1478,7 +1510,7 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset)
|
||||
int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool reset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -1491,26 +1523,18 @@ int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl_pcie_d3_complete_suspend(trans, test, reset);
|
||||
iwl_pcie_d3_complete_suspend(trans, reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
|
||||
enum iwl_d3_status *status,
|
||||
bool test, bool reset)
|
||||
bool reset)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (test) {
|
||||
iwl_enable_interrupts(trans);
|
||||
*status = IWL_D3_STATUS_ALIVE;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
|
||||
@@ -1518,9 +1542,12 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
||||
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
if (ret)
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
if (ret) {
|
||||
IWL_ERR(trans, "Failed to init nic upon resume. err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconfigure IVAR table in case of MSIX or reset ict table in
|
||||
@@ -1554,18 +1581,13 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
|
||||
iwl_read_umac_prph(trans, WFPM_GP2));
|
||||
|
||||
val = iwl_read32(trans, CSR_RESET);
|
||||
if (val & CSR_RESET_REG_FLAG_NEVO_RESET)
|
||||
*status = IWL_D3_STATUS_RESET;
|
||||
else
|
||||
*status = IWL_D3_STATUS_ALIVE;
|
||||
|
||||
out:
|
||||
if (*status == IWL_D3_STATUS_ALIVE)
|
||||
ret = iwl_pcie_d3_handshake(trans, false);
|
||||
else
|
||||
if (val & CSR_RESET_REG_FLAG_NEVO_RESET) {
|
||||
IWL_INFO(trans, "Device was reset during suspend\n");
|
||||
trans->state = IWL_TRANS_NO_FW;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return iwl_pcie_d3_handshake(trans, false);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1744,7 +1766,7 @@ static int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iwl_finish_nic_init(trans);
|
||||
ret = iwl_trans_activate_nic(trans);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1882,7 +1904,7 @@ void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val)
|
||||
iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
|
||||
}
|
||||
|
||||
void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans)
|
||||
void iwl_pcie_gen1_2_op_mode_enter(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
@@ -2000,6 +2022,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
|
||||
free_percpu(trans_pcie->txqs.tso_hdr_page);
|
||||
}
|
||||
|
||||
kmem_cache_destroy(trans_pcie->dev_cmd_pool);
|
||||
iwl_trans_free(trans);
|
||||
}
|
||||
|
||||
@@ -3516,7 +3539,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,
|
||||
struct iwl_trans_dump_data *dump_data;
|
||||
u32 len, num_rbs = 0, monitor_len = 0;
|
||||
int i, ptr;
|
||||
bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
|
||||
bool dump_rbs = iwl_trans_is_fw_error(trans) &&
|
||||
!trans->mac_cfg->mq_rx_supported &&
|
||||
dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
|
||||
|
||||
@@ -3684,28 +3707,40 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans)
|
||||
iwl_trans_sync_nmi_with_addr(trans, inta_addr, sw_err_bit);
|
||||
}
|
||||
|
||||
static int iwl_trans_pcie_set_txcmd_info(const struct iwl_mac_cfg *mac_cfg,
|
||||
unsigned int *txcmd_size,
|
||||
unsigned int *txcmd_align)
|
||||
static int iwl_trans_pcie_alloc_txcmd_pool(struct iwl_trans *trans)
|
||||
{
|
||||
if (!mac_cfg->gen2) {
|
||||
*txcmd_size = sizeof(struct iwl_tx_cmd_v6);
|
||||
*txcmd_align = sizeof(void *);
|
||||
} else if (mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
|
||||
*txcmd_size = sizeof(struct iwl_tx_cmd_v9);
|
||||
*txcmd_align = 64;
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
unsigned int txcmd_size, txcmd_align;
|
||||
|
||||
if (!trans->mac_cfg->gen2) {
|
||||
txcmd_size = sizeof(struct iwl_tx_cmd_v6);
|
||||
txcmd_align = sizeof(void *);
|
||||
} else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
|
||||
txcmd_size = sizeof(struct iwl_tx_cmd_v9);
|
||||
txcmd_align = 64;
|
||||
} else {
|
||||
*txcmd_size = sizeof(struct iwl_tx_cmd);
|
||||
*txcmd_align = 128;
|
||||
txcmd_size = sizeof(struct iwl_tx_cmd);
|
||||
txcmd_align = 128;
|
||||
}
|
||||
|
||||
*txcmd_size += sizeof(struct iwl_cmd_header);
|
||||
*txcmd_size += 36; /* biggest possible 802.11 header */
|
||||
txcmd_size += sizeof(struct iwl_cmd_header);
|
||||
txcmd_size += 36; /* biggest possible 802.11 header */
|
||||
|
||||
/* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */
|
||||
if (WARN_ON((mac_cfg->gen2 && *txcmd_size >= *txcmd_align)))
|
||||
if (WARN_ON((trans->mac_cfg->gen2 && txcmd_size >= txcmd_align)))
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(trans_pcie->dev_cmd_pool_name,
|
||||
sizeof(trans_pcie->dev_cmd_pool_name),
|
||||
"iwl_cmd_pool:%s", dev_name(trans->dev));
|
||||
|
||||
trans_pcie->dev_cmd_pool =
|
||||
kmem_cache_create(trans_pcie->dev_cmd_pool_name,
|
||||
txcmd_size, txcmd_align,
|
||||
SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!trans_pcie->dev_cmd_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3715,18 +3750,12 @@ iwl_trans_pcie_alloc(struct pci_dev *pdev,
|
||||
struct iwl_trans_info *info, u8 __iomem *hw_base)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie, **priv;
|
||||
unsigned int txcmd_size, txcmd_align;
|
||||
struct iwl_trans *trans;
|
||||
unsigned int bc_tbl_n_entries;
|
||||
int ret, addr_size;
|
||||
|
||||
ret = iwl_trans_pcie_set_txcmd_info(mac_cfg, &txcmd_size,
|
||||
&txcmd_align);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev,
|
||||
mac_cfg, txcmd_size, txcmd_align);
|
||||
mac_cfg);
|
||||
if (!trans)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -3737,6 +3766,10 @@ iwl_trans_pcie_alloc(struct pci_dev *pdev,
|
||||
/* Initialize the wait queue for commands */
|
||||
init_waitqueue_head(&trans_pcie->wait_command_queue);
|
||||
|
||||
ret = iwl_trans_pcie_alloc_txcmd_pool(trans);
|
||||
if (ret)
|
||||
goto out_free_trans;
|
||||
|
||||
if (trans->mac_cfg->gen2) {
|
||||
trans_pcie->txqs.tfd.addr_size = 64;
|
||||
trans_pcie->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS;
|
||||
@@ -3756,7 +3789,7 @@ iwl_trans_pcie_alloc(struct pci_dev *pdev,
|
||||
trans_pcie->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
|
||||
if (!trans_pcie->txqs.tso_hdr_page) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_trans;
|
||||
goto out_free_txcmd_pool;
|
||||
}
|
||||
|
||||
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
|
||||
@@ -3906,6 +3939,8 @@ out_free_ndev:
|
||||
free_netdev(trans_pcie->napi_dev);
|
||||
out_free_tso:
|
||||
free_percpu(trans_pcie->txqs.tso_hdr_page);
|
||||
out_free_txcmd_pool:
|
||||
kmem_cache_destroy(trans_pcie->dev_cmd_pool);
|
||||
out_free_trans:
|
||||
iwl_trans_free(trans);
|
||||
return ERR_PTR(ret);
|
||||
@@ -3999,6 +4034,7 @@ static void get_crf_id(struct iwl_trans *iwl_trans,
|
||||
|
||||
/* Read cdb info (also contains the jacket info if needed in the future */
|
||||
hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR);
|
||||
|
||||
IWL_INFO(iwl_trans, "Detected crf-id 0x%x, cnv-id 0x%x wfpm id 0x%x\n",
|
||||
info->hw_crf_id, info->hw_cnv_id, hw_wfpm_id);
|
||||
}
|
||||
@@ -4014,10 +4050,8 @@ static int map_crf_id(struct iwl_trans *iwl_trans,
|
||||
u32 val = info->hw_crf_id;
|
||||
u32 step_id = REG_CRF_ID_STEP(val);
|
||||
u32 slave_id = REG_CRF_ID_SLAVE(val);
|
||||
u32 jacket_id_cnv = REG_CRF_ID_SLAVE(info->hw_cnv_id);
|
||||
u32 hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans,
|
||||
WFPM_OTP_CFG1_ADDR);
|
||||
u32 jacket_id_wfpm = WFPM_OTP_CFG1_IS_JACKET(hw_wfpm_id);
|
||||
u32 cdb_id_wfpm = WFPM_OTP_CFG1_IS_CDB(hw_wfpm_id);
|
||||
|
||||
/* Map between crf id to rf id */
|
||||
@@ -4066,21 +4100,12 @@ static int map_crf_id(struct iwl_trans *iwl_trans,
|
||||
IWL_INFO(iwl_trans, "Adding cdb to rf id\n");
|
||||
}
|
||||
|
||||
/* Set Jacket capabilities */
|
||||
if (jacket_id_wfpm || jacket_id_cnv) {
|
||||
info->hw_rf_id += BIT(29);
|
||||
IWL_INFO(iwl_trans, "Adding jacket to rf id\n");
|
||||
}
|
||||
|
||||
IWL_INFO(iwl_trans,
|
||||
"Detected rf-type 0x%x step-id 0x%x slave-id 0x%x from crf id 0x%x\n",
|
||||
REG_CRF_ID_TYPE(val), step_id, slave_id, info->hw_rf_id);
|
||||
IWL_INFO(iwl_trans,
|
||||
"Detected cdb-id 0x%x jacket-id 0x%x from wfpm id 0x%x\n",
|
||||
cdb_id_wfpm, jacket_id_wfpm, hw_wfpm_id);
|
||||
IWL_INFO(iwl_trans, "Detected jacket-id 0x%x from cnvi id 0x%x\n",
|
||||
jacket_id_cnv, info->hw_cnv_id);
|
||||
|
||||
"Detected cdb-id 0x%x from wfpm id 0x%x\n",
|
||||
cdb_id_wfpm, hw_wfpm_id);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -4163,7 +4188,7 @@ int iwl_pci_gen1_2_probe(struct pci_dev *pdev,
|
||||
*/
|
||||
ret = iwl_pcie_prepare_card_hw(iwl_trans);
|
||||
if (!ret) {
|
||||
ret = iwl_finish_nic_init(iwl_trans);
|
||||
ret = iwl_trans_activate_nic(iwl_trans);
|
||||
if (ret)
|
||||
goto out_free_trans;
|
||||
if (iwl_trans_grab_nic_access(iwl_trans)) {
|
||||
@@ -4271,3 +4296,63 @@ out_free_trans:
|
||||
iwl_trans_pcie_free(iwl_trans);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iwl_pcie_gen1_2_remove(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
cancel_delayed_work_sync(&trans_pcie->me_recheck_wk);
|
||||
|
||||
iwl_drv_stop(trans->drv);
|
||||
|
||||
iwl_trans_pcie_free(trans);
|
||||
}
|
||||
|
||||
int iwl_pcie_gen1_2_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg;
|
||||
u32 poll_ready;
|
||||
int err;
|
||||
|
||||
if (mac_cfg->bisr_workaround) {
|
||||
/* ensure the TOP FSM isn't still in previous reset */
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set "initialization complete" bit to move adapter from
|
||||
* D0U* --> D0A* (powered-up active) state.
|
||||
*/
|
||||
if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ |
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
|
||||
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
|
||||
} else {
|
||||
iwl_set_bit(trans, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
||||
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY;
|
||||
}
|
||||
|
||||
if (mac_cfg->device_family == IWL_DEVICE_FAMILY_8000)
|
||||
udelay(2);
|
||||
|
||||
/*
|
||||
* Wait for clock stabilization; once stabilized, access to
|
||||
* device-internal resources is supported, e.g. iwl_write_prph()
|
||||
* and accesses to uCode SRAM.
|
||||
*/
|
||||
err = iwl_poll_bits(trans, CSR_GP_CNTRL, poll_ready, 25000);
|
||||
if (err < 0) {
|
||||
IWL_DEBUG_INFO(trans, "Failed to wake NIC\n");
|
||||
|
||||
iwl_pcie_dump_host_monitor(trans);
|
||||
}
|
||||
|
||||
if (mac_cfg->bisr_workaround) {
|
||||
/* ensure BISR shift has finished */
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2602,8 +2602,9 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
||||
}
|
||||
|
||||
if (test_bit(STATUS_FW_ERROR, &trans->status)) {
|
||||
if (!test_and_clear_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE,
|
||||
&trans->status)) {
|
||||
if (trans->suppress_cmd_error_once) {
|
||||
trans->suppress_cmd_error_once = false;
|
||||
} else {
|
||||
IWL_ERR(trans, "FW error in SYNC CMD %s\n", cmd_str);
|
||||
dump_stack();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
|
||||
iwlwifi-tests-y += module.o devinfo.o utils.o
|
||||
iwlwifi-tests-y += module.o devinfo.o utils.o nvm_parse.o
|
||||
|
||||
ccflags-y += -I$(src)/../
|
||||
|
||||
|
||||
72
drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c
Normal file
72
drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* KUnit tests for NVM parse
|
||||
*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
*/
|
||||
#include <kunit/static_stub.h>
|
||||
#include <kunit/test.h>
|
||||
#include <iwl-nvm-parse.h>
|
||||
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
||||
static const struct nvm_flag_case {
|
||||
const char *desc;
|
||||
u16 nvm_flags;
|
||||
u32 reg_rule_flags;
|
||||
u32 set_reg_rule_flags;
|
||||
u32 clear_reg_rule_flags;
|
||||
} nvm_flag_cases[] = {
|
||||
{
|
||||
.desc = "Restricting VLP client and AP access",
|
||||
.nvm_flags = 0,
|
||||
.set_reg_rule_flags = NL80211_RRF_NO_6GHZ_VLP_CLIENT,
|
||||
.clear_reg_rule_flags = NL80211_RRF_ALLOW_6GHZ_VLP_AP,
|
||||
},
|
||||
{
|
||||
.desc = "Allow VLP client and AP access",
|
||||
.nvm_flags = NVM_CHANNEL_VLP,
|
||||
.set_reg_rule_flags = NL80211_RRF_ALLOW_6GHZ_VLP_AP,
|
||||
.clear_reg_rule_flags = NL80211_RRF_NO_6GHZ_VLP_CLIENT,
|
||||
},
|
||||
{
|
||||
.desc = "Allow VLP client access, while restricting AP access",
|
||||
.nvm_flags = NVM_CHANNEL_VLP | NVM_CHANNEL_VLP_AP_NOT_ALLOWED,
|
||||
.set_reg_rule_flags = 0,
|
||||
.clear_reg_rule_flags = NL80211_RRF_ALLOW_6GHZ_VLP_AP |
|
||||
NL80211_RRF_NO_6GHZ_VLP_CLIENT,
|
||||
},
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM_DESC(nvm_flag, nvm_flag_cases, desc)
|
||||
|
||||
static void test_nvm_flags(struct kunit *test)
|
||||
{
|
||||
const struct nvm_flag_case *params = test->param_value;
|
||||
struct iwl_reg_capa reg_capa = {};
|
||||
u32 flags = 0;
|
||||
|
||||
flags = iwl_nvm_get_regdom_bw_flags(NULL, 0, params->nvm_flags,
|
||||
reg_capa);
|
||||
|
||||
if ((params->set_reg_rule_flags & flags) != params->set_reg_rule_flags)
|
||||
KUNIT_FAIL(test, "Expected set bits:0x%08x flags:0x%08x\n",
|
||||
params->set_reg_rule_flags, flags);
|
||||
|
||||
if (params->clear_reg_rule_flags & flags)
|
||||
KUNIT_FAIL(test, "Expected clear bits:0x%08x flags:0x%08x\n",
|
||||
params->clear_reg_rule_flags, flags);
|
||||
}
|
||||
|
||||
static struct kunit_case nvm_flags_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(test_nvm_flags,
|
||||
nvm_flag_gen_params),
|
||||
{},
|
||||
};
|
||||
|
||||
static struct kunit_suite nvm_flags_suite = {
|
||||
.name = "iwlwifi-nvm_flags",
|
||||
.test_cases = nvm_flags_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(nvm_flags_suite);
|
||||
@@ -317,7 +317,7 @@ static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
|
||||
tim_len = tim[1];
|
||||
tim_ie = (struct ieee80211_tim_ie *) &tim[2];
|
||||
|
||||
new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
|
||||
new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid, false);
|
||||
if (new_psm != priv->powersave_override) {
|
||||
priv->powersave_override = new_psm;
|
||||
p54_set_ps(priv);
|
||||
|
||||
@@ -686,10 +686,9 @@ static void mwifiex_reg_notifier(struct wiphy *wiphy,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't send world or same regdom info to firmware */
|
||||
if (strncmp(request->alpha2, "00", 2) &&
|
||||
strncmp(request->alpha2, adapter->country_code,
|
||||
sizeof(request->alpha2))) {
|
||||
/* Don't send same regdom info to firmware */
|
||||
if (strncmp(request->alpha2, adapter->country_code,
|
||||
sizeof(request->alpha2)) != 0) {
|
||||
memcpy(adapter->country_code, request->alpha2,
|
||||
sizeof(request->alpha2));
|
||||
mwifiex_send_domain_info_cmd_fw(wiphy);
|
||||
|
||||
@@ -494,6 +494,11 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
|
||||
return;
|
||||
}
|
||||
|
||||
if (adapter->rgpower_data) {
|
||||
release_firmware(adapter->rgpower_data);
|
||||
adapter->rgpower_data = NULL;
|
||||
}
|
||||
|
||||
mwifiex_unregister(adapter);
|
||||
pr_debug("info: %s: free adapter\n", __func__);
|
||||
}
|
||||
|
||||
@@ -982,6 +982,7 @@ struct mwifiex_adapter {
|
||||
u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
|
||||
u16 max_mgmt_ie_index;
|
||||
const struct firmware *cal_data;
|
||||
const struct firmware *rgpower_data;
|
||||
struct device_node *dt_node;
|
||||
|
||||
/* 11AC */
|
||||
@@ -1579,6 +1580,8 @@ int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
|
||||
int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
|
||||
struct device_node *node, const char *prefix);
|
||||
void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
|
||||
int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data,
|
||||
const size_t size);
|
||||
|
||||
extern const struct ethtool_ops mwifiex_ethtool_ops;
|
||||
|
||||
|
||||
@@ -1483,6 +1483,119 @@ int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mwifiex_rgpower_table_advance_to_content(u8 **pos, const u8 *data,
|
||||
const size_t size)
|
||||
{
|
||||
while (*pos - data < size) {
|
||||
/* skip spaces, tabs and empty lines */
|
||||
if (**pos == '\r' || **pos == '\n' || **pos == '\0' ||
|
||||
isspace(**pos)) {
|
||||
(*pos)++;
|
||||
continue;
|
||||
}
|
||||
/* skip line comments */
|
||||
if (**pos == '#') {
|
||||
*pos = strchr(*pos, '\n');
|
||||
if (!*pos)
|
||||
return -EINVAL;
|
||||
(*pos)++;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data,
|
||||
const size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
bool start_raw = false;
|
||||
u8 *ptr, *token, *pos = NULL;
|
||||
u8 *_data __free(kfree) = NULL;
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
struct mwifiex_ds_misc_cmd *hostcmd __free(kfree) = NULL;
|
||||
|
||||
hostcmd = kzalloc(sizeof(*hostcmd), GFP_KERNEL);
|
||||
if (!hostcmd)
|
||||
return -ENOMEM;
|
||||
|
||||
_data = kmemdup(data, size, GFP_KERNEL);
|
||||
if (!_data)
|
||||
return -ENOMEM;
|
||||
|
||||
pos = _data;
|
||||
ptr = hostcmd->cmd;
|
||||
while ((pos - _data) < size) {
|
||||
ret = mwifiex_rgpower_table_advance_to_content(&pos, _data, size);
|
||||
if (ret) {
|
||||
mwifiex_dbg(
|
||||
adapter, ERROR,
|
||||
"%s: failed to advance to content in rgpower table\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (*pos == '}' && start_raw) {
|
||||
hostcmd->len = get_unaligned_le16(&hostcmd->cmd[2]);
|
||||
ret = mwifiex_send_cmd(priv, 0, 0, 0, hostcmd, false);
|
||||
if (ret) {
|
||||
mwifiex_dbg(adapter, ERROR,
|
||||
"%s: failed to send hostcmd %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(hostcmd->cmd, 0, MWIFIEX_SIZE_OF_CMD_BUFFER);
|
||||
ptr = hostcmd->cmd;
|
||||
start_raw = false;
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!start_raw) {
|
||||
pos = strchr(pos, '=');
|
||||
if (pos) {
|
||||
pos = strchr(pos, '{');
|
||||
if (pos) {
|
||||
start_raw = true;
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mwifiex_dbg(adapter, ERROR,
|
||||
"%s: syntax error in hostcmd\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (start_raw) {
|
||||
while ((*pos != '\r' && *pos != '\n') &&
|
||||
(token = strsep((char **)&pos, " "))) {
|
||||
if (ptr - hostcmd->cmd >=
|
||||
MWIFIEX_SIZE_OF_CMD_BUFFER) {
|
||||
mwifiex_dbg(
|
||||
adapter, ERROR,
|
||||
"%s: hostcmd is larger than %d, aborting\n",
|
||||
__func__, MWIFIEX_SIZE_OF_CMD_BUFFER);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = kstrtou8(token, 16, ptr);
|
||||
if (ret < 0) {
|
||||
mwifiex_dbg(
|
||||
adapter, ERROR,
|
||||
"%s: failed to parse hostcmd %d token: %s\n",
|
||||
__func__, ret, token);
|
||||
return ret;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function prepares command of set_cfg_data. */
|
||||
static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
|
||||
struct host_cmd_ds_command *cmd, void *data_buf)
|
||||
|
||||
@@ -180,7 +180,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
|
||||
return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
|
||||
}
|
||||
|
||||
void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
|
||||
static void mwifiex_dnld_dt_txpwr_table(struct mwifiex_private *priv)
|
||||
{
|
||||
if (priv->adapter->dt_node) {
|
||||
char txpwr[] = {"marvell,00_txpwrlimit"};
|
||||
@@ -190,6 +190,62 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
|
||||
{
|
||||
struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg;
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
char rgpower_table_name[30];
|
||||
char country_code[3];
|
||||
|
||||
strscpy(country_code, domain_info->country_code, sizeof(country_code));
|
||||
|
||||
/* World regulatory domain "00" has WW as country code */
|
||||
if (strncmp(country_code, "00", 2) == 0)
|
||||
strscpy(country_code, "WW", sizeof(country_code));
|
||||
|
||||
snprintf(rgpower_table_name, sizeof(rgpower_table_name),
|
||||
"nxp/rgpower_%s.bin", country_code);
|
||||
|
||||
mwifiex_dbg(adapter, INFO, "info: %s: requesting regulatory power table %s\n",
|
||||
__func__, rgpower_table_name);
|
||||
|
||||
if (adapter->rgpower_data) {
|
||||
release_firmware(adapter->rgpower_data);
|
||||
adapter->rgpower_data = NULL;
|
||||
}
|
||||
|
||||
if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
|
||||
adapter->dev))) {
|
||||
mwifiex_dbg(
|
||||
adapter, INFO,
|
||||
"info: %s: failed to request regulatory power table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mwifiex_request_rgpower_table(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mwifiex_send_rgpower_table(priv, priv->adapter->rgpower_data->data,
|
||||
priv->adapter->rgpower_data->size);
|
||||
}
|
||||
|
||||
void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
|
||||
{
|
||||
if (mwifiex_dnld_rgpower_table(priv) == 0)
|
||||
return;
|
||||
|
||||
mwifiex_dnld_dt_txpwr_table(priv);
|
||||
}
|
||||
|
||||
static int mwifiex_process_country_ie(struct mwifiex_private *priv,
|
||||
struct cfg80211_bss *bss)
|
||||
{
|
||||
|
||||
@@ -794,12 +794,6 @@ static int get_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int change_bss(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct bss_parameters *params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_wiphy_params(struct wiphy *wiphy, int radio_idx, u32 changed)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
@@ -1709,7 +1703,6 @@ static const struct cfg80211_ops wilc_cfg80211_ops = {
|
||||
.change_station = change_station,
|
||||
.get_station = get_station,
|
||||
.dump_station = dump_station,
|
||||
.change_bss = change_bss,
|
||||
.set_wiphy_params = set_wiphy_params,
|
||||
|
||||
.external_auth = external_auth,
|
||||
|
||||
@@ -679,7 +679,7 @@ static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev,
|
||||
/* Check whenever the PHY can be turned off again. */
|
||||
|
||||
/* 1. What about buffered unicast traffic for our AID? */
|
||||
cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid);
|
||||
cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid, false);
|
||||
|
||||
/* 2. Maybe the AP wants to send multicast/broadcast data? */
|
||||
cam |= (tim_ie->bitmap_ctrl & 0x01);
|
||||
|
||||
@@ -519,7 +519,7 @@ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
|
||||
|
||||
/* 1. What about buffered unicast traffic for our AID? */
|
||||
u_buffed = ieee80211_check_tim(tim_ie, tim_len,
|
||||
rtlpriv->mac80211.assoc_id);
|
||||
rtlpriv->mac80211.assoc_id, false);
|
||||
|
||||
/* 2. Maybe the AP wants to send multicast/broadcast data? */
|
||||
m_buffed = tim_ie->bitmap_ctrl & 0x01;
|
||||
|
||||
@@ -2441,13 +2441,6 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cfg80211_rtw_change_bss(struct wiphy *wiphy,
|
||||
struct net_device *ndev,
|
||||
struct bss_parameters *params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtw_cfg80211_rx_action(struct adapter *adapter, u8 *frame, uint frame_len, const char *msg)
|
||||
{
|
||||
s32 freq;
|
||||
@@ -2704,7 +2697,6 @@ static struct cfg80211_ops rtw_cfg80211_ops = {
|
||||
.del_station = cfg80211_rtw_del_station,
|
||||
.change_station = cfg80211_rtw_change_station,
|
||||
.dump_station = cfg80211_rtw_dump_station,
|
||||
.change_bss = cfg80211_rtw_change_bss,
|
||||
|
||||
.mgmt_tx = cfg80211_rtw_mgmt_tx,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user