Miri Korenblit says:
====================
iwlwifi-next - iwlwifi features

Mostly cleanups. A few fixes and small features.
====================

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg
2025-06-25 11:22:30 +02:00
69 changed files with 782 additions and 613 deletions

View File

@@ -12521,7 +12521,7 @@ M: Miri Korenblit <miriam.rachel.korenblit@intel.com>
L: linux-wireless@vger.kernel.org
S: Supported
W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
T: git https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next.git/
F: drivers/net/wireless/intel/iwlwifi/
INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER

View File

@@ -97,6 +97,7 @@ config IWLWIFI_OPMODE_MODULAR
default y if IWLDVM=m
default y if IWLMVM=m
default y if IWLMLD=m
default y if IWLWIFI_KUNIT_TESTS=m
comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD"
depends on IWLDVM=n && IWLMVM=n && IWLMLD=n

View File

@@ -7,9 +7,11 @@ iwlwifi-objs += iwl-debug.o
iwlwifi-objs += iwl-nvm-utils.o
iwlwifi-objs += iwl-utils.o
iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o
iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-v2.o
iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o
# Bus
iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-v2.o pcie/drv.o pcie/utils.o
iwlwifi-objs += pcie/gen1_2/rx.o pcie/gen1_2/tx.o pcie/gen1_2/trans.o
iwlwifi-objs += pcie/gen1_2/trans-gen2.o pcie/gen1_2/tx-gen2.o
CFLAGS_pcie/drv.o += -Wno-override-init

View File

@@ -13,7 +13,7 @@
#define IWL_BZ_UCODE_API_MAX 99
/* Lowest firmware API version supported */
#define IWL_BZ_UCODE_API_MIN 93
#define IWL_BZ_UCODE_API_MIN 94
/* Memory offsets and lengths */
#define IWL_BZ_SMEM_OFFSET 0x400000

View File

@@ -12,7 +12,7 @@
#define IWL_DR_UCODE_API_MAX 99
/* Lowest firmware API version supported */
#define IWL_DR_UCODE_API_MIN 97
#define IWL_DR_UCODE_API_MIN 98
/* Memory offsets and lengths */
#define IWL_DR_SMEM_OFFSET 0x400000

View File

@@ -13,7 +13,7 @@
#define IWL_SC_UCODE_API_MAX 99
/* Lowest firmware API version supported */
#define IWL_SC_UCODE_API_MIN 97
#define IWL_SC_UCODE_API_MIN 98
/* NVM versions */
#define IWL_SC_NVM_VERSION 0x0a1d

View File

@@ -397,6 +397,8 @@ static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state)
* returns a (newly allocated) struct containing all the
* relevant values for driver use. The struct must be freed
* later with iwl_free_nvm_data().
*
* Return: the parsed NVM data
*/
struct iwl_nvm_data *
iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2005-2014, 2023-2024 Intel Corporation
* Copyright (C) 2005-2014, 2023-2025 Intel Corporation
*/
/*
* Please use this file (commands.h) only for uCode API definitions.
@@ -614,7 +614,7 @@ struct iwl_rxon_time_cmd {
* REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response)
*/
/**
* struct iwl5000_channel_switch_cmd
* struct iwl5000_channel_switch_cmd - channel switch command (5000 series)
* @band: 0- 5.2GHz, 1- 2.4GHz
* @expect_beacon: 0- resume transmits after channel switch
* 1- wait for beacon to resume transmits
@@ -635,7 +635,7 @@ struct iwl5000_channel_switch_cmd {
} __packed;
/**
* struct iwl6000_channel_switch_cmd
* struct iwl6000_channel_switch_cmd - channel switch command (6000 series)
* @band: 0- 5.2GHz, 1- 2.4GHz
* @expect_beacon: 0- resume transmits after channel switch
* 1- wait for beacon to resume transmits
@@ -791,7 +791,7 @@ struct iwl_keyinfo {
} __packed;
/**
* struct sta_id_modify
* struct sta_id_modify - station modify command
* @addr: station's MAC address
* @reserved1: reserved for alignment
* @sta_id: index of station in uCode's station table
@@ -2026,7 +2026,7 @@ struct iwl_spectrum_notification {
u8 channel;
u8 type; /* see enum iwl_measurement_type */
u8 reserved1;
/* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only
/* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only
* valid if applicable for measurement type requested. */
__le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */
__le32 cca_cck; /* cca fraction time in 44Mhz clock periods */
@@ -2992,7 +2992,7 @@ struct iwl_missed_beacon_notif {
#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1)
/**
* struct iwl_sensitivity_cmd
* struct iwl_sensitivity_cmd - sensitivity configuration command
* @control: (1) updates working table, (0) updates default table
* @table: energy threshold values, use HD_* as index into table
*
@@ -3848,7 +3848,7 @@ struct iwlagn_wowlan_status {
#define IWL_MIN_SLOT_TIME 20
/**
* struct iwl_wipan_slot
* struct iwl_wipan_slot - WiPAN slot configuration
* @width: Time in TU
* @type:
* 0 - BSS
@@ -3868,7 +3868,7 @@ struct iwl_wipan_slot {
#define IWL_WIPAN_PARAMS_FLG_FULL_SLOTTED_MODE BIT(5)
/**
* struct iwl_wipan_params_cmd
* struct iwl_wipan_params_cmd - WiPAN parameters
* @flags:
* bit0: reserved
* bit1: CP leave channel with CTS

View File

@@ -104,7 +104,7 @@ struct iwl_qos_info {
};
/**
* enum iwl_agg_state
* enum iwl_agg_state - aggregation state
*
* The state machine of the BA agreement establishment / tear down.
* These states relate to a specific RA / TID.
@@ -519,7 +519,7 @@ enum iwl_scan_type {
};
/**
* struct iwl_hw_params
* struct iwl_hw_params - HW parameters
*
* Holds the module parameters
*

View File

@@ -55,6 +55,7 @@ static void iwl1000_nic_config(struct iwl_priv *priv)
* iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
* @priv: pointer to iwl_priv data structure
* @tsf_bits: number of bits need to shift for masking)
* Return: low 32 bits of beacon time mask
*/
static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
u16 tsf_bits)
@@ -66,6 +67,7 @@ static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv,
* iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time
* @priv: pointer to iwl_priv data structure
* @tsf_bits: number of bits need to shift for masking)
* Return: high 32 bits of beacon time mask
*/
static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv,
u16 tsf_bits)

View File

@@ -1049,9 +1049,11 @@ static void iwl_bg_restart(struct work_struct *data)
*
*****************************************************************************/
static void iwl_setup_deferred_work(struct iwl_priv *priv)
static int iwl_setup_deferred_work(struct iwl_priv *priv)
{
priv->workqueue = alloc_ordered_workqueue(DRV_NAME, 0);
if (!priv->workqueue)
return -ENOMEM;
INIT_WORK(&priv->restart, iwl_bg_restart);
INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
@@ -1068,6 +1070,8 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
timer_setup(&priv->statistics_periodic, iwl_bg_statistics_periodic, 0);
timer_setup(&priv->ucode_trace, iwl_bg_ucode_trace, 0);
return 0;
}
void iwl_cancel_deferred_work(struct iwl_priv *priv)
@@ -1463,7 +1467,9 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
/********************
* 6. Setup services
********************/
iwl_setup_deferred_work(priv);
if (iwl_setup_deferred_work(priv))
goto out_uninit_drv;
iwl_setup_rx_handlers(priv);
iwl_power_initialize(priv);
@@ -1502,6 +1508,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
iwl_cancel_deferred_work(priv);
destroy_workqueue(priv->workqueue);
priv->workqueue = NULL;
out_uninit_drv:
iwl_uninit_drv(priv);
out_free_eeprom_blob:
kfree(priv->eeprom_blob);

View File

@@ -23,6 +23,4 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
int iwl_power_update_mode(struct iwl_priv *priv, bool force);
void iwl_power_initialize(struct iwl_priv *priv);
extern bool no_sleep_autoadjust;
#endif /* __iwl_power_setting_h__ */

View File

@@ -2899,7 +2899,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
/* Repeat initial/next rate.
* For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
* For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
while (repeat_rate > 0 && index < (LINK_QUAL_MAX_RETRY_NUM - 1)) {
if (is_legacy(tbl_type.lq_type)) {
if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
ant_toggle_cnt++;

View File

@@ -429,7 +429,7 @@ static void iwlagn_rx_statistics(struct iwl_priv *priv,
* thermal update even if the uCode doesn't give
* us one */
mod_timer(&priv->statistics_periodic, jiffies +
msecs_to_jiffies(reg_recalib_period * 1000));
secs_to_jiffies(reg_recalib_period));
if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
(pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {

View File

@@ -232,6 +232,8 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
* that may be %NULL, for example during TX or key setup. In
* that case, we need to use the broadcast station, so this
* inline wraps that pattern.
*
* Return: station ID for mac80211 station (or broadcast if %NULL)
*/
static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context,
struct ieee80211_sta *sta)

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -19,9 +19,11 @@ enum iwl_d0i3_flags {
/**
* enum iwl_d3_wakeup_flags - D3 manager wakeup flags
* @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
* @IWL_WAKEUP_D3_HOST_TIMER: wake up on host timer expiry
*/
enum iwl_d3_wakeup_flags {
IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
IWL_WAKEUP_D3_HOST_TIMER = BIT(1),
}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
/**

View File

@@ -864,7 +864,7 @@ struct iwl_extended_beacon_notif {
/**
* enum iwl_dump_control - dump (flush) control flags
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the FIFO is empty
* and the TFD queues are empty.
*/
enum iwl_dump_control {

View File

@@ -1106,6 +1106,7 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt,
u32 prph_val;
u32 dphy_state;
u32 dphy_addr;
u32 prph_stts;
int i;
range->internal_base_addr = cpu_to_le32(addr);
@@ -1133,6 +1134,21 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt,
iwl_write_prph_no_grab(fwrt->trans, indirect_wr_addr,
WMAL_INDRCT_CMD(addr + i));
if (fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF1 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF2 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR1 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR2) {
udelay(2);
prph_stts = iwl_read_prph_no_grab(fwrt->trans,
WMAL_MRSPF_STTS);
/* Abort dump if status is 0xA5A5A5A2 or FIFO1 empty */
if (prph_stts == WMAL_TIMEOUT_VAL ||
!WMAL_MRSPF_STTS_IS_FIFO1_NOT_EMPTY(prph_stts))
break;
}
prph_val = iwl_read_prph_no_grab(fwrt->trans,
indirect_rd_addr);
*val++ = cpu_to_le32(prph_val);
@@ -3008,6 +3024,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
struct iwl_fw_dump_desc *desc;
unsigned int delay = 0;
bool monitor_only = false;
int ret;
if (trigger) {
u16 occurrences = le16_to_cpu(trigger->occurrences) - 1;
@@ -3038,7 +3055,11 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
desc->trig_desc.type = cpu_to_le32(trig);
memcpy(desc->trig_desc.data, str, len);
return iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay);
ret = iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay);
if (ret)
kfree(desc);
return ret;
}
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect);
@@ -3046,7 +3067,7 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
struct iwl_fw_dbg_trigger_tlv *trigger,
const char *fmt, ...)
{
int ret, len = 0;
int len = 0;
char buf[64];
if (iwl_trans_dbg_ini_valid(fwrt->trans))
@@ -3068,13 +3089,8 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
len = strlen(buf) + 1;
}
ret = iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len,
trigger);
if (ret)
return ret;
return 0;
return iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len,
trigger);
}
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_trig);

View File

@@ -198,7 +198,7 @@ void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
iwl_fw_cancel_timestamp(fwrt);
fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
fwrt->timestamp.delay = secs_to_jiffies(delay);
schedule_delayed_work(&fwrt->timestamp.wk,
round_jiffies_relative(fwrt->timestamp.delay));

View File

@@ -53,8 +53,8 @@ struct iwl_ucode_capabilities {
u32 num_stations;
u32 num_links;
u32 num_beacons;
unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
DECLARE_BITMAP(_api, NUM_IWL_UCODE_TLV_API);
DECLARE_BITMAP(_capa, NUM_IWL_UCODE_TLV_CAPA);
const struct iwl_fw_cmd_version *cmd_versions;
u32 n_cmd_versions;

View File

@@ -332,7 +332,7 @@ iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
ret = iwl_trans_load_pnvm(trans, pnvm_data, capa);
if (ret)
goto free;
IWL_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version);
IWL_DEBUG_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version);
set:
iwl_trans_set_pnvm(trans, capa);

View File

@@ -113,6 +113,7 @@
#define CSR_IPC_STATE_RESET_SW_READY 1
#define CSR_IPC_STATE_RESET_TOP_READY 2
#define CSR_IPC_STATE_RESET_TOP_FOLLOWER 3
#define CSR_IPC_STATE_TOP_RESET_REQ BIT(6)
#define CSR_IPC_SLEEP_CONTROL (CSR_BASE + 0x114)
#define CSR_IPC_SLEEP_CONTROL_SUSPEND 0x3

View File

@@ -1276,8 +1276,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(*fseq_ver))
goto invalid_tlv_len;
IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
fseq_ver->version);
IWL_DEBUG_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
fseq_ver->version);
}
break;
case IWL_UCODE_TLV_FW_NUM_STATIONS:

View File

@@ -160,23 +160,26 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
* @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_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),
};
/**
@@ -1044,6 +1047,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
case IWL_CFG_RF_TYPE_GF:
case IWL_CFG_RF_TYPE_FM:
case IWL_CFG_RF_TYPE_WH:
case IWL_CFG_RF_TYPE_PE:
iftype_data->he_cap.he_cap_elem.phy_cap_info[9] |=
IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU;
if (!is_ap)
@@ -1629,8 +1633,7 @@ 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,
const struct iwl_rf_cfg *cfg)
struct iwl_reg_capa reg_capa)
{
u32 flags = NL80211_RRF_NO_HT40;
@@ -1685,10 +1688,12 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
}
/* Set the AP type for the UHB case. */
if (nvm_flags & NVM_CHANNEL_VLP)
flags |= NL80211_RRF_ALLOW_6GHZ_VLP_AP;
else
if (nvm_flags & NVM_CHANNEL_VLP) {
if (!(nvm_flags & NVM_CHANNEL_VLP_AP_NOT_ALLOWED))
flags |= NL80211_RRF_ALLOW_6GHZ_VLP_AP;
} else {
flags |= NL80211_RRF_NO_6GHZ_VLP_CLIENT;
}
if (!(nvm_flags & NVM_CHANNEL_AFC))
flags |= NL80211_RRF_NO_6GHZ_AFC_CLIENT;
@@ -1815,8 +1820,8 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
}
reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
ch_flags, reg_capa,
cfg);
ch_flags,
reg_capa);
/* we can't continue the same rule */
if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags ||

View File

@@ -147,6 +147,8 @@ struct iwl_fw_error_dump_mode {
* Op_mode needs to reset its internal state because the device did not
* survive the system state transition. The firmware is no longer running,
* etc...
* @dump: Op_mode needs to collect the firmware dump upon this handler
* being called.
*/
struct iwl_op_mode_ops {
struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@@ -174,6 +176,7 @@ struct iwl_op_mode_ops {
enum iwl_fw_ini_time_point tp_id,
union iwl_dbg_tlv_tp_data *tp_data);
void (*device_powered_off)(struct iwl_op_mode *op_mode);
void (*dump)(struct iwl_op_mode *op_mode);
};
int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops);
@@ -286,4 +289,11 @@ static inline void iwl_op_mode_device_powered_off(struct iwl_op_mode *op_mode)
op_mode->ops->device_powered_off(op_mode);
}
static inline void iwl_op_mode_dump(struct iwl_op_mode *op_mode)
{
if (!op_mode || !op_mode->ops || !op_mode->ops->dump)
return;
op_mode->ops->dump(op_mode);
}
#endif /* __iwl_op_mode_h__ */

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2005-2014, 2018-2024 Intel Corporation
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016 Intel Deutschland GmbH
*/
@@ -514,6 +514,14 @@ enum {
#define WMAL_INDRCT_CMD(addr) \
((WMAL_CMD_READ_BURST_ACCESS << WMAL_INDRCT_RD_CMD1_OPMOD_POS) | \
((addr) & WMAL_INDRCT_RD_CMD1_BYTE_ADDRESS_MSK))
#define WMAL_MRSPF_STTS 0xADFC24
#define WMAL_MRSPF_STTS_FIFO1_NOT_EMPTY_POS 15
#define WMAL_MRSPF_STTS_FIFO1_NOT_EMPTY_MSK 0x8000
#define WMAL_TIMEOUT_VAL 0xA5A5A5A2
#define WMAL_MRSPF_STTS_IS_FIFO1_NOT_EMPTY(val) \
(((val) >> (WMAL_MRSPF_STTS_FIFO1_NOT_EMPTY_POS)) & \
((WMAL_MRSPF_STTS_FIFO1_NOT_EMPTY_MSK) >> \
(WMAL_MRSPF_STTS_FIFO1_NOT_EMPTY_POS)))
#define WFPM_LMAC1_PS_CTL_RW 0xA03380
#define WFPM_LMAC2_PS_CTL_RW 0xA033C0

View File

@@ -14,8 +14,8 @@
#include "iwl-fh.h"
#include <linux/dmapool.h>
#include "fw/api/commands.h"
#include "pcie/internal.h"
#include "iwl-context-info-v2.h"
#include "pcie/gen1_2/internal.h"
#include "pcie/iwl-context-info-v2.h"
struct iwl_trans_dev_restart_data {
struct list_head list;
@@ -497,7 +497,19 @@ IWL_EXPORT_SYMBOL(iwl_trans_read_mem);
int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
const void *buf, int dwords)
{
return iwl_trans_pcie_write_mem(trans, addr, buf, dwords);
int offs, ret = 0;
const u32 *vals = buf;
if (iwl_trans_grab_nic_access(trans)) {
iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
for (offs = 0; offs < dwords; offs++)
iwl_write32(trans, HBUS_TARG_MEM_WDAT,
vals ? vals[offs] : 0);
iwl_trans_release_nic_access(trans);
} else {
ret = -EBUSY;
}
return ret;
}
IWL_EXPORT_SYMBOL(iwl_trans_write_mem);

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#include <net/gso.h>
#include <linux/ieee80211.h>
@@ -82,3 +82,114 @@ int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
}
IWL_EXPORT_SYMBOL(iwl_tx_tso_segment);
#endif /* CONFIG_INET */
static u32 iwl_div_by_db(u32 value, u8 db)
{
/*
* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping
* at 10 dB and looping instead of using a much larger table.
*
* Using 64 bit math is overkill, but means the helper does not require
* a limit on the input range.
*/
static const u32 db_to_val[] = {
0xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,
0x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,
};
while (value && db > 0) {
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
value = (((u64)value) * db_to_val[change - 1]) >> 32;
db -= change;
}
return value;
}
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len)
{
int average_magnitude;
u32 average_factor;
int sum_magnitude = -128;
u32 sum_factor = 0;
int i, count = 0;
/*
* To properly average the decibel values (signal values given in dBm)
* we need to do the math in linear space. Doing a linear average of
* dB (dBm) values is a bit annoying though due to the large range of
* at least -10 to -110 dBm that will not fit into a 32 bit integer.
*
* A 64 bit integer should be sufficient, but then we still have the
* problem that there are no directly usable utility functions
* available.
*
* So, lets not deal with that and instead do much of the calculation
* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit
* gives us plenty of head-room for adding up a few values and even
* doing some math on it. And the tail should be accurate enough too
* (1/2^16 is somewhere around -48 dB, so effectively zero).
*
* i.e. the real value of sum is:
* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW
*
* However, that does mean we need to be able to bring two values to
* a common base, so we need a helper for that.
*
* Note that this function takes an input with unsigned negative dBm
* values but returns a signed dBm (i.e. a negative value).
*/
for (i = 0; i < len; i++) {
int val_magnitude;
u32 val_factor;
/* Assume invalid */
if (neg_dbm_values[i] == 0xff)
continue;
val_factor = 0x10000;
val_magnitude = -neg_dbm_values[i];
if (val_magnitude <= sum_magnitude) {
u8 div_db = sum_magnitude - val_magnitude;
val_factor = iwl_div_by_db(val_factor, div_db);
val_magnitude = sum_magnitude;
} else {
u8 div_db = val_magnitude - sum_magnitude;
sum_factor = iwl_div_by_db(sum_factor, div_db);
sum_magnitude = val_magnitude;
}
sum_factor += val_factor;
count++;
}
/* No valid noise measurement, return a very high noise level */
if (count == 0)
return 0;
average_magnitude = sum_magnitude;
average_factor = sum_factor / count;
/*
* average_factor will be a number smaller than 1.0 (0x10000) at this
* point. What we need to do now is to adjust average_magnitude so that
* average_factor is between -0.5 dB and 0.5 dB.
*
* Just do -1 dB steps and find the point where
* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB
* = div_by_db(0xe429, i)
* is smaller than average_factor.
*/
for (i = 0; average_factor < iwl_div_by_db(0xe429, i); i++) {
/* nothing */
}
return clamp(average_magnitude - i, -128, 0);
}
IWL_EXPORT_SYMBOL(iwl_average_neg_dbm);

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#ifndef __iwl_utils_h__
#define __iwl_utils_h__
@@ -53,4 +53,6 @@ u32 iwl_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size)
return ie - beacon;
}
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len);
#endif /* __iwl_utils_h__ */

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021 - 2022 Intel Corporation
* Copyright (C) 2021 - 2022, 2025 Intel Corporation
*/
#ifndef __sap_h__
@@ -340,12 +340,12 @@ enum iwl_sap_wifi_auth_type {
};
/**
* enum iwl_sap_wifi_cipher_alg
* @SAP_WIFI_CIPHER_ALG_NONE: TBD
* @SAP_WIFI_CIPHER_ALG_TKIP: TBD
* @SAP_WIFI_CIPHER_ALG_CCMP: TBD
* @SAP_WIFI_CIPHER_ALG_GCMP: TBD
* @SAP_WIFI_CIPHER_ALG_GCMP_256: TBD
* enum iwl_sap_wifi_cipher_alg - MEI WiFi cipher algorithm IDs
* @SAP_WIFI_CIPHER_ALG_NONE: No encryption
* @SAP_WIFI_CIPHER_ALG_TKIP: TKIPO
* @SAP_WIFI_CIPHER_ALG_CCMP: CCMP
* @SAP_WIFI_CIPHER_ALG_GCMP: GCMP-128
* @SAP_WIFI_CIPHER_ALG_GCMP_256: GCMP-256
*/
enum iwl_sap_wifi_cipher_alg {
SAP_WIFI_CIPHER_ALG_NONE = IWL_MEI_CIPHER_NONE,
@@ -601,7 +601,7 @@ enum iwl_sap_flex_filter_flags {
};
/**
* struct iwl_sap_flex_filter -
* struct iwl_sap_flex_filter - filter configuration
* @src_port: Source port in network format.
* @dst_port: Destination port in network format.
* @flags: Flags and protocol, see &enum iwl_sap_flex_filter_flags.
@@ -633,7 +633,7 @@ enum iwl_sap_ipv4_filter_flags {
};
/**
* struct iwl_sap_ipv4_filter-
* struct iwl_sap_ipv4_filter - IPv4 filter configuration
* @ipv4_addr: The IP address to filer.
* @flags: See &enum iwl_sap_ipv4_filter_flags.
*/
@@ -643,7 +643,7 @@ struct iwl_sap_ipv4_filter {
} __packed;
/**
* enum iwl_sap_ipv6_filter_flags -
* enum iwl_sap_ipv6_filter_flags - IPv6 filter flags
* @SAP_IPV6_ADDR_FILTER_COPY: Pass packets to the host.
* @SAP_IPV6_ADDR_FILTER_ENABLED: If false, the filter should be ignored.
*/
@@ -653,7 +653,7 @@ enum iwl_sap_ipv6_filter_flags {
};
/**
* struct iwl_sap_ipv6_filter -
* struct iwl_sap_ipv6_filter - IPv6 filter configuration
* @addr_lo24: Lowest 24 bits of the IPv6 address.
* @flags: See &enum iwl_sap_ipv6_filter_flags.
*/
@@ -663,7 +663,7 @@ struct iwl_sap_ipv6_filter {
} __packed;
/**
* enum iwl_sap_icmpv6_filter_flags -
* enum iwl_sap_icmpv6_filter_flags - ICMPv6 filter flags
* @SAP_ICMPV6_FILTER_ENABLED: If false, the filter should be ignored.
* @SAP_ICMPV6_FILTER_COPY: Pass packets to the host.
*/
@@ -673,8 +673,8 @@ enum iwl_sap_icmpv6_filter_flags {
};
/**
* enum iwl_sap_vlan_filter_flags -
* @SAP_VLAN_FILTER_VLAN_ID_MSK: TBD
* enum iwl_sap_vlan_filter_flags - VLAN filter flags
* @SAP_VLAN_FILTER_VLAN_ID_MSK: VLAN ID
* @SAP_VLAN_FILTER_ENABLED: If false, the filter should be ignored.
*/
enum iwl_sap_vlan_filter_flags {
@@ -751,7 +751,7 @@ struct iwl_sap_pldr_data {
} __packed;
/**
* enum iwl_sap_pldr_status -
* enum iwl_sap_pldr_status - product reset status
* @SAP_PLDR_STATUS_SUCCESS: PLDR started/ended successfully
* @SAP_PLDR_STATUS_FAILURE: PLDR failed to start/end
*/

View File

@@ -9,8 +9,4 @@ iwlmld-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
iwlmld-$(CONFIG_IWLWIFI_LEDS) += led.o
iwlmld-$(CONFIG_PM_SLEEP) += d3.o
# non-upstream things
iwlmld-$(CONFIG_IWL_VENDOR_CMDS) += vendor-cmd.o
iwlmld-$(CONFIG_IWLMVM_AX_SOFTAP_TESTMODE) += ax-softap-testmode.o
subdir-ccflags-y += -I$(src)/../

View File

@@ -204,66 +204,6 @@ void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
}
#endif
enum rt_status {
FW_ALIVE,
FW_NEEDS_RESET,
FW_ERROR,
};
static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld,
struct ieee80211_vif *vif)
{
u32 err_id;
/* check for lmac1 error */
if (iwl_fwrt_read_err_table(mld->trans,
mld->trans->dbg.lmac_error_event_table[0],
&err_id)) {
if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
struct cfg80211_wowlan_wakeup wakeup = {
.rfkill_release = true,
};
ieee80211_report_wowlan_wakeup(vif, &wakeup,
GFP_KERNEL);
return FW_NEEDS_RESET;
}
return FW_ERROR;
}
/* check if we have lmac2 set and check for error */
if (iwl_fwrt_read_err_table(mld->trans,
mld->trans->dbg.lmac_error_event_table[1],
NULL))
return FW_ERROR;
/* check for umac error */
if (iwl_fwrt_read_err_table(mld->trans,
mld->trans->dbg.umac_error_event_table,
NULL))
return FW_ERROR;
return FW_ALIVE;
}
static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld,
struct ieee80211_vif *vif)
{
enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif);
if (rt_status == FW_ALIVE)
return false;
if (rt_status == FW_ERROR) {
IWL_ERR(mld, "FW Error occurred during suspend\n");
iwl_fwrt_dump_error_logs(&mld->fwrt);
iwl_dbg_tlv_time_point(&mld->fwrt,
IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
}
return true;
}
static int
iwl_mld_netdetect_config(struct iwl_mld *mld,
struct ieee80211_vif *vif,
@@ -928,7 +868,7 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
return true;
}
static bool
static void
iwl_mld_add_all_rekeys(struct ieee80211_vif *vif,
struct iwl_mld_wowlan_status *wowlan_status,
struct iwl_mld_resume_key_iter_data *key_iter_data,
@@ -941,21 +881,19 @@ iwl_mld_add_all_rekeys(struct ieee80211_vif *vif,
&wowlan_status->gtk[i],
link_conf,
key_iter_data->gtk_cipher))
return false;
return;
if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
&wowlan_status->igtk,
link_conf, key_iter_data->igtk_cipher))
return false;
return;
for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++)
if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
&wowlan_status->bigtk[i],
link_conf,
key_iter_data->bigtk_cipher))
return false;
return true;
return;
}
static bool
@@ -1317,6 +1255,13 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld)
struct iwl_d3_manager_config d3_cfg_cmd_data = {};
int ret;
if (mld->debug_max_sleep) {
d3_cfg_cmd_data.wakeup_host_timer =
cpu_to_le32(mld->debug_max_sleep);
d3_cfg_cmd_data.wakeup_flags =
cpu_to_le32(IWL_WAKEUP_D3_HOST_TIMER);
}
lockdep_assert_wiphy(mld->wiphy);
IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n");
@@ -1376,10 +1321,7 @@ int iwl_mld_no_wowlan_resume(struct iwl_mld *mld)
mld->fw_status.in_d3 = false;
iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
if (iwl_mld_fw_needs_restart(mld, NULL))
ret = -ENODEV;
else
ret = iwl_mld_wait_d3_notif(mld, &resume_data, false);
ret = iwl_mld_wait_d3_notif(mld, &resume_data, false);
if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE))
return -ENODEV;
@@ -1928,15 +1870,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld)
iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
if (iwl_mld_fw_needs_restart(mld, bss_vif)) {
fw_err = true;
goto err;
}
resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status),
GFP_KERNEL);
if (!resume_data.wowlan_status)
return -1;
return -ENOMEM;
if (mld->netdetect)
resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO;

View File

@@ -546,6 +546,11 @@ iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
#endif
MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200);
#ifdef CONFIG_PM_SLEEP
debugfs_create_u32("max_sleep", 0600, debugfs_dir,
&mld->debug_max_sleep);
#endif
debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir,
&mld->monitor.ptp_time);

View File

@@ -94,7 +94,7 @@ iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld,
IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n",
peer->chandef.width);
return -EINVAL;
}
}
/* non EDCA based measurement must use HE preamble */
if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)

View File

@@ -55,6 +55,8 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
wiphy_delayed_work_cancel(mld->wiphy, &mld_vif->mlo_scan_start_wk);
CLEANUP_STRUCT(mld_vif);
}
@@ -385,6 +387,17 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
return iwl_mld_send_mac_cmd(mld, &cmd);
}
static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
struct wiphy_work *wk)
{
struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
mlo_scan_start_wk.work);
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
}
IWL_MLD_ALLOC_FN(vif, vif)
/* Constructor function for struct iwl_mld_vif */
@@ -412,6 +425,8 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
iwl_mld_emlsr_prevent_done_wk);
wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk,
iwl_mld_emlsr_tmp_non_bss_done_wk);
wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk,
iwl_mld_mlo_scan_start_wk);
}
iwl_mld_init_internal_sta(&mld_vif->aux_sta);

View File

@@ -87,6 +87,8 @@ enum iwl_mld_emlsr_exit {
* @last_exit_reason: Reason for the last EMLSR exit
* @last_exit_ts: Time of the last EMLSR exit (if @last_exit_reason is non-zero)
* @exit_repeat_count: Number of times EMLSR was exited for the same reason
* @last_entry_ts: the time of the last EMLSR entry (if iwl_mld_emlsr_active()
* is true)
* @unblock_tpt_wk: Unblock EMLSR because the throughput limit was reached
* @check_tpt_wk: a worker to check if IWL_MLD_EMLSR_BLOCKED_TPT should be
* added, for example if there is no longer enough traffic.
@@ -105,6 +107,7 @@ struct iwl_mld_emlsr {
enum iwl_mld_emlsr_exit last_exit_reason;
unsigned long last_exit_ts;
u8 exit_repeat_count;
unsigned long last_entry_ts;
);
struct wiphy_work unblock_tpt_wk;
@@ -133,6 +136,8 @@ struct iwl_mld_emlsr {
* @low_latency_causes: bit flags, indicating the causes for low-latency,
* see @iwl_mld_low_latency_cause.
* @ps_disabled: indicates that PS is disabled for this interface
* @last_link_activation_time: last time a link was activated, for
* deferring MLO scans (to make them more reliable)
* @mld: pointer to the mld structure.
* @deflink: default link data, for use in non-MLO,
* @link: reference to link data for each valid link, for use in MLO.
@@ -144,6 +149,7 @@ struct iwl_mld_emlsr {
* @roc_activity: the id of the roc_activity running. Relevant for STA and
* p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use.
* @aux_sta: station used for remain on channel. Used in P2P device.
* @mlo_scan_start_wk: worker to start a deferred MLO scan
*/
struct iwl_mld_vif {
/* Add here fields that need clean up on restart */
@@ -161,6 +167,7 @@ struct iwl_mld_vif {
#endif
u8 low_latency_causes;
bool ps_disabled;
time64_t last_link_activation_time;
);
/* And here fields that survive a fw restart */
struct iwl_mld *mld;
@@ -179,6 +186,8 @@ struct iwl_mld_vif {
#endif
enum iwl_roc_activity roc_activity;
struct iwl_mld_int_sta aux_sta;
struct wiphy_delayed_work mlo_scan_start_wk;
};
static inline struct iwl_mld_vif *
@@ -187,6 +196,12 @@ iwl_mld_vif_from_mac80211(struct ieee80211_vif *vif)
return (void *)vif->drv_priv;
}
static inline struct ieee80211_vif *
iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
{
return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
}
#define iwl_mld_link_dereference_check(mld_vif, link_id) \
rcu_dereference_check((mld_vif)->link[link_id], \
lockdep_is_held(&mld_vif->mld->wiphy->mtx))

View File

@@ -404,6 +404,7 @@ int iwl_mld_activate_link(struct iwl_mld *mld,
struct ieee80211_bss_conf *link)
{
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_link->vif);
int ret;
lockdep_assert_wiphy(mld->wiphy);
@@ -418,6 +419,9 @@ int iwl_mld_activate_link(struct iwl_mld *mld,
LINK_CONTEXT_MODIFY_ACTIVE);
if (ret)
mld_link->active = false;
else
mld_vif->last_link_activation_time =
ktime_get_boottime_seconds();
return ret;
}

View File

@@ -508,8 +508,15 @@ int iwl_mld_mac80211_start(struct ieee80211_hw *hw)
if (in_d3) {
/* mac80211 already cleaned up the state, no need for cleanup */
ret = iwl_mld_no_wowlan_resume(mld);
if (ret)
if (ret) {
iwl_mld_stop_fw(mld);
/* We're not really restarting in the sense of
* in_hw_restart even if we got an error during
* this. We'll just start again below and have
* nothing to recover, mac80211 will do anyway.
*/
mld->fw_status.in_hw_restart = false;
}
}
#endif /* CONFIG_PM_SLEEP */
@@ -1003,6 +1010,7 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
/* Indicate to mac80211 that EML is enabled */
vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE;
mld_vif->emlsr.last_entry_ts = jiffies;
if (vif->active_links & BIT(mld_vif->emlsr.selected_links))
mld_vif->emlsr.primary = mld_vif->emlsr.selected_primary;
@@ -1470,7 +1478,7 @@ void iwl_mld_mac80211_mgd_prepare_tx(struct ieee80211_hw *hw,
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
u32 duration = IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS;
/* After a successful association the connection is etalibeshed
/* After a successful association the connection is established
* and we can rely on the quota to send the disassociation frame.
*/
if (info->was_assoc)
@@ -2575,28 +2583,6 @@ static int iwl_mld_mac80211_tx_last_beacon(struct ieee80211_hw *hw)
return mld->ibss_manager;
}
#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (5 * HZ)
static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
int ret;
if (!iwl_mld_vif_has_emlsr_cap(vif))
return;
ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS,
iwl_mld_get_primary_link(vif));
if (ret)
return;
wiphy_delayed_work_queue(mld_vif->mld->wiphy,
&mld_vif->emlsr.tmp_non_bss_done_wk,
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT);
}
static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw,
enum nl80211_iftype type)
{
@@ -2609,10 +2595,7 @@ static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw,
type == NL80211_IFTYPE_P2P_CLIENT))
return;
ieee80211_iterate_active_interfaces_mtx(mld->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mld_vif_iter_emlsr_block_tmp_non_bss,
NULL);
iwl_mld_emlsr_block_tmp_non_bss(mld);
}
static int iwl_mld_set_hw_timestamp(struct ieee80211_hw *hw,
@@ -2642,6 +2625,23 @@ static int iwl_mld_start_pmsr(struct ieee80211_hw *hw,
return iwl_mld_ftm_start(mld, vif, request);
}
static enum ieee80211_neg_ttlm_res
iwl_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_neg_ttlm *neg_ttlm)
{
u16 map;
/* Verify all TIDs are mapped to the same links set */
map = neg_ttlm->downlink[0];
for (int i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) {
if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] ||
neg_ttlm->uplink[i] != map)
return NEG_TTLM_RES_REJECT;
}
return NEG_TTLM_RES_ACCEPT;
}
const struct ieee80211_ops iwl_mld_hw_ops = {
.tx = iwl_mld_mac80211_tx,
.start = iwl_mld_mac80211_start,
@@ -2711,4 +2711,5 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
.prep_add_interface = iwl_mld_prep_add_interface,
.set_hw_timestamp = iwl_mld_set_hw_timestamp,
.start_pmsr = iwl_mld_start_pmsr,
.can_neg_ttlm = iwl_mld_can_neg_ttlm,
};

View File

@@ -357,7 +357,7 @@ iwl_mld_configure_trans(struct iwl_op_mode *op_mode)
trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc);
trans->conf.wide_cmd_header = true;
iwl_trans_op_mode_enter(trans, op_mode);
@@ -725,6 +725,17 @@ static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode)
{}
#endif
static void iwl_mld_dump(struct iwl_op_mode *op_mode)
{
struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);
struct iwl_fw_runtime *fwrt = &mld->fwrt;
if (!iwl_trans_fw_running(fwrt->trans))
return;
iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER, NULL);
}
static const struct iwl_op_mode_ops iwl_mld_ops = {
.start = iwl_op_mode_mld_start,
.stop = iwl_op_mode_mld_stop,
@@ -739,6 +750,7 @@ static const struct iwl_op_mode_ops iwl_mld_ops = {
.sw_reset = iwl_mld_sw_reset,
.time_point = iwl_mld_time_point,
.device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off),
.dump = iwl_mld_dump,
};
struct iwl_mld_mod_params iwlmld_mod_params = {

View File

@@ -159,6 +159,7 @@
* @addresses: device MAC addresses.
* @scan: instance of the scan object
* @wowlan: WoWLAN support data.
* @debug_max_sleep: maximum sleep time in D3 (for debug purposes)
* @led: the led device
* @mcc_src: the source id of the MCC, comes from the firmware
* @bios_enable_puncturing: is puncturing enabled by bios
@@ -252,6 +253,7 @@ struct iwl_mld {
struct iwl_mld_scan scan;
#ifdef CONFIG_PM_SLEEP
struct wiphy_wowlan_support wowlan;
u32 debug_max_sleep;
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;

View File

@@ -287,6 +287,36 @@ int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif,
return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true);
}
#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (10 * HZ)
static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
int ret;
if (!iwl_mld_vif_has_emlsr_cap(vif))
return;
ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS,
iwl_mld_get_primary_link(vif));
if (ret)
return;
wiphy_delayed_work_queue(mld_vif->mld->wiphy,
&mld_vif->emlsr.tmp_non_bss_done_wk,
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT);
}
void iwl_mld_emlsr_block_tmp_non_bss(struct iwl_mld *mld)
{
ieee80211_iterate_active_interfaces_mtx(mld->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mld_vif_iter_emlsr_block_tmp_non_bss,
NULL);
}
static void _iwl_mld_select_links(struct iwl_mld *mld,
struct ieee80211_vif *vif);
@@ -530,10 +560,12 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
/*
* TPT is unblocked, need to check if the TPT criteria is still met.
*
* If EMLSR is active, then we also need to check the secondar link
* requirements.
* If EMLSR is active for at least 5 seconds, then we also
* need to check the secondary link requirements.
*/
if (iwl_mld_emlsr_active(vif)) {
if (iwl_mld_emlsr_active(vif) &&
time_is_before_jiffies(mld_vif->emlsr.last_entry_ts +
IWL_MLD_TPT_COUNT_WINDOW)) {
sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif));
sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id);
if (WARN_ON_ONCE(!sec_link))
@@ -1167,8 +1199,8 @@ void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
if (!iwl_mld_vif_has_emlsr_cap(vif) || iwl_mld_emlsr_active(vif) ||
mld_vif->emlsr.blocked_reasons)
if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif) ||
iwl_mld_emlsr_active(vif) || mld_vif->emlsr.blocked_reasons)
return;
iwl_mld_int_mlo_scan(mld, vif);

View File

@@ -157,6 +157,8 @@ struct iwl_mld_link_sel_data {
u16 grade;
};
void iwl_mld_emlsr_block_tmp_non_bss(struct iwl_mld *mld);
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
u32 iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif,
struct iwl_mld_link_sel_data *a,

View File

@@ -181,7 +181,7 @@ int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld)
.phy_specific_cfg = mld->fwrt.phy_filters,
};
IWL_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);
IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);
return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd);
}

View File

@@ -1752,6 +1752,10 @@ int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req,
struct ieee80211_scan_ies *ies)
{
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
iwl_mld_emlsr_block_tmp_non_bss(mld);
return _iwl_mld_single_scan_start(mld, vif, req, ies,
IWL_MLD_SCAN_REGULAR);
}
@@ -1800,17 +1804,20 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
}
#define IWL_MLD_MLO_SCAN_BLOCKOUT_TIME 5 /* seconds */
void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif)
{
struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
unsigned long usable_links = ieee80211_vif_usable_links(vif);
size_t n_channels = 0;
u8 link_id;
lockdep_assert_wiphy(mld->wiphy);
if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) ||
hweight16(vif->valid_links) == 1)
if (!IWL_MLD_AUTO_EML_ENABLE || !vif->cfg.assoc ||
!ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) == 1)
return;
if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) {
@@ -1818,6 +1825,15 @@ void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif)
return;
}
if (mld_vif->last_link_activation_time > ktime_get_boottime_seconds() -
IWL_MLD_MLO_SCAN_BLOCKOUT_TIME) {
/* timing doesn't matter much, so use the blockout time */
wiphy_delayed_work_queue(mld->wiphy,
&mld_vif->mlo_scan_start_wk,
IWL_MLD_MLO_SCAN_BLOCKOUT_TIME);
return;
}
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_bss_conf *link_conf =
link_conf_dereference_check(vif, link_id);

View File

@@ -130,7 +130,7 @@ struct iwl_mld_scan {
void *cmd;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
unsigned long last_mlo_scan_time;
u64 last_mlo_scan_time;
};
#endif /* __iwl_mld_scan_h__ */

View File

@@ -120,19 +120,17 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
struct {
struct iwl_mvm_wep_key_cmd wep_key_cmd;
struct iwl_mvm_wep_key wep_key;
} __packed wkc = {
.wep_key_cmd.mac_id_n_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color)),
.wep_key_cmd.num_keys = 1,
/* firmware sets STA_KEY_FLG_WEP_13BYTES */
.wep_key_cmd.decryption_type = STA_KEY_FLG_WEP,
.wep_key.key_index = key->keyidx,
.wep_key.key_size = key->keylen,
};
DEFINE_RAW_FLEX(struct iwl_mvm_wep_key_cmd, wkc, wep_key, 1);
struct iwl_mvm_wep_key *wep_key = wkc->wep_key;
wkc->mac_id_n_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color));
wkc->num_keys = 1;
/* firmware sets STA_KEY_FLG_WEP_13BYTES */
wkc->decryption_type = STA_KEY_FLG_WEP;
wep_key->key_index = key->keyidx;
wep_key->key_size = key->keylen;
/*
* This will fail -- the key functions don't set support
@@ -142,18 +140,19 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
break;
memcpy(&wkc.wep_key.key[3], key->key, key->keylen);
memcpy(&wep_key->key[3], key->key, key->keylen);
if (key->keyidx == mvmvif->tx_key_idx) {
/* TX key must be at offset 0 */
wkc.wep_key.key_offset = 0;
wep_key->key_offset = 0;
} else {
/* others start at 1 */
data->wep_key_idx++;
wkc.wep_key.key_offset = data->wep_key_idx;
wep_key->key_offset = data->wep_key_idx;
}
mutex_lock(&mvm->mutex);
ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0,
__struct_size(wkc), wkc);
data->error = ret != 0;
mvm->ptk_ivlen = key->iv_len;
@@ -2061,10 +2060,8 @@ static bool iwl_mvm_mlo_gtk_rekey(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_mlo_gtk *mlo_key = &status->mlo_keys[i];
struct ieee80211_key_conf *key, *old_key;
struct ieee80211_key_seq seq;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {};
DEFINE_RAW_FLEX(struct ieee80211_key_conf, conf, key,
WOWLAN_KEY_MAX_SIZE);
u16 flags = le16_to_cpu(mlo_key->flags);
int j, link_id, key_id, key_type;
@@ -2081,40 +2078,40 @@ static bool iwl_mvm_mlo_gtk_rekey(struct iwl_wowlan_status_data *status,
key_type >= WOWLAN_MLO_GTK_KEY_NUM_TYPES))
continue;
conf.conf.cipher = old_keys->cipher[link_id][key_type];
conf->cipher = old_keys->cipher[link_id][key_type];
/* WARN_ON? */
if (!conf.conf.cipher)
if (!conf->cipher)
continue;
conf.conf.keylen = 0;
switch (conf.conf.cipher) {
conf->keylen = 0;
switch (conf->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
conf->keylen = WLAN_KEY_LEN_CCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
conf->keylen = WLAN_KEY_LEN_GCMP_256;
break;
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128;
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;
conf->keylen = WLAN_KEY_LEN_BIP_GMAC_256;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC;
conf->keylen = WLAN_KEY_LEN_AES_CMAC;
break;
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256;
conf->keylen = WLAN_KEY_LEN_BIP_CMAC_256;
break;
}
if (WARN_ON(!conf.conf.keylen ||
conf.conf.keylen > sizeof(conf.key)))
if (WARN_ON(!conf->keylen ||
conf->keylen > WOWLAN_KEY_MAX_SIZE))
continue;
memcpy(conf.conf.key, mlo_key->key, conf.conf.keylen);
conf.conf.keyidx = key_id;
memcpy(conf->key, mlo_key->key, conf->keylen);
conf->keyidx = key_id;
old_key = old_keys->key[link_id][key_id];
if (old_key) {
@@ -2126,7 +2123,7 @@ static bool iwl_mvm_mlo_gtk_rekey(struct iwl_wowlan_status_data *status,
IWL_DEBUG_WOWLAN(mvm, "Add MLO key id %d, link id %d\n",
key_id, link_id);
key = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id);
key = ieee80211_gtk_rekey_add(vif, conf, link_id);
if (WARN_ON(IS_ERR(key))) {
ret = false;
goto out;
@@ -2156,30 +2153,28 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
{
int i, j;
struct ieee80211_key_conf *key;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {
.conf.cipher = gtk_cipher,
};
DEFINE_RAW_FLEX(struct ieee80211_key_conf, conf, key,
WOWLAN_KEY_MAX_SIZE);
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
conf->cipher = gtk_cipher;
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) < sizeof(status->gtk[0].key));
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.conf.keylen = WLAN_KEY_LEN_CCMP;
conf->keylen = WLAN_KEY_LEN_CCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
conf->keylen = WLAN_KEY_LEN_GCMP_256;
break;
case WLAN_CIPHER_SUITE_TKIP:
conf.conf.keylen = WLAN_KEY_LEN_TKIP;
conf->keylen = WLAN_KEY_LEN_TKIP;
break;
default:
WARN_ON(1);
@@ -2189,14 +2184,14 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
if (!status->gtk[i].len)
continue;
conf.conf.keyidx = status->gtk[i].id;
conf->keyidx = status->gtk[i].id;
IWL_DEBUG_WOWLAN(mvm,
"Received from FW GTK cipher %d, key index %d\n",
conf.conf.cipher, conf.conf.keyidx);
memcpy(conf.conf.key, status->gtk[i].key,
conf->cipher, conf->keyidx);
memcpy(conf->key, status->gtk[i].key,
sizeof(status->gtk[i].key));
key = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id);
key = ieee80211_gtk_rekey_add(vif, conf, link_id);
if (IS_ERR(key))
return false;
@@ -2218,42 +2213,40 @@ iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status,
struct ieee80211_vif *vif, u32 cipher,
struct iwl_multicast_key_data *key_data)
{
DEFINE_RAW_FLEX(struct ieee80211_key_conf, conf, key,
WOWLAN_KEY_MAX_SIZE);
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,
};
struct ieee80211_key_seq seq;
int link_id = vif->active_links ? __ffs(vif->active_links) : -1;
conf->cipher = cipher;
conf->keyidx = key_data->id;
if (!key_data->len)
return true;
iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, conf.conf.cipher);
iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, conf->cipher);
switch (cipher) {
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128;
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;
conf->keylen = WLAN_KEY_LEN_BIP_GMAC_256;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC;
conf->keylen = WLAN_KEY_LEN_AES_CMAC;
break;
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256;
conf->keylen = WLAN_KEY_LEN_BIP_CMAC_256;
break;
default:
WARN_ON(1);
}
BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key));
memcpy(conf.conf.key, key_data->key, conf.conf.keylen);
BUILD_BUG_ON(WOWLAN_KEY_MAX_SIZE < sizeof(key_data->key));
memcpy(conf->key, key_data->key, conf->keylen);
key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id);
key_config = ieee80211_gtk_rekey_add(vif, conf, link_id);
if (IS_ERR(key_config))
return false;
ieee80211_set_key_rx_seq(key_config, 0, &seq);

View File

@@ -314,7 +314,8 @@ int iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant,
/* This has been tested on those devices only */
if (mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_9000 &&
mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_22000)
mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_22000 &&
mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_AX210)
return -EOPNOTSUPP;
if (!mvm->nvm_data)

View File

@@ -2133,7 +2133,6 @@ bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
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

View File

@@ -61,8 +61,10 @@ static int __init iwl_mvm_init(void)
}
ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
if (ret)
if (ret) {
pr_err("Unable to register MVM op_mode: %d\n", ret);
iwl_mvm_rate_control_unregister();
}
return ret;
}

View File

@@ -411,6 +411,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
* with the mac80211 subsystem. This should be performed prior to calling
* ieee80211_register_hw
*
* Return: negative error code, or 0 on success
*/
int iwl_mvm_rate_control_register(void);

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -11,6 +11,7 @@
#include "mvm.h"
#include "fw/api/scan.h"
#include "iwl-io.h"
#include "iwl-utils.h"
#define IWL_DENSE_EBS_SCAN_RATIO 5
#define IWL_SPARSE_EBS_SCAN_RATIO 1
@@ -3685,117 +3686,6 @@ static int iwl_mvm_chanidx_from_phy(struct iwl_mvm *mvm,
return -EINVAL;
}
static u32 iwl_mvm_div_by_db(u32 value, u8 db)
{
/*
* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping
* at 10 dB and looping instead of using a much larger table.
*
* Using 64 bit math is overkill, but means the helper does not require
* a limit on the input range.
*/
static const u32 db_to_val[] = {
0xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,
0x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,
};
while (value && db > 0) {
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
value = (((u64)value) * db_to_val[change - 1]) >> 32;
db -= change;
}
return value;
}
VISIBLE_IF_IWLWIFI_KUNIT s8
iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif)
{
s8 average_magnitude;
u32 average_factor;
s8 sum_magnitude = -128;
u32 sum_factor = 0;
int i, count = 0;
/*
* To properly average the decibel values (signal values given in dBm)
* we need to do the math in linear space. Doing a linear average of
* dB (dBm) values is a bit annoying though due to the large range of
* at least -10 to -110 dBm that will not fit into a 32 bit integer.
*
* A 64 bit integer should be sufficient, but then we still have the
* problem that there are no directly usable utility functions
* available.
*
* So, lets not deal with that and instead do much of the calculation
* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit
* gives us plenty of head-room for adding up a few values and even
* doing some math on it. And the tail should be accurate enough too
* (1/2^16 is somewhere around -48 dB, so effectively zero).
*
* i.e. the real value of sum is:
* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW
*
* However, that does mean we need to be able to bring two values to
* a common base, so we need a helper for that.
*
* Note that this function takes an input with unsigned negative dBm
* values but returns a signed dBm (i.e. a negative value).
*/
for (i = 0; i < ARRAY_SIZE(notif->noise); i++) {
s8 val_magnitude;
u32 val_factor;
if (notif->noise[i] == 0xff)
continue;
val_factor = 0x10000;
val_magnitude = -notif->noise[i];
if (val_magnitude <= sum_magnitude) {
u8 div_db = sum_magnitude - val_magnitude;
val_factor = iwl_mvm_div_by_db(val_factor, div_db);
val_magnitude = sum_magnitude;
} else {
u8 div_db = val_magnitude - sum_magnitude;
sum_factor = iwl_mvm_div_by_db(sum_factor, div_db);
sum_magnitude = val_magnitude;
}
sum_factor += val_factor;
count++;
}
/* No valid noise measurement, return a very high noise level */
if (count == 0)
return 0;
average_magnitude = sum_magnitude;
average_factor = sum_factor / count;
/*
* average_factor will be a number smaller than 1.0 (0x10000) at this
* point. What we need to do now is to adjust average_magnitude so that
* average_factor is between -0.5 dB and 0.5 dB.
*
* Just do -1 dB steps and find the point where
* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB
* = div_by_db(0xe429, i)
* is smaller than average_factor.
*/
for (i = 0; average_factor < iwl_mvm_div_by_db(0xe429, i); i++) {
/* nothing */
}
return average_magnitude - i;
}
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_average_dbm_values);
void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb)
{
@@ -3853,5 +3743,6 @@ void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
info->time_busy = le32_to_cpu(notif->busy_time);
info->time_rx = le32_to_cpu(notif->rx_time);
info->time_tx = le32_to_cpu(notif->tx_time);
info->noise = iwl_mvm_average_dbm_values(notif);
info->noise =
iwl_average_neg_dbm(notif->noise, ARRAY_SIZE(notif->noise));
}

View File

@@ -214,7 +214,7 @@ struct iwl_mvm_vif;
*/
/**
* enum iwl_mvm_agg_state
* enum iwl_mvm_agg_state - aggregation session state
*
* The state machine of the BA agreement establishment / tear down.
* These states relate to a specific RA / TID.
@@ -483,6 +483,7 @@ struct iwl_mvm_int_sta {
* about. Otherwise (if this is a new STA), this should be false.
* @flags: if update==true, this marks what is being changed via ORs of values
* from enum iwl_sta_modify_flag. Otherwise, this is ignored.
* Return: negative error code or 0 on success
*/
int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
bool update, unsigned int flags);

View File

@@ -1,3 +1,3 @@
iwlmvm-tests-y += module.o links.o scan.o hcmd.o
iwlmvm-tests-y += module.o links.o hcmd.o
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmvm-tests.o

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2012-2014, 2019-2020, 2023 Intel Corporation
* Copyright (C) 2012-2014, 2019-2020, 2023, 2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
*/
#ifndef __time_event_h__
@@ -124,6 +124,8 @@ void iwl_mvm_rx_roc_notif(struct iwl_mvm *mvm,
* ROC request, it will issue a notification to the driver that it is on the
* requested channel. Once the FW completes the ROC request it will issue
* another notification to the driver.
*
* Return: negative error code or 0 on success
*/
int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int duration, enum ieee80211_roc_type type);
@@ -179,6 +181,8 @@ void iwl_mvm_remove_csa_period(struct iwl_mvm *mvm,
*
* This function is used to schedule NoA time event and is used to perform
* the channel switch flow.
*
* Return: negative error code or 0 on success
*/
int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
@@ -188,7 +192,7 @@ int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm,
* iwl_mvm_te_scheduled - check if the fw received the TE cmd
* @te_data: the time event data that corresponds to that time event
*
* This function returns true iff this TE is added to the fw.
* Return: %true if this TE is added to the fw, %false otherwise
*/
static inline bool
iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)

View File

@@ -6,7 +6,7 @@
#include "iwl-trans.h"
#include "iwl-fh.h"
#include "iwl-context-info-v2.h"
#include "internal.h"
#include "gen1_2/internal.h"
#include "iwl-prph.h"
static const struct dmi_system_id dmi_force_scu_active_approved_list[] = {
@@ -391,13 +391,13 @@ static int iwl_pcie_load_payloads_segments
{
struct iwl_dram_data *cur_payload_dram = &dram_regions->drams[0];
struct iwl_dram_data *desc_dram = &dram_regions->prph_scratch_mem_desc;
struct iwl_prph_scrath_mem_desc_addr_array *addresses;
struct iwl_prph_scratch_mem_desc_addr_array *addresses;
const void *data;
u32 len;
int i;
/* allocate and init DRAM descriptors array */
len = sizeof(struct iwl_prph_scrath_mem_desc_addr_array);
len = sizeof(struct iwl_prph_scratch_mem_desc_addr_array);
desc_dram->block = iwl_pcie_ctxt_info_dma_alloc_coherent
(trans,
len,

View File

@@ -6,7 +6,7 @@
#include "iwl-trans.h"
#include "iwl-fh.h"
#include "iwl-context-info.h"
#include "internal.h"
#include "gen1_2/internal.h"
#include "iwl-prph.h"
static void *_iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans,

View File

@@ -15,7 +15,7 @@
#include "iwl-trans.h"
#include "iwl-drv.h"
#include "iwl-prph.h"
#include "internal.h"
#include "gen1_2/internal.h"
#define _IS_A(cfg, _struct) __builtin_types_compatible_p(typeof(cfg), \
struct _struct)
@@ -545,6 +545,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_mac_cfg)},
{IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_mac_cfg)},
{IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_mac_cfg)},
{IWL_PCI_DEVICE(0xD240, PCI_ANY_ID, iwl_sc_mac_cfg)},
#endif /* CONFIG_IWLMLD */
{0}
@@ -1580,12 +1581,21 @@ static const struct dev_pm_ops iwl_dev_pm_ops = {
#endif /* CONFIG_PM_SLEEP */
static void iwl_pci_dump(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct iwl_trans *trans = pci_get_drvdata(pdev);
iwl_op_mode_dump(trans->op_mode);
}
static struct pci_driver iwl_pci_driver = {
.name = DRV_NAME,
.id_table = iwl_hw_card_ids,
.probe = iwl_pci_probe,
.remove = iwl_pci_remove,
.driver.pm = IWL_PM_OPS,
.driver.coredump = iwl_pci_dump,
};
int __must_check iwl_pci_register_driver(void)

View File

@@ -22,7 +22,7 @@
#include "iwl-io.h"
#include "iwl-op-mode.h"
#include "iwl-drv.h"
#include "iwl-context-info.h"
#include "pcie/iwl-context-info.h"
/*
* RX related structures and functions
@@ -39,7 +39,7 @@ struct iwl_host_cmd;
* trans_pcie layer */
/**
* struct iwl_rx_mem_buffer
* struct iwl_rx_mem_buffer - driver-side RX buffer descriptor
* @page_dma: bus address of rxb page
* @page: driver's pointer to the rxb page
* @list: list entry for the membuffer
@@ -190,6 +190,7 @@ struct iwl_rb_allocator {
* iwl_get_closed_rb_stts - get closed rb stts from different structs
* @trans: transport pointer (for configuration)
* @rxq: the rxq to get the rb stts from
* Return: last closed RB index
*/
static inline u16 iwl_get_closed_rb_stts(struct iwl_trans *trans,
struct iwl_rxq *rxq)
@@ -382,8 +383,7 @@ struct iwl_pcie_txqs {
* @irq_lock: lock to synchronize IRQ handling
* @txq_memory: TXQ allocation array
* @sx_waitq: waitqueue for Sx transitions
* @sx_complete: completion for Sx transitions
* @pcie_dbg_dumped_once: indicates PCIe regs were dumped already
* @sx_state: state tracking Sx transitions
* @opmode_down: indicates opmode went away
* @num_rx_bufs: number of RX buffers to allocate/use
* @affinity_mask: IRQ affinity mask for each RX queue
@@ -448,13 +448,17 @@ struct iwl_trans_pcie {
u8 __iomem *hw_base;
bool ucode_write_complete;
bool sx_complete;
enum {
IWL_SX_INVALID = 0,
IWL_SX_WAITING,
IWL_SX_ERROR,
IWL_SX_COMPLETE,
} sx_state;
wait_queue_head_t ucode_write_waitq;
wait_queue_head_t sx_waitq;
u16 num_rx_bufs;
bool pcie_dbg_dumped_once;
u32 rx_page_order;
u32 rx_buf_bytes;
u32 supported_dma_mask;
@@ -698,6 +702,7 @@ static inline void iwl_txq_stop(struct iwl_trans *trans, struct iwl_txq *txq)
* iwl_txq_inc_wrap - increment queue index, wrap back to beginning
* @trans: the transport (for configuration data)
* @index: current index
* Return: the queue index incremented, subject to wrapping
*/
static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index)
{
@@ -709,6 +714,7 @@ static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index)
* iwl_txq_dec_wrap - decrement queue index, wrap back to end
* @trans: the transport (for configuration data)
* @index: current index
* Return: the queue index decremented, subject to wrapping
*/
static inline int iwl_txq_dec_wrap(struct iwl_trans *trans, int index)
{
@@ -1028,40 +1034,12 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
}
static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
u32 reg, u32 mask, u32 value)
{
u32 v;
#ifdef CONFIG_IWLWIFI_DEBUG
WARN_ON_ONCE(value & ~mask);
#endif
v = iwl_read32(trans, reg);
v &= ~mask;
v |= value;
iwl_write32(trans, reg, v);
}
static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
u32 reg, u32 mask)
{
__iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
}
static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
u32 reg, u32 mask)
{
__iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
}
static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans)
{
return (trans->dbg.dest_tlv || iwl_trans_dbg_ini_valid(trans));
}
void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq);
void iwl_trans_pcie_dump_regs(struct iwl_trans *trans);
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans);
@@ -1074,6 +1052,7 @@ 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);
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);
void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val);
@@ -1083,8 +1062,6 @@ u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg);
void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val);
int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
void *buf, int dwords);
int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
const void *buf, int dwords);
int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership);
struct iwl_trans_dump_data *
iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask,

View File

@@ -12,7 +12,7 @@
#include "iwl-io.h"
#include "internal.h"
#include "iwl-op-mode.h"
#include "iwl-context-info-v2.h"
#include "pcie/iwl-context-info-v2.h"
#include "fw/dbg.h"
/******************************************************************************
@@ -1700,6 +1700,15 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
timer_delete(&trans_pcie->txqs.txq[i]->stuck_timer);
}
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
u32 val = iwl_read32(trans, CSR_IPC_STATE);
if (val & CSR_IPC_STATE_TOP_RESET_REQ) {
IWL_ERR(trans, "FW requested TOP reset for FSEQ\n");
trans->do_top_reset = 1;
}
}
/* The STATUS_FW_ERROR bit is set in this function. This must happen
* before we wake up the command caller, to ensure a proper cleanup. */
iwl_trans_fw_error(trans, IWL_ERR_TYPE_IRQ);
@@ -1852,7 +1861,12 @@ static void iwl_trans_pcie_handle_reset_interrupt(struct iwl_trans *trans)
}
fallthrough;
case CSR_IPC_STATE_RESET_TOP_READY:
/* FIXME: handle this case when requesting TOP reset */
if (trans_pcie->fw_reset_state == FW_RESET_TOP_REQUESTED) {
IWL_DEBUG_ISR(trans, "TOP Reset continues\n");
trans_pcie->fw_reset_state = FW_RESET_OK;
wake_up(&trans_pcie->fw_reset_waitq);
break;
}
fallthrough;
case CSR_IPC_STATE_RESET_NONE:
IWL_FW_CHECK_FAILED(trans,
@@ -2380,6 +2394,11 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
} else {
iwl_pcie_irq_handle_error(trans);
}
if (trans_pcie->sx_state == IWL_SX_WAITING) {
trans_pcie->sx_state = IWL_SX_ERROR;
wake_up(&trans_pcie->sx_waitq);
}
}
/* After checking FH register check HW register */
@@ -2414,13 +2433,20 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
if (inta_hw & MSIX_HW_INT_CAUSES_REG_WAKEUP && trans_pcie->prph_info) {
u32 sleep_notif =
le32_to_cpu(trans_pcie->prph_info->sleep_notif);
if (sleep_notif == IWL_D3_SLEEP_STATUS_SUSPEND ||
sleep_notif == IWL_D3_SLEEP_STATUS_RESUME) {
IWL_DEBUG_ISR(trans,
"Sx interrupt: sleep notification = 0x%x\n",
sleep_notif);
trans_pcie->sx_complete = true;
wake_up(&trans_pcie->sx_waitq);
if (trans_pcie->sx_state == IWL_SX_WAITING) {
trans_pcie->sx_state = IWL_SX_COMPLETE;
wake_up(&trans_pcie->sx_waitq);
} else {
IWL_ERR(trans,
"unexpected Sx interrupt (0x%x)\n",
sleep_notif);
}
} else {
/* uCode wakes up after power-down sleep */
IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");

View File

@@ -5,8 +5,8 @@
*/
#include "iwl-trans.h"
#include "iwl-prph.h"
#include "iwl-context-info.h"
#include "iwl-context-info-v2.h"
#include "pcie/iwl-context-info.h"
#include "pcie/iwl-context-info-v2.h"
#include "internal.h"
#include "fw/dbg.h"
@@ -610,6 +610,11 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
msleep(10);
IWL_INFO(trans, "TOP reset successful, reinit now\n");
/* now load the firmware again properly */
ret = _iwl_trans_pcie_start_hw(trans);
if (ret) {
IWL_ERR(trans, "failed to start HW after TOP reset\n");
goto out;
}
trans_pcie->prph_scratch->ctrl_cfg.control.control_flags &=
~cpu_to_le32(IWL_PRPH_SCRATCH_TOP_RESET);
top_reset_done = true;

View File

@@ -28,106 +28,13 @@
#include "mei/iwl-mei.h"
#include "internal.h"
#include "iwl-fh.h"
#include "iwl-context-info-v2.h"
#include "pcie/iwl-context-info-v2.h"
#include "pcie/utils.h"
/* extended range in FW SRAM */
#define IWL_FW_MEM_EXTENDED_START 0x40000
#define IWL_FW_MEM_EXTENDED_END 0x57FFF
void iwl_trans_pcie_dump_regs(struct iwl_trans *trans)
{
#define PCI_DUMP_SIZE 352
#define PCI_MEM_DUMP_SIZE 64
#define PCI_PARENT_DUMP_SIZE 524
#define PREFIX_LEN 32
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct pci_dev *pdev = trans_pcie->pci_dev;
u32 i, pos, alloc_size, *ptr, *buf;
char *prefix;
if (trans_pcie->pcie_dbg_dumped_once)
return;
/* Should be a multiple of 4 */
BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3);
BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3);
BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3);
/* Alloc a max size buffer */
alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN;
alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN);
alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN);
alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN);
buf = kmalloc(alloc_size, GFP_ATOMIC);
if (!buf)
return;
prefix = (char *)buf + alloc_size - PREFIX_LEN;
IWL_ERR(trans, "iwlwifi transaction failed, dumping registers\n");
/* Print wifi device registers */
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
IWL_ERR(trans, "iwlwifi device config registers:\n");
for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++)
if (pci_read_config_dword(pdev, i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
IWL_ERR(trans, "iwlwifi device memory mapped registers:\n");
for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++)
*ptr = iwl_read32(trans, i);
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
if (pos) {
IWL_ERR(trans, "iwlwifi device AER capability structure:\n");
for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++)
if (pci_read_config_dword(pdev, pos + i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
32, 4, buf, i, 0);
}
/* Print parent device registers next */
if (!pdev->bus->self)
goto out;
pdev = pdev->bus->self;
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n",
pci_name(pdev));
for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++)
if (pci_read_config_dword(pdev, i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
/* Print root port AER registers */
pos = 0;
pdev = pcie_find_root_port(pdev);
if (pdev)
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
if (pos) {
IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n",
pci_name(pdev));
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++)
if (pci_read_config_dword(pdev, pos + i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32,
4, buf, i, 0);
}
goto out;
err_read:
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
IWL_ERR(trans, "Read failed at 0x%X\n", i);
out:
trans_pcie->pcie_dbg_dumped_once = 1;
kfree(buf);
}
int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership)
{
/* Reset entire device - do controller reset (results in SHRD_HW_RST) */
@@ -387,8 +294,8 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
u32 dl_cfg_reg;
/* Force XTAL ON */
__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
iwl_trans_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
ret = iwl_trans_pcie_sw_reset(trans, true);
@@ -397,8 +304,8 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
if (WARN_ON(ret)) {
/* Release XTAL ON request */
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
iwl_trans_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
return;
}
@@ -449,12 +356,12 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* Activates XTAL resources monitor */
__iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG,
CSR_MONITOR_XTAL_RESOURCES);
iwl_trans_set_bit(trans, CSR_MONITOR_CFG_REG,
CSR_MONITOR_XTAL_RESOURCES);
/* Release XTAL ON request */
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
iwl_trans_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
udelay(10);
/* Release APMG XTAL */
@@ -704,7 +611,7 @@ static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans,
trans_pcie->ucode_write_complete, 5 * HZ);
if (!ret) {
IWL_ERR(trans, "Failed to load firmware chunk!\n");
iwl_trans_pcie_dump_regs(trans);
iwl_trans_pcie_dump_regs(trans, trans_pcie->pci_dev);
return -ETIMEDOUT;
}
@@ -1536,30 +1443,41 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
return 0;
trans_pcie->sx_state = IWL_SX_WAITING;
if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND :
UREG_DOORBELL_TO_ISR6_RESUME);
else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
else
iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,
suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :
CSR_IPC_SLEEP_CONTROL_RESUME);
else
return 0;
ret = wait_event_timeout(trans_pcie->sx_waitq,
trans_pcie->sx_complete, 2 * HZ);
/* Invalidate it toward next suspend or resume */
trans_pcie->sx_complete = false;
trans_pcie->sx_state != IWL_SX_WAITING,
2 * HZ);
if (!ret) {
IWL_ERR(trans, "Timeout %s D3\n",
suspend ? "entering" : "exiting");
return -ETIMEDOUT;
ret = -ETIMEDOUT;
} else {
ret = 0;
}
return 0;
if (trans_pcie->sx_state == IWL_SX_ERROR) {
IWL_ERR(trans, "FW error while %s D3\n",
suspend ? "entering" : "exiting");
ret = -EIO;
}
/* Invalidate it toward next suspend or resume */
trans_pcie->sx_state = IWL_SX_INVALID;
return ret;
}
int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset)
@@ -1845,7 +1763,7 @@ static int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans)
return iwl_trans_pcie_sw_reset(trans, true);
}
static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans)
int _iwl_trans_pcie_start_hw(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int err;
@@ -2412,7 +2330,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent)
}
/* this bit wakes up the NIC */
__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, write);
iwl_trans_set_bit(trans, CSR_GP_CNTRL, write);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000)
udelay(2);
@@ -2449,7 +2367,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent)
"Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
cntrl);
iwl_trans_pcie_dump_regs(trans);
iwl_trans_pcie_dump_regs(trans, trans_pcie->pci_dev);
if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U)
iwl_trans_pcie_reset(trans,
@@ -2501,11 +2419,11 @@ iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
if (trans_pcie->cmd_hold_nic_awake)
goto out;
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
iwl_trans_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ);
else
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
iwl_trans_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
/*
* Above we read the CSR_GP_CNTRL register, which will flush
* any previous writes, but we need the write that clears the
@@ -2567,24 +2485,6 @@ int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
return 0;
}
int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
const void *buf, int dwords)
{
int offs, ret = 0;
const u32 *vals = buf;
if (iwl_trans_grab_nic_access(trans)) {
iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
for (offs = 0; offs < dwords; offs++)
iwl_write32(trans, HBUS_TARG_MEM_WDAT,
vals ? vals[offs] : 0);
iwl_trans_release_nic_access(trans);
} else {
ret = -EBUSY;
}
return ret;
}
int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs,
u32 *val)
{
@@ -2704,7 +2604,7 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
spin_lock_bh(&trans_pcie->reg_lock);
__iwl_trans_pcie_set_bits_mask(trans, reg, mask, value);
_iwl_trans_set_bits_mask(trans, reg, mask, value);
spin_unlock_bh(&trans_pcie->reg_lock);
}
@@ -4046,7 +3946,7 @@ int iwl_trans_pcie_copy_imr(struct iwl_trans *trans,
IMR_D2S_REQUESTED, 5 * HZ);
if (!ret || trans_pcie->imr_status == IMR_D2S_ERROR) {
IWL_ERR(trans, "Failed to copy IMR Memory chunk!\n");
iwl_trans_pcie_dump_regs(trans);
iwl_trans_pcie_dump_regs(trans, trans_pcie->pci_dev);
return -ETIMEDOUT;
}
trans_pcie->imr_status = IMR_D2S_IDLE;

View File

@@ -25,6 +25,7 @@
#include "iwl-op-mode.h"
#include "internal.h"
#include "fw/api/tx.h"
#include "pcie/utils.h"
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
@@ -203,8 +204,8 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
}
trans_pcie->cmd_hold_nic_awake = false;
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
iwl_trans_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
spin_unlock(&trans_pcie->reg_lock);
}
@@ -494,9 +495,9 @@ void iwl_pcie_tx_start(struct iwl_trans *trans)
iwl_read_prph(trans, SCD_SRAM_BASE_ADDR);
/* reset context data, TX status and translation data */
iwl_trans_pcie_write_mem(trans, trans_pcie->scd_base_addr +
SCD_CONTEXT_MEM_LOWER_BOUND,
NULL, clear_dwords);
iwl_trans_write_mem(trans, trans_pcie->scd_base_addr +
SCD_CONTEXT_MEM_LOWER_BOUND,
NULL, clear_dwords);
iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
trans_pcie->txqs.scd_bc_tbls.dma >> 10);
@@ -1292,9 +1293,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
if (configure_scd) {
iwl_scd_txq_set_inactive(trans, txq_id);
iwl_trans_pcie_write_mem(trans, stts_addr,
(const void *)zero_val,
ARRAY_SIZE(zero_val));
iwl_trans_write_mem(trans, stts_addr, (const void *)zero_val,
ARRAY_SIZE(zero_val));
}
iwl_pcie_txq_unmap(trans, txq_id);

View File

@@ -130,11 +130,11 @@ struct iwl_prph_scratch_pnvm_cfg {
} __packed; /* PERIPH_SCRATCH_PNVM_CFG_S */
/**
* struct iwl_prph_scrath_mem_desc_addr_array
* struct iwl_prph_scratch_mem_desc_addr_array - DRAM
* @mem_descs: array of dram addresses.
* Each address is the beggining of a pnvm payload.
* Each address is the beginning of a PNVM payload.
*/
struct iwl_prph_scrath_mem_desc_addr_array {
struct iwl_prph_scratch_mem_desc_addr_array {
__le64 mem_descs[IPC_DRAM_MAP_ENTRY_NUM_MAX];
} __packed; /* PERIPH_SCRATCH_MEM_DESC_ADDR_ARRAY_S_VER_1 */

View File

@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2025 Intel Corporation
*/
#include <linux/pci.h>
#include <linux/gfp.h>
#include "iwl-io.h"
#include "pcie/utils.h"
void iwl_trans_pcie_dump_regs(struct iwl_trans *trans, struct pci_dev *pdev)
{
#define PCI_DUMP_SIZE 352
#define PCI_MEM_DUMP_SIZE 64
#define PCI_PARENT_DUMP_SIZE 524
#define PREFIX_LEN 32
static bool pcie_dbg_dumped_once = 0;
u32 i, pos, alloc_size, *ptr, *buf;
char *prefix;
if (pcie_dbg_dumped_once)
return;
/* Should be a multiple of 4 */
BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3);
BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3);
BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3);
/* Alloc a max size buffer */
alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN;
alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN);
alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN);
alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN);
buf = kmalloc(alloc_size, GFP_ATOMIC);
if (!buf)
return;
prefix = (char *)buf + alloc_size - PREFIX_LEN;
IWL_ERR(trans, "iwlwifi transaction failed, dumping registers\n");
/* Print wifi device registers */
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
IWL_ERR(trans, "iwlwifi device config registers:\n");
for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++)
if (pci_read_config_dword(pdev, i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
IWL_ERR(trans, "iwlwifi device memory mapped registers:\n");
for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++)
*ptr = iwl_read32(trans, i);
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
if (pos) {
IWL_ERR(trans, "iwlwifi device AER capability structure:\n");
for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++)
if (pci_read_config_dword(pdev, pos + i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
32, 4, buf, i, 0);
}
/* Print parent device registers next */
if (!pdev->bus->self)
goto out;
pdev = pdev->bus->self;
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n",
pci_name(pdev));
for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++)
if (pci_read_config_dword(pdev, i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
/* Print root port AER registers */
pos = 0;
pdev = pcie_find_root_port(pdev);
if (pdev)
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
if (pos) {
IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n",
pci_name(pdev));
sprintf(prefix, "iwlwifi %s: ", pci_name(pdev));
for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++)
if (pci_read_config_dword(pdev, pos + i, ptr))
goto err_read;
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32,
4, buf, i, 0);
}
goto out;
err_read:
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0);
IWL_ERR(trans, "Read failed at 0x%X\n", i);
out:
pcie_dbg_dumped_once = 1;
kfree(buf);
}

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2025 Intel Corporation
*/
#ifndef __iwl_pcie_utils_h__
#define __iwl_pcie_utils_h__
void iwl_trans_pcie_dump_regs(struct iwl_trans *trans, struct pci_dev *pdev);
static inline void _iwl_trans_set_bits_mask(struct iwl_trans *trans,
u32 reg, u32 mask, u32 value)
{
u32 v;
#ifdef CONFIG_IWLWIFI_DEBUG
WARN_ON_ONCE(value & ~mask);
#endif
v = iwl_read32(trans, reg);
v &= ~mask;
v |= value;
iwl_write32(trans, reg, v);
}
static inline void iwl_trans_clear_bit(struct iwl_trans *trans,
u32 reg, u32 mask)
{
_iwl_trans_set_bits_mask(trans, reg, mask, 0);
}
static inline void iwl_trans_set_bit(struct iwl_trans *trans,
u32 reg, u32 mask)
{
_iwl_trans_set_bits_mask(trans, reg, mask, mask);
}
#endif /* __iwl_pcie_utils_h__ */

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
iwlwifi-tests-y += module.o devinfo.o
iwlwifi-tests-y += module.o devinfo.o utils.o
ccflags-y += -I$(src)/../

View File

@@ -1,20 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for channel helper functions
* KUnit tests for utilities
*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024-2025 Intel Corporation
*/
#include <net/mac80211.h>
#include "../mvm.h"
#include "../iwl-utils.h"
#include <kunit/test.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_IMPORT_NS("IWLWIFI");
static const struct acs_average_db_case {
static const struct average_neg_db_case {
const char *desc;
u8 neg_dbm[22];
s8 result;
} acs_average_db_cases[] = {
} average_neg_db_cases[] = {
{
.desc = "Smallest possible value, all filled",
.neg_dbm = {
@@ -73,38 +72,38 @@ static const struct acs_average_db_case {
},
};
KUNIT_ARRAY_PARAM_DESC(acs_average_db, acs_average_db_cases, desc)
KUNIT_ARRAY_PARAM_DESC(average_neg_db, average_neg_db_cases, desc)
static void test_acs_average_db(struct kunit *test)
static void test_average_neg_db(struct kunit *test)
{
const struct acs_average_db_case *params = test->param_value;
struct iwl_umac_scan_channel_survey_notif notif;
const struct average_neg_db_case *params = test->param_value;
u8 reversed[ARRAY_SIZE(params->neg_dbm)];
int i;
/* Test the values in the given order */
for (i = 0; i < ARRAY_SIZE(params->neg_dbm); i++)
notif.noise[i] = params->neg_dbm[i];
KUNIT_ASSERT_EQ(test,
iwl_mvm_average_dbm_values(&notif),
iwl_average_neg_dbm(params->neg_dbm,
ARRAY_SIZE(params->neg_dbm)),
params->result);
/* Test in reverse order */
for (i = 0; i < ARRAY_SIZE(params->neg_dbm); i++)
notif.noise[ARRAY_SIZE(params->neg_dbm) - i - 1] =
reversed[ARRAY_SIZE(params->neg_dbm) - i - 1] =
params->neg_dbm[i];
KUNIT_ASSERT_EQ(test,
iwl_mvm_average_dbm_values(&notif),
iwl_average_neg_dbm(reversed,
ARRAY_SIZE(params->neg_dbm)),
params->result);
}
static struct kunit_case acs_average_db_case[] = {
KUNIT_CASE_PARAM(test_acs_average_db, acs_average_db_gen_params),
static struct kunit_case average_db_case[] = {
KUNIT_CASE_PARAM(test_average_neg_db, average_neg_db_gen_params),
{}
};
static struct kunit_suite acs_average_db = {
.name = "iwlmvm-acs-average-db",
.test_cases = acs_average_db_case,
static struct kunit_suite average_db = {
.name = "iwl-average-db",
.test_cases = average_db_case,
};
kunit_test_suite(acs_average_db);
kunit_test_suite(average_db);