From 2510e2047c6b1aaf76fbcf012256988d15402391 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Thu, 21 Aug 2025 20:47:12 +0300 Subject: wifi: iwlwifi: mvm: remove MLO code Now as we have iwlmld, which is the op-mode that will be loaded for EHT capable devices, there is no need for EHT features in iwlmvm. This change removes: - The logic of entering and exiting EMLSR - Link selection - MLO scan - relevant Kunit tests - related debugfs entries Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.933bb1a12e42.I3d017c30ffc2a29bc12ff0270562bcfb234c0785@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 131 ---- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 20 +- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 - .../net/wireless/intel/iwlwifi/mvm/debugfs-vif.c | 94 --- drivers/net/wireless/intel/iwlwifi/mvm/link.c | 809 --------------------- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 34 - drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 124 +--- .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 138 +--- drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 2 - drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 130 +--- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 53 -- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 133 ---- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 14 +- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 101 --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 89 --- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 24 - .../net/wireless/intel/iwlwifi/mvm/tests/Makefile | 2 +- .../net/wireless/intel/iwlwifi/mvm/tests/links.c | 433 ----------- .../net/wireless/intel/iwlwifi/mvm/time-event.c | 3 - drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 +- 20 files changed, 18 insertions(+), 2330 deletions(-) delete mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tests/links.c diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 13cdc077d8d3..ca63f780140f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -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) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 776600ddaea6..17f94663c941 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -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 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 029c846a236f..a4090db00d0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1278,10 +1278,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); @@ -3303,8 +3299,6 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) 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; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index fbe4e4a50852..a56c352a459a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -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) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c index 2269acc55c0e..738facceb240 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 8805ab344895..7d84ac26949c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -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,8 +1615,6 @@ 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); @@ -1644,41 +1640,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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 4ad3d32683d8..44029ceb8f77 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index bf24f8cb673e..b1dca76b7141 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -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, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index e1010521c3ea..d9a2801636cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -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; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index fdaeefa305e1..4aeb27ee9149 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -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; @@ -1212,11 +1128,7 @@ struct iwl_mvm { 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 +2011,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,10 +2021,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); @@ -2201,7 +2107,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 +2232,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 +2832,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 +2892,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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index c7f08cde1f72..5ebd046371f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 8fae0d41b119..8c1bb3a7ffca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 62e76a79f621..d8be2f6124c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2099,7 +2099,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 +2245,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 +2375,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 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 9ce1ce0dab34..b588f1dcf20d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -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) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 11c6b86db4ec..363232bb74fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -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); -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index f6906061510b..c25edc7c1813 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile index bb33f4a06f1c..2267be4cfb44 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile @@ -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 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/links.c b/drivers/net/wireless/intel/iwlwifi/mvm/tests/links.c deleted file mode 100644 index d692f1813d44..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tests/links.c +++ /dev/null @@ -1,433 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * KUnit tests for channel helper functions - * - * Copyright (C) 2024 Intel Corporation - */ -#include -#include "../mvm.h" -#include - -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); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index aa653782d6d7..0c9c2492d8a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -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); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 25d1a882a6a0..bb97837baeda 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -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; } -- cgit v1.2.3 From 2f72134320654a54eab44f9aaf81a385196de906 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:13 +0300 Subject: wifi: iwlwifi: mld: cleanup cipher lookup in resume We used to lookup the ciphers of the mcast keys, but this was beacuse it was required for ieee80211_get_rekey_add. Now as this API no longer needs the cipher as an argument, we can remove the cipher lookups. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.0650021c587b.Iae55243b575248cb4cc0b416f7f63092b5803219@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 81 +++-------------------------- 1 file changed, 7 insertions(+), 74 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index ed0a0f76f1c5..daa01fe62f84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -40,7 +40,7 @@ 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; + u32 num_keys; bool unhandled_cipher; }; @@ -708,11 +708,6 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, return; } - 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; @@ -721,20 +716,10 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, 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]); } @@ -750,65 +735,16 @@ 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; @@ -850,18 +786,15 @@ iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, 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); + link_conf); iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, - &wowlan_status->igtk, - link_conf, key_iter_data->igtk_cipher); + &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); + link_conf); } static bool -- cgit v1.2.3 From 9e7f13d27de963394ca326d02326091eae696e4b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:14 +0300 Subject: wifi: iwlwifi: mvm: cleanup cipher lookup in resume We used to lookup the ciphers of the mcast keys, but this was beacuse it was required for ieee80211_get_rekey_add. Now as this API no longer needs the cipher as an argument, we can remove the cipher lookups. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.937cbf5fd26e.I5d92258a9d63a39ee3acb02a72a2af9984993018@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 165 +++------------------------- 1 file changed, 14 insertions(+), 151 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index a4090db00d0b..a31bc2af5300 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1790,63 +1790,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) @@ -1892,9 +1837,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: @@ -1943,52 +1885,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)); - - key = ieee80211_gtk_rekey_add(vif, status->gtk[i].id, key_data, - sizeof(key_data), link_id); + "Received from FW GTK: key index %d\n", + status->gtk[i].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) @@ -2011,53 +1925,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) { @@ -2111,27 +1998,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 @@ -2145,17 +2011,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; } @@ -2164,7 +2028,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) { -- cgit v1.2.3 From 33d958b39ad0ca5f3741d11b092e94137ef0b13e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:15 +0300 Subject: wifi: iwlwifi: mld: support MLO rekey on resume When resuming from wowlan, update mac80211 on rekeys of MLO group keys and set the PN for those keys. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.fae2b42fbbfc.I7fcba97b6424577e49f7295f0c40b7d294ab56d8@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 126 ++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index daa01fe62f84..0ac6ceddb44f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -71,6 +71,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 +95,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 +116,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 \ @@ -407,19 +418,65 @@ iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, } } +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 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); + u32 len = iwl_rx_packet_payload_len(pkt); + u32 len_with_mlo_keys; - expected_len = sizeof(*notif); + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid wowlan_info_notif (expected=%zu got=%u)\n", + sizeof(*notif), len)) + return true; - if (IWL_FW_CHECK(mld, len < expected_len, - "Invalid wowlan_info_notif (expected=%ud got=%ud)\n", - expected_len, len)) + /* Now that we know that we have at least sizeof(notif), + * check also the variable length part + */ + len_with_mlo_keys = sizeof(*notif) + + notif->num_mlo_link_keys * sizeof(notif->mlo_gtks[0]); + + if (IWL_FW_CHECK(mld, len < len_with_mlo_keys, + "Invalid wowlan_info_notif (expected=%ud got=%u)\n", + len_with_mlo_keys, len)) return true; if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID, @@ -442,8 +499,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 @@ -687,7 +746,6 @@ 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; @@ -797,6 +855,57 @@ iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, 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 iwl_mld_update_sec_keys(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -831,9 +940,10 @@ iwl_mld_update_sec_keys(struct iwl_mld *mld, iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data, 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; } -- cgit v1.2.3 From 8925c7876c20b6c2b57512856e5b8100d2339d77 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:16 +0300 Subject: wifi: iwlwifi: mld: track BIGTK per link We track the BIGTKs installed for beacon protection purposes. But in MLO we will have a different BIGTK per link. Track the BIGTK per-link and not per-vif. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.0392769d3abb.I5d8e232d663e3ca8fc23de12dd8534cb076cabb9@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 9 +++----- drivers/net/wireless/intel/iwlwifi/mld/iface.h | 3 --- drivers/net/wireless/intel/iwlwifi/mld/key.c | 26 +++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/key.h | 4 ++++ drivers/net/wireless/intel/iwlwifi/mld/link.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 10 ++++----- drivers/net/wireless/intel/iwlwifi/mld/rx.c | 13 +++++++++--- 7 files changed, 49 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 0ac6ceddb44f..da621fe11d62 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -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" @@ -825,12 +826,8 @@ 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 diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 05dcb63701b1..38e10e279153 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c index 13462a5ad79a..a90477971c72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -368,3 +368,29 @@ 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); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.h b/drivers/net/wireless/intel/iwlwifi/mld/key.h index a68ea48913be..63de3469270a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.h @@ -36,4 +36,8 @@ 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); + #endif /* __iwl_mld_key_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index cad2c9426349..9e4da8e4de93 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index b0bd01914a91..f434012b03a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -2065,9 +2065,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 +2115,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 || diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index b6dedd1ecd4d..53cac9d8018c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -1619,12 +1619,14 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, 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; + u8 link_id; if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) == IWL_RX_MPDU_STATUS_SEC_NONE) @@ -1657,12 +1659,17 @@ 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; } @@ -1680,7 +1687,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; } -- cgit v1.2.3 From 6a1adca41f86b8c234068e5b3e961065a4a2d873 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:17 +0300 Subject: wifi: iwlwifi: mvm/mld: correctly retrieve the keyidx from the beacon key->icv_len already includes the pn length and the keyidx length. In fact it is the size of the MMIE, so subtracting it from the overall length will actually bring us to the beggining of the MMIE and not of the keyidx inside it. Also, we also need to consider a 16 byte long MIC. Fix the code to correctly retrieve the keyidx. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.e0aea411cd2a.I4220348147541a1478b02389475426047ecf84bc@changeid --- drivers/net/wireless/intel/iwlwifi/mld/rx.c | 13 ++++++++----- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 9 +++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index 53cac9d8018c..20d866dd92c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -1611,8 +1611,6 @@ 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, @@ -1626,6 +1624,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, 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) == @@ -1674,11 +1673,15 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, 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 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d8be2f6124c1..d35c63a673b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -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) { /* -- cgit v1.2.3 From 205a7309cccd34ad49c2b6b1b59b907c12395d6c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:18 +0300 Subject: wifi: iwlwifi: mld/mvm: set beacon protection capability in wowlan config Although the FW knows if a BIGTK was installed and can conclude from that the beacon protection capability, the specific component of the FW that is responsible for rekeying while in wowlan, doesn't know what keys were installed. So we need to tell that the FW when we go to wowlan, otherwise it will ignore the BIGTK rekey, if such occurs. Set this bit when needed. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.d3968487865e.I784f564ab85f618f26d3f082197a384bb219e07c@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 1 + drivers/net/wireless/intel/iwlwifi/mld/d3.c | 8 ++++++-- drivers/net/wireless/intel/iwlwifi/mld/key.c | 12 ++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/key.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 ++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index 53445087e9cb..9103f70c41c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -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), }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index da621fe11d62..86bb3a7a9f7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1570,7 +1570,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; @@ -1580,6 +1581,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 | @@ -1777,7 +1781,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) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c index a90477971c72..04192c5f07ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -394,3 +394,15 @@ void iwl_mld_track_bigtk(struct iwl_mld *mld, 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]); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.h b/drivers/net/wireless/intel/iwlwifi/mld/key.h index 63de3469270a..5a9efdaa3b03 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.h @@ -40,4 +40,7 @@ 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__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index a31bc2af5300..198a280b60f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -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); -- cgit v1.2.3 From 17e580918d24a7fc9e635f269855bbd35c03c5f9 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:19 +0300 Subject: wifi: iwlwifi: mvm: remove a function declaration iwl_mvm_average_dbm_values was removed, but the declaration wasn't. Remove it now. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821174726.2425334-2-miriam.rachel.korenblit@intel.com --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4aeb27ee9149..f02da4e0380f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2021,9 +2021,6 @@ struct iwl_mvm_link_sel_data { }; #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) - -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 -- cgit v1.2.3 From 8788f6b3c664c83441039a453a0ec9cd27bf4859 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:20 +0300 Subject: wifi: iwlwifi: bump MIN API in HR/GF/BZ/SC/DR Stop supporting API 98. Since API 99 will not be released, bump to 100. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821174726.2425334-3-miriam.rachel.korenblit@intel.com --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 9f543946b285..0acf52064132 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -13,7 +13,7 @@ #define IWL_BZ_UCODE_API_MAX 102 /* 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 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 807f4e29d55a..3c8fa8aa78ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -12,7 +12,7 @@ #define IWL_DR_UCODE_API_MAX 102 /* 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 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c index 7ff5170faaa9..ff2cc90f1896 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c @@ -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" diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c index 9f408d276ce9..6cf187d92dbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c @@ -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" diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 6d4a3bce49b9..ee1dc27362c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -13,7 +13,7 @@ #define IWL_SC_UCODE_API_MAX 102 /* 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 -- cgit v1.2.3 From 86adc88438156912535ed424033e700f7cb78990 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:21 +0300 Subject: Reapply "wifi: iwlwifi: remove support of several iwl_ppag_table_cmd versions" This reverts commit da75f183fea0 ("wifi: iwlwifi: Revert "wifi: iwlwifi: remove support of several iwl_ppag_table_cmd versions""). Now as we no longer support the FWs that required some old versions, this can be reapplied. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821174726.2425334-4-miriam.rachel.korenblit@intel.com --- drivers/net/wireless/intel/iwlwifi/fw/api/power.h | 20 ++++---------------- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 20 ++++++-------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 786b3bf4b448..ab84aac6605d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -571,8 +571,7 @@ 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 + * @v2: command version 5 structure. * @v3: command version 7 structure. * @v1.flags: values from &enum iwl_ppag_flags * @v1.gain: table of antenna gain values per chain and sub-band @@ -593,9 +592,7 @@ 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 v2; /* 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]; @@ -603,20 +600,11 @@ union iwl_ppag_table_cmd { } __packed v3; /* 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 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 3d6d1a85bb51..80d8373fccfc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -344,18 +344,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); + cmd->v2.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, @@ -378,17 +378,9 @@ 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 { -- cgit v1.2.3 From 0a477ddb6ec60127b39aa04c582ed2881fd58104 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:22 +0300 Subject: wifi: iwlwifi: make ppag versioning clear We used to have in iwl_ppag_table_cmd v2, that covered multiple FW versions of the command (2-6), so we just called it v2, and v3 for FW version 7. This is a bit confusing, and now v2 actually covers only FW version 5. Rename v2 to v5 and v3 to v7 so we don't have a different versioning than the FW has. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.e1240c6889e5.If7898bdf9ef70eed9c12484c03a3cc4f27635682@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/power.h | 18 +++++++++--------- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index ab84aac6605d..5eb8d10678fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -571,16 +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 5 structure. - * @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 { @@ -592,12 +592,12 @@ 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_5 */ + } __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_V1_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 80d8373fccfc..00921f86286a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -353,9 +353,9 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, } } 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 & IWL_PPAG_CMD_V5_MASK); + 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 +363,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; @@ -387,13 +387,13 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, 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++) { -- cgit v1.2.3 From e7e14d8e39d0a3fb4d90ced6b928cb53eb6dd4c0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:23 +0300 Subject: wifi: iwlwifi: mld: don't consider old versions of PPAG There is a utility function, iwl_fill_ppag_table, to fill the PPAG table according the version of the FW API and on of the BIOS table. But this function handles really old APIs that iwlmld will not support. Also, iwlmvm will no longer have new APIs of PPAG (because it is loaded on frozen devices only). So in the next versions we might introdue regressions to iwlmvm. Simply fill the PPAG table separately in iwlmld code, without using this utility. Reviewed-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.35698eb46b67.Ie77dc9c3ee8275d1c2e4eafa27f1c7899c2660ce@changeid --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 1 + .../net/wireless/intel/iwlwifi/mld/regulatory.c | 28 ++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 00921f86286a..6e98ac341997 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -305,6 +305,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) { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 75d2f5cb23a7..40571125b3ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -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); -- cgit v1.2.3 From 457b2a881f7b920112b0107bf2fdc373e748c7f4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:24 +0300 Subject: wifi: iwlwifi: mld: refactor iwl_mld_add_all_rekeys This receives iwl_mld_resume_key_iter_data, but it really only needs the mld object. Pass that instead. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.1d12ab0b5699.I201044d175b979520970090153de4d622652f86d@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 86bb3a7a9f7f..db0c83a425fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -831,24 +831,21 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, } 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], + 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); + 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], + iwl_mld_add_mcast_rekey(vif, mld, &wowlan_status->bigtk[i], link_conf); } @@ -934,7 +931,7 @@ iwl_mld_update_sec_keys(struct iwl_mld *mld, if (!key_iter_data.num_keys || !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); -- cgit v1.2.3 From 370fc69ed95ea3547dc106fb0508651fd4478412 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:25 +0300 Subject: wifi: iwlwifi: mld: rename iwl_mld_set_key_rx_seq This function should only be used for group keys. For pairwise keys we have iwl_mld_update_ptk_rx_seq. Make that clear from the name. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.ebf93a07905a.I8380b5cf9f6095b3a0b35fe4b7d56c544b921600@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index db0c83a425fa..b27b874b3e84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -679,8 +679,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: @@ -768,7 +768,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, } status_idx = key->keyidx == wowlan_status->gtk[1].id; - iwl_mld_set_key_rx_seq(key, &wowlan_status->gtk[status_idx]); + iwl_mld_update_mcast_rx_seq(key, &wowlan_status->gtk[status_idx]); break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: @@ -776,11 +776,13 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_AES_CMAC: if (key->keyidx == 4 || key->keyidx == 5) { if (key->keyidx == wowlan_status->igtk.id) - iwl_mld_set_key_rx_seq(key, &wowlan_status->igtk); + iwl_mld_update_mcast_rx_seq(key, + &wowlan_status->igtk); } if (key->keyidx == 6 || key->keyidx == 7) { status_idx = key->keyidx == wowlan_status->bigtk[1].id; - iwl_mld_set_key_rx_seq(key, &wowlan_status->bigtk[status_idx]); + iwl_mld_update_mcast_rx_seq(key, + &wowlan_status->bigtk[status_idx]); } break; default: @@ -807,7 +809,7 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, 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) { -- cgit v1.2.3 From 433570ee392f2f8f792a454bc2d34a4d3ce8d47d Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 21 Aug 2025 20:47:26 +0300 Subject: wifi: iwlwifi: mld: don't validate keys state on resume When resuming, we iterate over all the (installed) keys to update the PNs. If we find a key with an unexpected cipher we disconnect. But there is no reason for us to validate the internal key state specifically on resume, it should be the same as it was before the suspend. Remove the 'unhandled_cipher' from the iteration data. Also remove the num_keys indication as it is not really needed. If no keys were installed before the suspend, we will have num_of_gtk_rekeys = 0 and we will return early anyway. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250821204455.fb7e3bd4a967.I7eb24756ee27ad7b6731c0fb5dce5acb5d986694@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index b27b874b3e84..6a32aa22ffb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -41,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; - bool unhandled_cipher; }; struct iwl_mld_suspend_key_iter_data { @@ -747,14 +745,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; u8 status_idx; - 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: @@ -785,11 +776,7 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, &wowlan_status->bigtk[status_idx]); } break; - default: - data->unhandled_cipher = true; - return; } - data->num_keys++; } static void @@ -922,15 +909,10 @@ 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(mld, vif, wowlan_status, -- cgit v1.2.3 From da707495680bb429020c15c8f8daa744b08e11c4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:54:50 +0300 Subject: wifi: iwlwifi: mld: don't check the cipher on resume On resume, we are iterating all the keys in order to update the PN. Currently we check the cipher of the key we are currently iterating on to decide whether the key is PTK, GTK, IGTK or BIGTK. But we can find the type of the key by the keyidx, and we anyway have to check the keyidx, so just remove the cipher switch case and check only the keyidx instead Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.8c4f9c30242c.Ie34c200f321aae60771476fa9907c333a8a99747@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 43 +++++++++++++---------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 6a32aa22ffb8..dd8764029581 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -745,37 +745,32 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; u8 status_idx; - switch (key->cipher) { - 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; - } - - status_idx = key->keyidx == wowlan_status->gtk[1].id; - iwl_mld_update_mcast_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 (key->keyidx == wowlan_status->igtk.id) - iwl_mld_update_mcast_rx_seq(key, - &wowlan_status->igtk); - } - if (key->keyidx == 6 || key->keyidx == 7) { - status_idx = key->keyidx == wowlan_status->bigtk[1].id; + /* GTK */ + } else { + status_idx = key->keyidx == wowlan_status->gtk[1].id; iwl_mld_update_mcast_rx_seq(key, - &wowlan_status->bigtk[status_idx]); + &wowlan_status->gtk[status_idx]); } - break; + } + + /* 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]); } } -- cgit v1.2.3 From 14a4aca568f6e78af7564c6fc5f1ecc1a5a32c33 Mon Sep 17 00:00:00 2001 From: Somashekhar Puttagangaiah Date: Tue, 26 Aug 2025 18:54:51 +0300 Subject: wifi: iwlwifi: mld: trigger mlo scan only when not in EMLSR When beacon loss happens or the RSSI drops, trigger MLO scan only if not in EMLSR. The link switch was meant to be done when we are not in EMLSR and we can try to switch to a better link. If in EMLSR, we exit first and then trigger MLO scan. Signed-off-by: Somashekhar Puttagangaiah Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.f6ae8e3882cf.I60901c16487371b8e62019bd0bf25c45ab23752f@changeid --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 7 +++++-- drivers/net/wireless/intel/iwlwifi/mld/stats.c | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 782fc41aa1c3..dfaa6fbf8a54 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -572,8 +572,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 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index cbc64db5eab6..7b8709716324 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -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 = -- cgit v1.2.3 From bc4043ce7096a5e48bca255003ca58abd5b5b0e2 Mon Sep 17 00:00:00 2001 From: Itamar Shalev Date: Tue, 26 Aug 2025 18:54:52 +0300 Subject: wifi: iwlwifi: pcie: relocate finish_nic_init logic to gen1_2 Refactor finish_nic_init by moving its logic to the gen1_2 transport layer. Prepares for future gen3 support. Signed-off-by: Itamar Shalev Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.9fcc48d81435.I13403938219765b79c299ea081e8373d0e007785@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 89 +--------------------- .../wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 1 + .../net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 89 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 5e483a55a4ba..0a68378dd505 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -3,7 +3,6 @@ * Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation * Copyright (C) 2015-2016 Intel Deutschland GmbH */ -#include #include #include @@ -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) { @@ -396,94 +396,9 @@ 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 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_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; + return iwl_pcie_gen1_2_finish_nic_init(trans); } IWL_EXPORT_SYMBOL(iwl_finish_nic_init); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index f48aeebb151c..eca524c6a9f3 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1105,6 +1105,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_finish_nic_init(struct iwl_trans *trans); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 327366bf87de..3e50a935e1d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -32,6 +32,46 @@ #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 @@ -4271,3 +4311,52 @@ out_free_trans: iwl_trans_pcie_free(iwl_trans); return ret; } + +int iwl_pcie_gen1_2_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_pcie_dump_host_monitor(trans); + } + + if (mac_cfg->bisr_workaround) { + /* ensure BISR shift has finished */ + udelay(200); + } + + return err; +} -- cgit v1.2.3 From 80fb870262c81ea6221c4f7346f18a5caa48f6cd Mon Sep 17 00:00:00 2001 From: Itamar Shalev Date: Tue, 26 Aug 2025 18:54:53 +0300 Subject: wifi: iwlwifi: simplify iwl_poll_prph_bit return value Update iwl_poll_prph_bit to return 0 on success or an error code. Remove timing information from the return value, as it is unused. Signed-off-by: Itamar Shalev Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.6d5444d553ce.I769146e3ba78e12ccd014b4692492df6aea17ca1@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 0a68378dd505..fb645dafea38 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -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); -- cgit v1.2.3 From 27dc5813065016b1e4e896c67b5d698d46516c8c Mon Sep 17 00:00:00 2001 From: Somashekhar Puttagangaiah Date: Tue, 26 Aug 2025 18:54:54 +0300 Subject: wifi: iwlwifi: mld: Add debug log for second link When there is a missed beacon scenario its not clear how many times beacon missed, log this data. Signed-off-by: Somashekhar Puttagangaiah Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.70366c88907e.I39b5c121f9884cd572a19bf89f7cca02ce79eb33@changeid --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index dfaa6fbf8a54..6135da34a9c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -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; @@ -587,6 +588,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. -- cgit v1.2.3 From 7a1f7c5217606698c633102ea1e16317661dc38b Mon Sep 17 00:00:00 2001 From: Daniel Gabay Date: Tue, 26 Aug 2025 18:54:55 +0300 Subject: wifi: iwlwifi: mld: add few missing hcmd/notif names - SEC_KEY_CMD - REPLY_ERROR - PHY_CONFIGURATION_CMD - BT_CONFIG Signed-off-by: Daniel Gabay Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.badba3cc8be8.Ic8bc3ed4a1b05e80bbbc08636463a22ccbd8568f@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 7b46ccc306ab..a6962256bdd1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -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), -- cgit v1.2.3 From 5f708cccde9d1ea61bb50574d361d1c80fc1a248 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Aug 2025 18:54:56 +0300 Subject: wifi: iwlwifi: add a new FW file numbering scheme Firmware releases follow a "Core N" pattern, but due to some historical accidents, the API number for a Core N has always been N+3. That's confusing for everyone. For future firmware releases the firmware will make new file names that, instead of being named with the API number, will be named with the core number. For example, for the next one for bz/fm it'd be "iwlwifi-bz-b0-fm-c0-c99.ucode" instead of the now expected "iwlwifi-bz-b0-fm-c0-102.ucode". In the driver, represent that as an offset of 1000, and then request the "c" format instead of just "". When looking for older versions, skip from 1099 to 101 (which is core 98.) Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.7bba17bf200b.I33044cf6d97c623c47f765ea467635f01fc88731@changeid --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 16 +++++----- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 11 +++---- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 16 +++++----- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 39 ++++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 41 ++++++++++++++++++------- 5 files changed, 85 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 0acf52064132..3e6206e739f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -9,8 +9,8 @@ #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 100 @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 3c8fa8aa78ca..e53a785686c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -8,8 +8,8 @@ #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 100 @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index ee1dc27362c5..e9449b59114a 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -9,8 +9,8 @@ #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 100 @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 30e5f5a5cd89..d27c9ec151f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -107,6 +107,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 +195,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 +213,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". + */ +#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 +453,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; }; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 28aad975434b..6045a7915b91 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -341,6 +341,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) 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 +350,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 +366,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 +1593,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 +1656,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; } -- cgit v1.2.3 From a055bbb7bc91344a82497120a4af3e6e66c0d34f Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:54:57 +0300 Subject: wifi: iwlwifi: mvm: remove d3 test code This is no longer needed. Remove it from mvm, and a later patch will remove it from the transport layer. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.6727aba3f4cf.I5d702f3fd031c52629c9fd8a3f4835c892f7543a@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 188 +++-------------------- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 1 - drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 - drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 10 -- 4 files changed, 20 insertions(+), 182 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 198a280b60f9..9b1e96f1767f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1242,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 @@ -1258,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; } @@ -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, false, !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 { @@ -2590,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 */ @@ -2782,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 | \ @@ -3006,7 +2981,7 @@ 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; @@ -3017,7 +2992,7 @@ 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, &d3_status, false, !reset); if (ret) return ret; @@ -3070,7 +3045,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; @@ -3090,13 +3065,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, @@ -3161,7 +3135,7 @@ 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; } @@ -3207,7 +3181,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, @@ -3246,7 +3220,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); @@ -3334,125 +3308,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 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index f0e184c8a81a..289a0db1f91f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index f02da4e0380f..b515028adc8f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1119,9 +1119,6 @@ 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 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 62da0132f383..22602c32faa5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -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. -- cgit v1.2.3 From 6504e3f4c0fad33c63b25a19e3647985fec5e191 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:54:58 +0300 Subject: wifi: iwlwifi: remove dump file name extension support The options to configure a dump file name extension was added for 2 cases: 1. if we dump because of a missed beacon, we added the mac id and type to the filename. 2. to add the error id of the LMAC/UMAC/TCM/RCM error id to the file name. For 1, there is a bug: in cases in which missed beacon will not trigger a dump (for example in the default preset), and a missed beacon occurred, and eventually there is a dump for a different reason, the dump file name will contain the mac type and id even thought the dump has nothing to do with a missed beacon. Anyway, both cases are no longer required. Remove the code. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.d97f93fd1147.I7d0b056b6f9c38cafc53a0f29e0cf1236e2d2e8c@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 31 -------------- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 52 ----------------------- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 2 - drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 -- drivers/net/wireless/intel/iwlwifi/mld/link.c | 5 --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 4 -- 6 files changed, 98 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 2879be4b8fcb..09e8c93293e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2478,36 +2478,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 +2734,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); } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index f633124979ab..a39c038db08e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -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", @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 0444a736c2b2..9b116fa1d5d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -146,8 +146,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; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index d0e658801c2e..52f4a09c740f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -658,8 +658,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 +696,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; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 6135da34a9c1..738f80fe0c50 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -551,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); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 7d84ac26949c..9c9e0e1c6e1d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1619,10 +1619,6 @@ iwl_mvm_handle_missed_beacons_notif(struct iwl_mvm *mvm, 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); -- cgit v1.2.3 From b2e4bccc55b138616cce9127c78e4eaded240abc Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:54:59 +0300 Subject: wifi: iwlwifi: trans: remove d3 test code This is no longer needed. Remove it. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.4742846b17ed.I08c70ac544364d68baae03f830b1e01ce702b06d@changeid --- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 4 +-- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 8 ++--- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 +-- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 4 +-- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 ++-- .../wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 4 +-- .../net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 37 ++++++---------------- 7 files changed, 25 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 0771a46bd552..f1a39169eb4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -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; @@ -451,7 +451,7 @@ 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, &d3_status, true); if (ret) goto out_unlock; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 3694b41d6621..c5944e9fa6c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -507,13 +507,13 @@ 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); + err = iwl_trans_pcie_d3_suspend(trans, reset); if (!err) set_bit(STATUS_SUSPENDED, &trans->status); @@ -523,13 +523,13 @@ int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, bool 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) + bool reset) { int err; might_sleep(); - err = iwl_trans_pcie_d3_resume(trans, status, test, reset); + err = iwl_trans_pcie_d3_resume(trans, status, reset); clear_bit(STATUS_SUSPENDED, &trans->status); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 52f4a09c740f..b7e2355ece30 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -952,10 +952,10 @@ 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); + bool reset); struct iwl_trans_dump_data * iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index dd8764029581..aad944f8ab02 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1211,7 +1211,7 @@ 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); + ret = iwl_trans_d3_resume(mld->trans, &d3_status, 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"); @@ -1272,7 +1272,7 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) goto out; } - 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); } else { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9b1e96f1767f..d22ee06ff2c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1361,7 +1361,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - ret = iwl_trans_d3_suspend(mvm->trans, false, !unified_image); + ret = iwl_trans_d3_suspend(mvm->trans, !unified_image); out: if (ret < 0) { iwl_mvm_free_nd(mvm); @@ -2992,7 +2992,7 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm) 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, false, !reset); + ret = iwl_trans_d3_resume(mvm->trans, &d3_status, !reset); if (ret) return ret; @@ -3255,7 +3255,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); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index eca524c6a9f3..b6ff9d62fab7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1065,8 +1065,8 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, 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, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 3e50a935e1d0..ff0979d0b8b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -1437,17 +1437,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); @@ -1518,7 +1511,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; @@ -1531,26 +1524,19 @@ 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); @@ -1594,18 +1580,15 @@ 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) + 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 trans->state = IWL_TRANS_NO_FW; + } else { + *status = IWL_D3_STATUS_ALIVE; + return iwl_pcie_d3_handshake(trans, false); + } - return ret; + return 0; } static void -- cgit v1.2.3 From e769f6f27ffe41331e00b69a33aa8a34db4dd830 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:55:00 +0300 Subject: wifi: iwlwifi: trans: remove STATUS_SUSPENDED We needed this bit to prevent sending host commands when suspended in pseudo mode (in real suspension we can't send commands anyway). Now as pseudo mode is removed, we no longer need it. Remove. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.2642406f0e9f.Ic530fa7901bd6bd9df805c8f892357078377ac8b@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 22 ++-------------------- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 3 --- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index c5944e9fa6c3..d68a820c3d48 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -318,9 +318,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; @@ -408,8 +405,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); } @@ -509,31 +504,18 @@ iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask, int iwl_trans_d3_suspend(struct iwl_trans *trans, bool reset) { - int err; - might_sleep(); - err = iwl_trans_pcie_d3_suspend(trans, 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 reset) { - int err; - might_sleep(); - err = iwl_trans_pcie_d3_resume(trans, status, reset); - - clear_bit(STATUS_SUSPENDED, &trans->status); - - return err; + return iwl_trans_pcie_d3_resume(trans, status, reset); } IWL_EXPORT_SYMBOL(iwl_trans_d3_resume); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index b7e2355ece30..0fca6992ec5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -302,8 +302,6 @@ enum iwl_d3_status { * 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, @@ -318,7 +316,6 @@ enum iwl_trans_status { STATUS_IN_SW_RESET, STATUS_RESET_PENDING, STATUS_TRANS_RESET_IN_PROGRESS, - STATUS_SUSPENDED, }; static inline int -- cgit v1.2.3 From 49e58e9b0a4b4ddd42e8d0d341bb11d345cdce8f Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:55:01 +0300 Subject: wifi: iwlwifi: simplify iwl_trans_pcie_d3_resume If iwl_trans_d3_resume succeeded but the hw requested a reset, this will be indicated to the opmode via the iwl_d3_status parameter while the return value will be 0. But the opmode doesn't really care if the resume failed or if a restart is required. It acts the same in both cases (beside different logs, but this can be done in iwl_trans_pcie_d3_resume) This complicates the code for no good reason. Change the iwl_trans_pcie_d3_resume to return an error value also in the case that everything went successfully but a restart is required, and add more logs so we can differentiate between the cases. This makes iwl_d3_status redundant. Remove it as well. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.5fa2d909c75d.Ida19d8d8d73eddf12b30f1d473ea675f415778b2@changeid --- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 8 +------- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 5 ++--- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 13 +------------ drivers/net/wireless/intel/iwlwifi/mld/d3.c | 11 ++--------- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 8 +------- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 1 - drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 14 +++++++------- 7 files changed, 14 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index f1a39169eb4d..a0a26ef482a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -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, 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); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index d68a820c3d48..a19ffff2fffb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -510,12 +510,11 @@ int iwl_trans_d3_suspend(struct iwl_trans *trans, bool reset) } IWL_EXPORT_SYMBOL(iwl_trans_d3_suspend); -int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status, - bool reset) +int iwl_trans_d3_resume(struct iwl_trans *trans, bool reset) { might_sleep(); - return iwl_trans_pcie_d3_resume(trans, status, reset); + return iwl_trans_pcie_d3_resume(trans, reset); } IWL_EXPORT_SYMBOL(iwl_trans_d3_resume); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 0fca6992ec5b..b0bf88a889b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -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 @@ -951,8 +941,7 @@ void iwl_trans_stop_device(struct iwl_trans *trans); 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 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, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index aad944f8ab02..5d24292c45a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1195,7 +1195,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) @@ -1211,14 +1210,8 @@ 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); - 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) { iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d22ee06ff2c9..38832f5e4068 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2984,7 +2984,6 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, 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, @@ -2992,15 +2991,10 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm) 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, !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 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index b6ff9d62fab7..54b978830043 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1064,7 +1064,6 @@ 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 reset); int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool reset); void iwl_trans_pci_interrupts(struct iwl_trans *trans, bool enable); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index ff0979d0b8b0..0946ea223e46 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -1530,7 +1530,6 @@ int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool reset) } int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status, bool reset) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1545,8 +1544,11 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); ret = iwl_finish_nic_init(trans); - if (ret) + 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 @@ -1581,14 +1583,12 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, val = iwl_read32(trans, CSR_RESET); if (val & CSR_RESET_REG_FLAG_NEVO_RESET) { - *status = IWL_D3_STATUS_RESET; + IWL_INFO(trans, "Device was reset during suspend\n"); trans->state = IWL_TRANS_NO_FW; - } else { - *status = IWL_D3_STATUS_ALIVE; - return iwl_pcie_d3_handshake(trans, false); + return -ENOENT; } - return 0; + return iwl_pcie_d3_handshake(trans, false); } static void -- cgit v1.2.3 From 4b12516640b3c37f9fc9da6949957719e5d3ea0c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:55:02 +0300 Subject: wifi: iwlwifi: mld: don't modify trans state where not needed In suspend and resume flows, if we had any error we set the transport state to 'FW_ERROR' This was done to avoid sending commands when we shouldn't. In the mentioned flows, we can have a few types of errors: 1. logic errors 2. FW is in error state (can't send commands) 3. FW is misbehaving 4. D3 handshake error In the first, we can still talk to the firmware. In the second - the transport already knows about the FW error, no need to tell it. In the third - we need to treat it as any other FW misbehaviour. There is no reason to have a special handling here. So we only need it for the last type. Change the code to set the tansport state to FW error only in case of a d3 handshake error. While at it, add a comment explaining why the opmode sets the FW error bits. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.44c74ac0eb2a.Ic7369e622d908684f9b25ffc293d14c167c26414@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 35 +++++++---------------- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 7 +---- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 5d24292c45a5..8cd9d61a92e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1212,6 +1212,9 @@ static int iwl_mld_wait_d3_notif(struct iwl_mld *mld, ret = iwl_trans_d3_resume(mld->trans, false); if (ret) { + /* Avoid sending commands if the FW is dead */ + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); return ret; } @@ -1245,16 +1248,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, @@ -1262,24 +1260,21 @@ 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); 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 */ + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); } 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; } @@ -1299,15 +1294,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); @@ -1820,7 +1812,6 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) }; int link_id; int ret; - bool fw_err = false; lockdep_assert_wiphy(mld->wiphy); @@ -1863,7 +1854,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; } @@ -1900,11 +1890,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: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index f434012b03a6..debfb986a387 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -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; -- cgit v1.2.3 From d92185b1259b7cbd6686c4b81effc8d03e70baca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Aug 2025 18:55:03 +0300 Subject: wifi: iwlwifi: iwl-config: include module.h This file declares a macro using MODULE_FIRMWARE, so it should include module.h to always guarantee it being available. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.82a9e8f37c37.I5f4b310784bd49273ac905a79e8e3e0f39e15107@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index d27c9ec151f4..a607e7ab914b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "iwl-csr.h" #include "iwl-drv.h" -- cgit v1.2.3 From 5272d45914fff0e68ecb7ee9887f174770236ae3 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 18:55:04 +0300 Subject: wifi: iwlwifi: refactor iwl_pnvm_get_from_fs Instead of having an error code or 0 as a return value and passing a pointer to a pointer to be set by this function, change it to return a pointer, and use NULL as an error indication. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826184046.c752988eb0d1.I7b3a9a4f6a8d23663838a1e232f8bfad57c596ce@changeid --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 4d91ae065c8d..0421a84a44a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -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,31 +251,32 @@ 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; } 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; - u8 *image = NULL; /* Get PNVM from BIOS for non-Intel SKU */ if (sku_id[2]) { package = iwl_uefi_get_pnvm(trans_p, len); if (!IS_ERR_OR_NULL(package)) { + u8 *image = NULL; + if (*len >= sizeof(*package)) { /* we need only the data */ *len -= sizeof(*package); @@ -298,9 +300,7 @@ static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len, } /* 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; + return iwl_pnvm_get_from_fs(trans_p, len); } static void -- cgit v1.2.3 From f53f2bd8fc5f7d986b676a3f80505ba4d6b0b311 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Aug 2025 11:25:47 +0300 Subject: wifi: iwlwifi: uefi: remove runtime check of constant values There's no need to check an ARRAY_SIZE() at runtime, it's already determined at build time, so could be a BUILD_BUG_ON. However it's not that useful here since the array is defined using UEFI_MAX_DSM_FUNCS, check DSM_FUNC_NUM_FUNCS instead to ensure the array cannot be accessed out-of-band, i.e. ensure the range check there is always good enough. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.cc3c17327ea2.I99c7175be1f72f29b154454fc24978daafad476f@changeid --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 48126ec6b94b..44c7c565d1c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -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; - } - *value = data->functions[func]; IWL_DEBUG_RADIO(fwrt, -- cgit v1.2.3 From 1160c99ed93188a2b47636db0fd5093d189588f1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Aug 2025 11:25:48 +0300 Subject: wifi: iwlwifi: acpi: make iwl_guid static This variable is now only used in the same file, so there's no need to expose it. Make it static. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.83ec118cd1fb.I50c8e86fb786488f97e1ff2e115c4166c6b9bee1@changeid --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index bee7d92293b8..0bfc7a40d5a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -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), diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 68d8fb5f6357..20bc6671f4eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -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); -- cgit v1.2.3 From c8166b218540359146cd2b0ad150fa7e652cb304 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:49 +0300 Subject: wifi: iwlwifi: remove .pnvm files from module info For BZ/GF and SC/GF, we no longer have a .pnvm file, don't declare those Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.74f7fdba4ff6.Ia5e2123471df1fdc3688d8687a8e437388412206@changeid --- drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c index ff2cc90f1896..c16cda087a7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c @@ -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)); -- cgit v1.2.3 From 35adaa67354ad7aa588acff9594d261dd3304346 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:50 +0300 Subject: wifi: iwlwifi: trans: move dev_cmd_pool to trans specific This pool has different parameters for different devices, move it to the generation specific transport sub-layer. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.faf685de7aa2.I83e31e36d3159aa5c7e6f82a773d9981d3aac70d@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 27 +++++----- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 24 ++------- .../wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 23 +++++++++ .../net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 57 +++++++++++++--------- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index a19ffff2fffb..485e00d6158c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -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) @@ -345,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. * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index b0bf88a889b4..277fd4131999 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -854,9 +854,6 @@ struct iwl_trans_info { * @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 @@ -896,10 +893,6 @@ struct iwl_trans { 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 @@ -948,19 +941,12 @@ 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); @@ -1187,9 +1173,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) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 54b978830043..b5ff7a6da325 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -400,6 +400,9 @@ 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 */ struct iwl_trans_pcie { struct iwl_rxq *rxq; @@ -506,6 +509,9 @@ 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]; }; static inline struct iwl_trans_pcie * @@ -783,6 +789,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); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 0946ea223e46..7aa9683624f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -2023,6 +2023,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); } @@ -3707,28 +3708,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; } @@ -3738,18 +3751,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); @@ -3760,6 +3767,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; @@ -3779,7 +3790,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) @@ -3929,6 +3940,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); -- cgit v1.2.3 From 815cc0c75950d26347d3c7fbeb856251a5bcd0c0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:51 +0300 Subject: wifi: iwlwifi: don't publish TWT capabilities Due to the echosystem readiness this is not supported for now. Don't publish the capability. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828082601.537908-2-miriam.rachel.korenblit@intel.com --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index a67b9572aac3..c03f057ecddd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1042,10 +1042,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; -- cgit v1.2.3 From c40e28c47fc4e55e5f523f295f7096e5d9428608 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:52 +0300 Subject: wifi: iwlwifi: remove unneeded jacket indication This is only needed for internal builds. Don't read it. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.13a01468449c.Iab8255539567a82f0b9e0d1b269fababa2e72e61@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 8 ++------ drivers/net/wireless/intel/iwlwifi/fw/error-dump.h | 3 ++- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 16 +++------------- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 09e8c93293e5..10d016308d77 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index cf41021d59ad..c2a73cc85eff 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -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 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 7aa9683624f0..5643aac4032b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -4035,6 +4035,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); } @@ -4050,10 +4051,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 */ @@ -4102,21 +4101,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; } -- cgit v1.2.3 From aa9b9865a5534c08ae913fe6621bded5af3c65a6 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:53 +0300 Subject: wifi: iwlwifi: really remove hw_wfpm_id This was meant to be removed, but seems it was re-added to the info structure eventually. Anyway, it is not set or used so remove it. Reviewed-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.78e84f722963.I1bc574a315cd5a587439974ee250887954589321@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 277fd4131999..9f09629e2ac5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -813,7 +813,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. @@ -829,7 +828,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; -- cgit v1.2.3 From 6f2d548e0da60bc414d115f459c0fb2145462f62 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:54 +0300 Subject: wifi: iwlwifi: gen1_2: rename iwl_trans_pcie_op_mode_enter As a new version of this will be added for gen3, rename to iwl_pcie_gen1_2_op_mode_enter to distinguish between the different versions. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.64b3f290c397.I3ae2ca53330a8543bcbac32880824683f919ac74@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 485e00d6158c..26aafaf19eda 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -397,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); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index b5ff7a6da325..4f49473decb9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1070,7 +1070,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); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 5643aac4032b..bd503cb8f504 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -1905,7 +1905,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); -- cgit v1.2.3 From df70a9a86eee0d10fdb3fe02099176b5c75e4559 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:55 +0300 Subject: wifi: iwlwifi: gen1_2: move gen specific code to a function The remove function will be called also for gen3 devices, so move out the gen1_2 code to a function that will be called only for gen1/2 devices. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.a584254bcf83.I69d176b94d23f0f34d28733c48964f277a0a67a1@changeid --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 7 +------ drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 11 +++++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index b7add05f7a85..5cb0b519e762 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1171,16 +1171,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 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 4f49473decb9..b5114fb4eaa2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1103,6 +1103,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); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index bd503cb8f504..234a02223c20 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -4298,6 +4298,17 @@ out_free_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_finish_nic_init(struct iwl_trans *trans) { const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg; -- cgit v1.2.3 From 40f6e94d873fe29bd5d987f53772eb8d968b8103 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:56 +0300 Subject: wifi: iwlwifi: mld: support TLC command version 5 A new version of the TLC command was added in order to support the new MCSs intoduced in UHR, and an indication of ELR support. To support the new MCSs, the new version will have MCS bitmaps (ht_rates) of 32 bit and not 16 bit, as in the old version. Change the code to populate the new version of the command, and if the FW requires the old version, copy the content of the new version structure to the old version structure. Note that this doesn't actually set the new MCSs, this will come later. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.032a450cc279.Iecf6570c9fe11d8fbdc0718341ac92506b02d78c@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/rs.h | 35 ++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 75 +++++++++++++++++++------- 2 files changed, 91 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 3222cbcbe1ab..9c464e7aba10 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -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 + * 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 diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index a9ca92c0455e..0e172281b0c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -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); } -- cgit v1.2.3 From 1a33efe4fc64b8135fe94e22299761cc69333404 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:57 +0300 Subject: wifi: iwlwifi: pcie: remember when interrupts are disabled trans_pcie::fh_mask and hw_mask indicates what are the interrupts are currently enabled (unmasked). When we disable all interrupts, those should be set to 0, so if, for some reason, we get an interrupt even though it was disabled, we will know to ignore. Reviewed-by: Yedidya Ben Shimol Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.e293d6a8385b.I919375e5ad7bd7e4fee4a95ce6ce6978653d6b16@changeid --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index b5114fb4eaa2..79893e2d2701 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -841,6 +841,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"); } @@ -1023,6 +1025,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); } -- cgit v1.2.3 From 0755db9f2605e8dfc24857cf5ac1d9a8d4e91fc5 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:58 +0300 Subject: wifi: iwlwifi: mld: make iwl_mld_rm_vif void Unlike adding/allocating an object, destroying it should always succeed. In addition, the return value of iwl_mld_rm_vif is not even used. Make it a void function. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.418e898e908d.I18cc8d6b55a4e468dd155a40089ebea7de70594c@changeid --- drivers/net/wireless/intel/iwlwifi/mld/iface.c | 9 +++------ drivers/net/wireless/intel/iwlwifi/mld/iface.h | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 38993d65c052..c4738400ee11 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -451,24 +451,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, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 38e10e279153..a3573d20f214 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -224,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); -- cgit v1.2.3 From 187b114a2ab3d6d83792c659ca3998454446f44e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:25:59 +0300 Subject: wifi: iwlwifi: carefully select the PNVM source For newer device, and from API 100 (core 97), the PNVM should be taken from the .ucode file, and not from an external .pnvm file. In the current logic, if the PNVM doesn't exist in the .ucode file, we fallback to fetching the .ucode file. This is wrong and hides bugs. This fallback was needed for (a) old devices and (b) for newer devices with an old API. Since we no longer support those old APIs, (b) is not longer relevant. We can, according to the device, select the right PNVM source and fail if we couldn't find the PNVM there. Add clear logic to select the expected PNVM source, and print an error if we couldn't get the PNVM from there. Reviewed-by: Johannes Berg Reviewed-by: Daniel Gabay Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.7e75d33e3c28.I87fbcd25bbee733d2612206b76c2d8593d0cbd39@changeid --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 69 ++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 0421a84a44a8..f297e82d63d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -266,17 +266,60 @@ static u8 *iwl_pnvm_get_from_fs(struct iwl_trans *trans, size_t *len) 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)) { - u8 *image = NULL; - if (*len >= sizeof(*package)) { /* we need only the data */ *len -= sizeof(*package); @@ -291,16 +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 */ - return iwl_pnvm_get_from_fs(trans_p, len); + IWL_ERR(trans_p, "Couldn't get PNVM from required source: %d\n", pnvm_src); + return NULL; } static void -- cgit v1.2.3 From 8cab67474b97e613767618a081982da4bab2434d Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:26:00 +0300 Subject: wifi: iwlwifi: mld: remove a TODO This was already done. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.c445b2fc8bce.Ic616d605a4d6f82122466f50022cd046d229de4e@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index e57f5388fe77..99a9219438a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -735,12 +735,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); -- cgit v1.2.3 From 9b273ee9c084a7db2978e2101bbb6fc163701c85 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 28 Aug 2025 11:26:01 +0300 Subject: wifi: iwlwifi: don't support WH a step This is no longer supported. Fail the probe if such an HW is detected. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250828111032.8d484f21a237.I16a30af0b4b964339bd60c3bed854d1028c1fff8@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 6045a7915b91..607fcea6f4ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -337,6 +337,12 @@ 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) -- cgit v1.2.3 From 58febb47b961a91d0d12ee0c1618a7843c0908ce Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 28 Aug 2025 22:17:59 -0400 Subject: wifi: cfg80211: Remove unused tracepoints Tracepoints that are defined take up around 5K each, even if they are not used. If they are defined and not used, then they waste memory for unused code. Soon unused tracepoints will cause warnings. Remove the unused tracepoints of the cfg80211 subsystem. They are: cfg80211_chandef_dfs_required cfg80211_return_u32 cfg80211_return_uint cfg80211_send_rx_auth Signed-off-by: Steven Rostedt (Google) Link: https://patch.msgid.link/20250828221759.131160ee@batman.local.home Signed-off-by: Johannes Berg --- net/wireless/trace.h | 56 ---------------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 34c584a215e5..9b6074155d59 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3137,23 +3137,6 @@ DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate, TP_ARGS(netdev, macaddr) ); -DECLARE_EVENT_CLASS(netdev_evt_only, - TP_PROTO(struct net_device *netdev), - TP_ARGS(netdev), - TP_STRUCT__entry( - NETDEV_ENTRY - ), - TP_fast_assign( - NETDEV_ASSIGN; - ), - TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG) -); - -DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth, - TP_PROTO(struct net_device *netdev), - TP_ARGS(netdev) -); - TRACE_EVENT(cfg80211_send_rx_assoc, TP_PROTO(struct net_device *netdev, const struct cfg80211_rx_assoc_resp_data *data), @@ -3480,21 +3463,6 @@ TRACE_EVENT(cfg80211_reg_can_beacon, __entry->prohibited_flags, __entry->permitting_flags) ); -TRACE_EVENT(cfg80211_chandef_dfs_required, - TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), - TP_ARGS(wiphy, chandef), - TP_STRUCT__entry( - WIPHY_ENTRY - CHAN_DEF_ENTRY - ), - TP_fast_assign( - WIPHY_ASSIGN; - CHAN_DEF_ASSIGN(chandef); - ), - TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, - WIPHY_PR_ARG, CHAN_DEF_PR_ARG) -); - TRACE_EVENT(cfg80211_ch_switch_notify, TP_PROTO(struct net_device *netdev, struct cfg80211_chan_def *chandef, @@ -3862,30 +3830,6 @@ DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss, TP_ARGS(pub) ); -TRACE_EVENT(cfg80211_return_uint, - TP_PROTO(unsigned int ret), - TP_ARGS(ret), - TP_STRUCT__entry( - __field(unsigned int, ret) - ), - TP_fast_assign( - __entry->ret = ret; - ), - TP_printk("ret: %d", __entry->ret) -); - -TRACE_EVENT(cfg80211_return_u32, - TP_PROTO(u32 ret), - TP_ARGS(ret), - TP_STRUCT__entry( - __field(u32, ret) - ), - TP_fast_assign( - __entry->ret = ret; - ), - TP_printk("ret: %u", __entry->ret) -); - TRACE_EVENT(cfg80211_report_wowlan_wakeup, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_wowlan_wakeup *wakeup), -- cgit v1.2.3 From ac36daa83650c26fd55dee1a6ee5144769239911 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 26 Aug 2025 13:54:31 +0200 Subject: wifi: mac80211: Make CONNECTION_MONITOR optional for MLO sta Since commit '1bc892d76a6f ("wifi: mac80211: extend connection monitoring for MLO")' mac80211 supports connection monitor for MLO client interfaces. Remove the CONNECTION_MONITOR requirement in ieee80211_register_hw routine. Fixes: 1bc892d76a6f ("wifi: mac80211: extend connection monitoring for MLO") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20250826-remove-conn-mon-check-ieee80211_register_hw-v2-1-5a1e2f038245@kernel.org Signed-off-by: Johannes Berg --- net/mac80211/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9c8f18b258a6..beee51354931 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1164,9 +1164,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (WARN_ON(!ieee80211_hw_check(hw, MFP_CAPABLE))) return -EINVAL; - if (WARN_ON(!ieee80211_hw_check(hw, CONNECTION_MONITOR))) - return -EINVAL; - if (WARN_ON(ieee80211_hw_check(hw, NEED_DTIM_BEFORE_ASSOC))) return -EINVAL; -- cgit v1.2.3 From 1373f94148a5adac2f42c8ba9771105624fe4af0 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 26 Aug 2025 20:25:24 +0300 Subject: wifi: mac80211: count reg connection element in the size We currently don't count the reg connection length in the per-link capability length. Fix it. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250826202512.b14fc82f736b.I03442382e8a07f6f9836bcdac2e22ce8afbe6a21@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1008eb8e9b13..353e89973d1e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2104,8 +2104,11 @@ ieee80211_link_common_elems_size(struct ieee80211_sub_if_data *sdata, sizeof(struct ieee80211_he_mcs_nss_supp) + IEEE80211_HE_PPE_THRES_MAX_LEN; - if (sband->band == NL80211_BAND_6GHZ) + if (sband->band == NL80211_BAND_6GHZ) { size += 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa); + /* reg connection */ + size += 4; + } size += 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + sizeof(struct ieee80211_eht_mcs_nss_supp) + -- cgit v1.2.3 From b662bc503d95058b1cd640941edc48588849d9ce Mon Sep 17 00:00:00 2001 From: Darshan Rathod Date: Tue, 12 Aug 2025 18:06:36 +0530 Subject: wifi: brcmfmac: avoid assignment in if/else-if conditions in NVRAM load path The NVRAM selection logic in brcmf_fw_request_nvram_done() used patterns like: if ((data = bcm47xx_nvram_get_contents(&data_len))) free_bcm47xx_nvram = true; else if ((data = brcmf_fw_nvram_from_efi(&data_len))) kfree_nvram = true; This style violates kernel coding style guidelines and triggers checkpatch.pl errors. It also slightly reduces readability. Refactor these cases by separating the assignment and the check, ensuring behavior remains identical while complying with coding standards. Signed-off-by: Darshan Rathod Acked-by: Arend van Spriel Link: https://patch.msgid.link/20250812123636.2142292-1-darshanrathod475@gmail.com Signed-off-by: Johannes Berg --- .../net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 83f8ed7d00f9..ef79924fd8f4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -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) -- cgit v1.2.3 From f90caeba1dcacbba5956aa4219b83f8694aa5a79 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Aug 2025 14:10:39 +0900 Subject: wifi: iwlegacy: Remove unused structs and avoid -Wflex-array-member-not-at-end warnings Remove unused structures and avoid the following -Wflex-array-member-not-at-end warnings: drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h:68:39: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h:60:39: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] Signed-off-by: Gustavo A. R. Silva Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/aJl7TxeWgLdEKWhg@kspp Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h | 24 ---------------------- 1 file changed, 24 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h index 1e8ab704dbfb..f00eb878b94b 100644 --- a/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h +++ b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h @@ -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 -- cgit v1.2.3 From 7b6f16a258065f85793fb2597a290041c1e3d201 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Mon, 4 Aug 2025 15:58:27 +0200 Subject: wifi: mwifiex: add rgpower table loading support Marvell/NXP Wi-Fi adapters allow fine-grained adjustment of the transmit power levels and various other internal parameters. This is done by sending command streams to the adapter. One storage format of these command streams are the rgpower tables, which consist of multiple command blocks in the following format: command_block_1 = { XX XX LL LL XX XX .. } command_block_n = { XX XX LL LL XX XX XX .. } XX = raw byte as hex chars LL = total length of the "raw" command block These command blocks are parsed into their binary representation and then send to the adapter. The parsing logic was adapted from NXP's mwifiex driver[1]. The rgpower tables matching the currently set regulatory domain are automatically requested and applied. If not found the existing device tree provided power tables are tried as well. [1]: https://github.com/nxp-imx/mwifiex/blob/7a8beaa1605cb0870dc7ba3312c76df91cb0d6cf/mlan/mlan_cmdevt.c#L812 Signed-off-by: Stefan Kerkmann Link: https://patch.msgid.link/20250804-feature-mwifiex-rgpower-table-loading-v1-1-358e70a4d45e@pengutronix.de Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/main.c | 5 + drivers/net/wireless/marvell/mwifiex/main.h | 3 + drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 115 +++++++++++++++++++++++ drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 58 +++++++++++- 4 files changed, 180 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 7b50a88a18e5..36c931fe5322 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -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__); } diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 9ac36bef980e..27559e2ddc31 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -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; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index c93281f5a47c..6d9e2af29a69 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1483,6 +1483,121 @@ 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) { + kfree(hostcmd); + 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) { + memcpy(&hostcmd->len, &hostcmd->cmd[2], sizeof(u16)); + 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) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index f79589cafe57..ef6722ffdc74 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -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) { -- cgit v1.2.3 From 56819d00bc2ebaa6308913c28680da5d896852b8 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Mon, 4 Aug 2025 16:16:59 +0200 Subject: wifi: mwifiex: send world regulatory domain to driver The world regulatory domain is a restrictive subset of channel configurations which allows legal operation of the adapter all over the world. Changing to this domain should not be prevented. Fixes: dd4a9ac05c8e1 ("mwifiex: send regulatory domain info to firmware only if alpha2 changed") changed Signed-off-by: Stefan Kerkmann Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20250804-fix-mwifiex-regulatory-domain-v1-1-e4715c770c4d@pengutronix.de Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 3498743d5ec0..2090c99d9e9e 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -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); -- cgit v1.2.3 From 74e2ef72bd4b25ce21c8f309d4f5b91b5df9ff5b Mon Sep 17 00:00:00 2001 From: Gokul Sivakumar Date: Thu, 24 Jul 2025 15:41:36 +0530 Subject: wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW) Cypress(Infineon) is not the vendor for this 43752 SDIO WLAN chip, and so has not officially released any firmware binary for it. It is incorrect to maintain this WLAN chip with firmware vendor ID as "CYW". So relabel the chip's firmware Vendor ID as "WCC" as suggested by the maintainer. Fixes: d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") Fixes: f74f1ec22dc2 ("wifi: brcmfmac: add support for Cypress firmware api") Signed-off-by: Gokul Sivakumar Acked-by: Arend van Spriel Link: https://patch.msgid.link/20250724101136.6691-1-gokulkumar.sivakumar@infineon.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 8 ++++---- drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 1 - include/linux/mmc/sdio_ids.h | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 8ab7d1e34a6e..6a3f187320fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -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 */ } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 9074ab49e806..4239f2b21e54 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -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); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 8a0bad5119a0..8cf9d7e7c3f7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -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, diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index b39c5c1ee18b..df3b67ba4db2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -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 diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index fe3d6d98f8da..673cbdf43453 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -77,7 +77,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43439 0xa9af #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf #define SDIO_DEVICE_ID_BROADCOM_43751 0xaae7 -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 0xaae8 +#define SDIO_DEVICE_ID_BROADCOM_43752 0xaae8 #define SDIO_VENDOR_ID_CYPRESS 0x04b4 #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xbd3d -- cgit v1.2.3 From ee63609454838ea2b108f96f74a287be72d281ee Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 25 Jul 2025 23:22:19 +1000 Subject: wifi: mac80211: support block bitmap S1G TIM encoding An S1G TIM PVB is encoded differently compared to a non-s1g TIM PVB. As the AP dictates which encoding mode it uses, here we only implement block bitmap encoding. This is the default encoding mode used by all current vendor implementations. Additionally, S1G has a maximum AID count of 8192, however we are limiting the current implementation to 1600. This has no resemblence to the standard and is purely an implementation detail. The reason for this is due to the TIM elements maximum length of 255. This allows for, at most, 25 encoded blocks for a PVB encoded with block bitmap. Support for the maximum of 8192 AIDs will require an implementation of page slicing to be added to mac80211. As a result, we perform extra validation on both the STA and AP side when receiving an AID as an S1G interface. Add support for block bitmap encoding for an S1G AP and limit the maximum AID count to 1600 for the current mac80211 implementations. Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20250725132221.258217-2-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 +- net/mac80211/cfg.c | 10 ++- net/mac80211/ieee80211_i.h | 8 +++ net/mac80211/mlme.c | 19 ++--- net/mac80211/tx.c | 166 +++++++++++++++++++++++++++++++++---------- 5 files changed, 156 insertions(+), 50 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d1a14f2892d9..a4bc0c2729f6 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2283,7 +2283,8 @@ enum nl80211_commands { * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). * This is similar to @NL80211_ATTR_STA_AID but with a difference of being * allowed to be used with the first @NL80211_CMD_SET_STATION command to - * update a TDLS peer STA entry. + * update a TDLS peer STA entry. For S1G interfaces, this is limited to + * 1600 for the current mac80211 implementation. * * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2ed07fa121ab..4603350989c8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2171,10 +2171,16 @@ static int sta_apply_parameters(struct ieee80211_local *local, /* * cfg80211 validates this (1-2007) and allows setting the AID - * only when creating a new station entry + * only when creating a new station entry. For S1G APs, the current + * implementation supports a maximum of 1600 AIDs. */ - if (params->aid) + if (params->aid) { + if (sdata->vif.cfg.s1g && + params->aid > IEEE80211_MAX_SUPPORTED_S1G_AID) + return -EINVAL; + sta->sta.aid = params->aid; + } /* * Some of the following updates would be racy if called on an diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8afa2404eaa8..07f5fb11569b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -86,6 +86,14 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; #define IEEE80211_MAX_NAN_INSTANCE_ID 255 +/* + * Current mac80211 implementation supports a maximum of 1600 AIDS + * for S1G interfaces. With regards to an S1G TIM, this covers 25 blocks + * as each block is 64 AIDs. + */ +#define IEEE80211_MAX_SUPPORTED_S1G_AID 1600 +#define IEEE80211_MAX_SUPPORTED_S1G_TIM_BLOCKS 25 + enum ieee80211_status_data { IEEE80211_STATUS_TYPE_MASK = 0x00f, IEEE80211_STATUS_TYPE_INVALID = 0, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 353e89973d1e..f5d09ded9827 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6351,6 +6351,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, }; u8 ap_mld_addr[ETH_ALEN] __aligned(2); unsigned int link_id; + u16 max_aid = IEEE80211_MAX_AID; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -6377,10 +6378,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control); capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); - if (assoc_data->s1g) + if (assoc_data->s1g) { elem_start = mgmt->u.s1g_assoc_resp.variable; - else + max_aid = IEEE80211_MAX_SUPPORTED_S1G_AID; + } else { elem_start = mgmt->u.assoc_resp.variable; + } /* * Note: this may not be perfect, AP might misbehave - if @@ -6404,16 +6407,15 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (elems->aid_resp) aid = le16_to_cpu(elems->aid_resp->aid); - else if (assoc_data->s1g) - aid = 0; /* TODO */ else aid = le16_to_cpu(mgmt->u.assoc_resp.aid); /* - * The 5 MSB of the AID field are reserved - * (802.11-2016 9.4.1.8 AID field) + * The 5 MSB of the AID field are reserved for a non-S1G STA. For + * an S1G STA the 3 MSBs are reserved. + * (802.11-2016 9.4.1.8 AID field). */ - aid &= 0x7ff; + aid &= assoc_data->s1g ? 0x1fff : 0x7ff; sdata_info(sdata, "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", @@ -6450,7 +6452,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, event.u.mlme.reason = status_code; drv_event_callback(sdata->local, sdata, &event); } else { - if (aid == 0 || aid > IEEE80211_MAX_AID) { + if (aid == 0 || aid > max_aid) { sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n", aid); @@ -6488,6 +6490,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, } sdata->vif.cfg.aid = aid; + sdata->vif.cfg.s1g = assoc_data->s1g; if (!ieee80211_assoc_success(sdata, mgmt, elems, elem_start, elem_len)) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 00671ae45b2f..0ece8d89e094 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4882,15 +4882,114 @@ void ieee80211_tx_pending(struct tasklet_struct *t) /* functions for drivers to get certain frames */ +static void ieee80211_beacon_add_tim_pvb(struct ps_data *ps, + struct sk_buff *skb, + bool mcast_traffic) +{ + int i, n1 = 0, n2; + + /* + * Find largest even number N1 so that bits numbered 1 through + * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits + * (N2 + 1) x 8 through 2007 are 0. + */ + for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { + if (ps->tim[i]) { + n1 = i & 0xfe; + break; + } + } + n2 = n1; + for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { + if (ps->tim[i]) { + n2 = i; + break; + } + } + + /* Bitmap control */ + skb_put_u8(skb, n1 | mcast_traffic); + /* Part Virt Bitmap */ + skb_put_data(skb, ps->tim + n1, n2 - n1 + 1); +} + +/* + * mac80211 currently supports encoding using block bitmap mode, non + * inversed. The current implementation supports up to 1600 AIDs. + * + * Block bitmap encoding breaks down the AID bitmap into blocks of 64 + * AIDs. Each block contains between 0 and 8 subblocks. Each subblock + * describes 8 AIDs and the presence of a subblock is determined by + * the block bitmap. + */ +static void ieee80211_s1g_beacon_add_tim_pvb(struct ps_data *ps, + struct sk_buff *skb, + bool mcast_traffic) +{ + int blk; + + /* + * Emit a bitmap control block with a page slice number of 31 and a + * page index of 0 which indicates as per IEEE80211-2024 9.4.2.5.1 + * that the entire page (2048 bits) indicated by the page index + * is encoded in the partial virtual bitmap. + */ + skb_put_u8(skb, mcast_traffic | (31 << 1)); + + /* Emit an encoded block for each non-zero sub-block */ + for (blk = 0; blk < IEEE80211_MAX_SUPPORTED_S1G_TIM_BLOCKS; blk++) { + u8 blk_bmap = 0; + int sblk; + + for (sblk = 0; sblk < 8; sblk++) { + int sblk_idx = blk * 8 + sblk; + + /* + * If the current subblock is non-zero, increase the + * number of subblocks to emit for the current block. + */ + if (ps->tim[sblk_idx]) + blk_bmap |= BIT(sblk); + } + + /* If the current block contains no non-zero sublocks */ + if (!blk_bmap) + continue; + + /* + * Emit a block control byte for the current encoded block + * with an encoding mode of block bitmap (0x0), not inverse + * (0x0) and the current block offset (5 bits) + */ + skb_put_u8(skb, blk << 3); + + /* + * Emit the block bitmap for the current encoded block which + * contains the present subblocks. + */ + skb_put_u8(skb, blk_bmap); + + /* Emit the present subblocks */ + for (sblk = 0; sblk < 8; sblk++) { + int sblk_idx = blk * 8 + sblk; + + if (!(blk_bmap & BIT(sblk))) + continue; + + skb_put_u8(skb, ps->tim[sblk_idx]); + } + } +} + static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, struct ps_data *ps, struct sk_buff *skb, bool is_template) { - u8 *pos, *tim; - int aid0 = 0; - int i, have_bits = 0, n1, n2; + struct element *tim; + bool mcast_traffic = false, have_bits = false; struct ieee80211_bss_conf *link_conf = link->conf; + bool s1g = ieee80211_get_link_sband(link)->band == NL80211_BAND_S1GHZ; /* Generate bitmap for TIM only if there are any STAs in power save * mode. */ @@ -4898,7 +4997,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, /* in the hope that this is faster than * checking byte-for-byte */ have_bits = !bitmap_empty((unsigned long *)ps->tim, - IEEE80211_MAX_AID+1); + IEEE80211_MAX_AID + 1); + if (!is_template) { if (ps->dtim_count == 0) ps->dtim_count = link_conf->dtim_period - 1; @@ -4906,51 +5006,39 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, ps->dtim_count--; } - tim = pos = skb_put(skb, 5); - *pos++ = WLAN_EID_TIM; - *pos++ = 3; - *pos++ = ps->dtim_count; - *pos++ = link_conf->dtim_period; + /* Length is set after parsing the AID bitmap */ + tim = skb_put(skb, sizeof(struct element)); + tim->id = WLAN_EID_TIM; + skb_put_u8(skb, ps->dtim_count); + skb_put_u8(skb, link_conf->dtim_period); if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf)) - aid0 = 1; + mcast_traffic = true; - ps->dtim_bc_mc = aid0 == 1; + ps->dtim_bc_mc = mcast_traffic; if (have_bits) { - /* Find largest even number N1 so that bits numbered 1 through - * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits - * (N2 + 1) x 8 through 2007 are 0. */ - n1 = 0; - for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { - if (ps->tim[i]) { - n1 = i & 0xfe; - break; - } - } - n2 = n1; - for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { - if (ps->tim[i]) { - n2 = i; - break; - } - } - - /* Bitmap control */ - *pos++ = n1 | aid0; - /* Part Virt Bitmap */ - skb_put_data(skb, ps->tim + n1, n2 - n1 + 1); - - tim[1] = n2 - n1 + 4; + if (s1g) + ieee80211_s1g_beacon_add_tim_pvb(ps, skb, + mcast_traffic); + else + ieee80211_beacon_add_tim_pvb(ps, skb, mcast_traffic); } else { - *pos++ = aid0; /* Bitmap control */ - - if (ieee80211_get_link_sband(link)->band != NL80211_BAND_S1GHZ) { - tim[1] = 4; + /* + * If there is no buffered unicast traffic for an S1G + * interface, we can exclude the bitmap control. This is in + * contrast to other phy types as they do include the bitmap + * control and pvb even when there is no buffered traffic. + */ + if (!s1g) { + /* Bitmap control */ + skb_put_u8(skb, mcast_traffic); /* Part Virt Bitmap */ skb_put_u8(skb, 0); } } + + tim->datalen = skb_tail_pointer(skb) - tim->data; } static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, -- cgit v1.2.3 From e0c47c6229c25b54440fe1f84a0ff533942290b1 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 25 Jul 2025 23:22:20 +1000 Subject: wifi: mac80211: support parsing S1G TIM PVB An S1G TIM PVB has 3 mandatory encoding modes, that being block bitmap, single AID and OBL alongside the ability for each encoding mode to be inverted. Introduce the ability to parse the 3 encoding formats. The implementation specification for the encoding formats can be found in IEEE80211-2024 9.4.2.5. Signed-off-by: Arien Judge Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20250725132221.258217-3-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/carl9170/rx.c | 2 +- drivers/net/wireless/intersil/p54/txrx.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 2 +- drivers/net/wireless/realtek/rtlwifi/ps.c | 2 +- include/linux/ieee80211.h | 265 ++++++++++++++++++++++++- net/mac80211/mesh_ps.c | 2 +- net/mac80211/mlme.c | 3 +- 7 files changed, 263 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 908c4c8b7f82..6833430130f4 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -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); diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 2deb1bb54f24..1294a1d6528e 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -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); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 7db29e90eb4f..f8a6f9c968a1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -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); diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c index 6241e4fed4f6..bcab12c3b4c1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/ps.c +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c @@ -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; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e5a2096e022e..d350263f23f3 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -220,6 +220,12 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2) #define IEEE80211_MAX_AID_S1G 8191 #define IEEE80211_MAX_TIM_LEN 251 #define IEEE80211_MAX_MESH_PEERINGS 63 + +/* S1G encoding types */ +#define IEEE80211_S1G_TIM_ENC_MODE_BLOCK 0 +#define IEEE80211_S1G_TIM_ENC_MODE_SINGLE 1 +#define IEEE80211_S1G_TIM_ENC_MODE_OLB 2 + /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section 6.2.1.1.2. @@ -4757,15 +4763,8 @@ static inline unsigned long ieee80211_tu_to_usec(unsigned long tu) return 1024 * tu; } -/** - * ieee80211_check_tim - check if AID bit is set in TIM - * @tim: the TIM IE - * @tim_len: length of the TIM IE - * @aid: the AID to look for - * Return: whether or not traffic is indicated in the TIM for the given AID - */ -static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, - u8 tim_len, u16 aid) +static inline bool __ieee80211_check_tim(const struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid) { u8 mask; u8 index, indexn1, indexn2; @@ -4788,6 +4787,254 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, return !!(tim->virtual_map[index] & mask); } +struct s1g_tim_aid { + u16 aid; + u8 target_blk; /* Target block index */ + u8 target_subblk; /* Target subblock index */ + u8 target_subblk_bit; /* Target subblock bit */ +}; + +struct s1g_tim_enc_block { + u8 enc_mode; + bool inverse; + const u8 *ptr; + u8 len; + + /* + * For an OLB encoded block that spans multiple blocks, this + * is the offset into the span described by that encoded block. + */ + u8 olb_blk_offset; +}; + +/* + * Helper routines to quickly extract the length of an encoded block. Validation + * is also performed to ensure the length extracted lies within the TIM. + */ + +static inline int ieee80211_s1g_len_bitmap(const u8 *ptr, const u8 *end) +{ + u8 blkmap; + u8 n_subblks; + + if (ptr >= end) + return -EINVAL; + + blkmap = *ptr; + n_subblks = hweight8(blkmap); + + if (ptr + 1 + n_subblks > end) + return -EINVAL; + + return 1 + n_subblks; +} + +static inline int ieee80211_s1g_len_single(const u8 *ptr, const u8 *end) +{ + return (ptr + 1 > end) ? -EINVAL : 1; +} + +static inline int ieee80211_s1g_len_olb(const u8 *ptr, const u8 *end) +{ + if (ptr >= end) + return -EINVAL; + + return (ptr + 1 + *ptr > end) ? -EINVAL : 1 + *ptr; +} + +/* + * Enumerate all encoded blocks until we find the encoded block that describes + * our target AID. OLB is a special case as a single encoded block can describe + * multiple blocks as a single encoded block. + */ +static inline int ieee80211_s1g_find_target_block(struct s1g_tim_enc_block *enc, + const struct s1g_tim_aid *aid, + const u8 *ptr, const u8 *end) +{ + /* need at least block-control octet */ + while (ptr + 1 <= end) { + u8 ctrl = *ptr++; + u8 mode = ctrl & 0x03; + bool contains, inverse = ctrl & BIT(2); + u8 span, blk_off = ctrl >> 3; + int len; + + switch (mode) { + case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: + len = ieee80211_s1g_len_bitmap(ptr, end); + contains = blk_off == aid->target_blk; + break; + case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: + len = ieee80211_s1g_len_single(ptr, end); + contains = blk_off == aid->target_blk; + break; + case IEEE80211_S1G_TIM_ENC_MODE_OLB: + len = ieee80211_s1g_len_olb(ptr, end); + /* + * An OLB encoded block can describe more then one + * block, meaning an encoded OLB block can span more + * then a single block. + */ + if (len > 0) { + /* Minus one for the length octet */ + span = DIV_ROUND_UP(len - 1, 8); + /* + * Check if our target block lies within the + * block span described by this encoded block. + */ + contains = (aid->target_blk >= blk_off) && + (aid->target_blk < blk_off + span); + } + break; + default: + return -EOPNOTSUPP; + } + + if (len < 0) + return len; + + if (contains) { + enc->enc_mode = mode; + enc->inverse = inverse; + enc->ptr = ptr; + enc->len = (u8)len; + enc->olb_blk_offset = blk_off; + return 0; + } + + ptr += len; + } + + return -ENOENT; +} + +static inline bool ieee80211_s1g_parse_bitmap(struct s1g_tim_enc_block *enc, + struct s1g_tim_aid *aid) +{ + const u8 *ptr = enc->ptr; + u8 blkmap = *ptr++; + + /* + * If our block bitmap does not contain a set bit that corresponds + * to our AID, it could mean a variety of things depending on if + * the encoding mode is inverted or not. + * + * 1. If inverted, it means the entire subblock is present and hence + * our AID has been set. + * 2. If not inverted, it means our subblock is not present and hence + * it is all zero meaning our AID is not set. + */ + if (!(blkmap & BIT(aid->target_subblk))) + return enc->inverse; + + /* + * Increment ptr by the number of set subblocks that appear before our + * target subblock. If our target subblock is 0, do nothing as ptr + * already points to our target subblock. + */ + if (aid->target_subblk) + ptr += hweight8(blkmap & GENMASK(aid->target_subblk - 1, 0)); + + return !!(*ptr & BIT(aid->target_subblk_bit)) ^ enc->inverse; +} + +static inline bool ieee80211_s1g_parse_single(struct s1g_tim_enc_block *enc, + struct s1g_tim_aid *aid) +{ + /* + * Single AID mode describes, as the name suggests, a single AID + * within the block described by the encoded block. The octet + * contains the 6 LSBs of the AID described in the block. The other + * 2 bits are reserved. When inversed, every single AID described + * by the current block have buffered traffic except for the AID + * described in the single AID octet. + */ + return ((*enc->ptr & 0x3f) == (aid->aid & 0x3f)) ^ enc->inverse; +} + +static inline bool ieee80211_s1g_parse_olb(struct s1g_tim_enc_block *enc, + struct s1g_tim_aid *aid) +{ + const u8 *ptr = enc->ptr; + u8 blk_len = *ptr++; + /* + * Given an OLB encoded block that describes multiple blocks, + * calculate the offset into the span. Then calculate the + * subblock location normally. + */ + u16 span_offset = aid->target_blk - enc->olb_blk_offset; + u16 subblk_idx = span_offset * 8 + aid->target_subblk; + + if (subblk_idx >= blk_len) + return enc->inverse; + + return !!(ptr[subblk_idx] & BIT(aid->target_subblk_bit)) ^ enc->inverse; +} + +/* + * An S1G PVB has 3 non optional encoding types, each that can be inverted. + * An S1G PVB is constructed with zero or more encoded block subfields. Each + * encoded block represents a single "block" of AIDs (64), and each encoded + * block can contain one of the 3 encoding types alongside a single bit for + * whether the bits should be inverted. + * + * As the standard makes no guarantee about the ordering of encoded blocks, + * we must parse every encoded block in the worst case scenario given an + * AID that lies within the last block. + */ +static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid) +{ + int err; + struct s1g_tim_aid target_aid; + struct s1g_tim_enc_block enc_blk; + + if (tim_len < 3) + return false; + + target_aid.aid = aid; + target_aid.target_blk = (aid >> 6) & 0x1f; + target_aid.target_subblk = (aid >> 3) & 0x7; + target_aid.target_subblk_bit = aid & 0x7; + + /* + * Find our AIDs target encoded block and fill &enc_blk with the + * encoded blocks information. If no entry is found or an error + * occurs return false. + */ + err = ieee80211_s1g_find_target_block(&enc_blk, &target_aid, + tim->virtual_map, + (const u8 *)tim + tim_len + 2); + if (err) + return false; + + switch (enc_blk.enc_mode) { + case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: + return ieee80211_s1g_parse_bitmap(&enc_blk, &target_aid); + case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: + return ieee80211_s1g_parse_single(&enc_blk, &target_aid); + case IEEE80211_S1G_TIM_ENC_MODE_OLB: + return ieee80211_s1g_parse_olb(&enc_blk, &target_aid); + default: + return false; + } +} + +/** + * ieee80211_check_tim - check if AID bit is set in TIM + * @tim: the TIM IE + * @tim_len: length of the TIM IE + * @aid: the AID to look for + * @s1g: whether the TIM is from an S1G PPDU + * Return: whether or not traffic is indicated in the TIM for the given AID + */ +static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid, bool s1g) +{ + return s1g ? ieee80211_s1g_check_tim(tim, tim_len, aid) : + __ieee80211_check_tim(tim, tim_len, aid); +} + /** * ieee80211_get_tdls_action - get TDLS action code * @skb: the skb containing the frame, length will not be checked diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index 20e022a03933..ebab1f0a0138 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c @@ -586,7 +586,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta, if (sta->mesh->plink_state == NL80211_PLINK_ESTAB) has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, - sta->mesh->aid); + sta->mesh->aid, false); if (has_buffered) mps_dbg(sta->sdata, "%pM indicates buffered frames\n", diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f5d09ded9827..9568cc95a7ff 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7438,7 +7438,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ncrc = elems->crc; if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && - ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->aid)) { + ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->aid, + vif_cfg->s1g)) { if (local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { local->hw.conf.flags &= ~IEEE80211_CONF_PS; -- cgit v1.2.3 From 1860b1a8257c5d52fbed3093eb9a3e7476619403 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 25 Jul 2025 23:22:21 +1000 Subject: wifi: mac80211: kunit: add kunit tests for S1G PVB decoding Add support for testing the 6 examples mentioned in IEEE80211-2024 Annex L. These tests cover the 3 mandatory decoding modes being block bitmap, single AID and OLB alongside their equivalent inverses. Test output: 1..6 s1g_tim_block_test: Block 0 (ENC=BLOCK, blk_off=0, inverse=0) s1g_tim_block_test: octet 1 (ctrl) : 00000000 (0x00) s1g_tim_block_test: octet 2 (blk-map) : 00000101 (0x05) s1g_tim_block_test: octet 3 (SB 0) : 01000010 (0x42) s1g_tim_block_test: octet 4 (SB 2) : 10100000 (0xa0) ok 1 s1g_tim_block_test s1g_tim_single_test: Block 0 (ENC=SINGLE, blk_off=0, inverse=0) s1g_tim_single_test: octet 1 (ctrl) : 00000001 (0x01) s1g_tim_single_test: octet 2 (single) : 00011111 (0x1f) ok 2 s1g_tim_single_test s1g_tim_olb_test: Block 0 (ENC=OLB, blk_off=0, inverse=0) s1g_tim_olb_test: octet 1 (ctrl) : 00000010 (0x02) s1g_tim_olb_test: octet 2 (len= 9) : 00001001 (0x09) s1g_tim_olb_test: octet 3 (SB 0) : 01000010 (0x42) s1g_tim_olb_test: octet 4 (SB 1) : 10100000 (0xa0) s1g_tim_olb_test: octet 5 (SB 2) : 01000010 (0x42) s1g_tim_olb_test: octet 6 (SB 3) : 10100000 (0xa0) s1g_tim_olb_test: octet 7 (SB 4) : 01000010 (0x42) s1g_tim_olb_test: octet 8 (SB 5) : 10100000 (0xa0) s1g_tim_olb_test: octet 9 (SB 6) : 01000010 (0x42) s1g_tim_olb_test: octet 10 (SB 7) : 10100000 (0xa0) s1g_tim_olb_test: octet 11 (SB 8) : 01000010 (0x42) ok 3 s1g_tim_olb_test s1g_tim_inverse_block_test: Block 0 (ENC=BLOCK, blk_off=0, inverse=1) s1g_tim_inverse_block_test: octet 1 (ctrl) : 00000100 (0x04) s1g_tim_inverse_block_test: octet 2 (blk-map) : 00000101 (0x05) s1g_tim_inverse_block_test: octet 3 (SB 0) : 01000010 (0x42) s1g_tim_inverse_block_test: octet 4 (SB 2) : 10100000 (0xa0) ok 4 s1g_tim_inverse_block_test s1g_tim_inverse_single_test: Block 0 (ENC=SINGLE, blk_off=0, inverse=1) s1g_tim_inverse_single_test: octet 1 (ctrl) : 00000101 (0x05) s1g_tim_inverse_single_test: octet 2 (single) : 00011111 (0x1f) ok 5 s1g_tim_inverse_single_test s1g_tim_inverse_olb_test: Block 0 (ENC=OLB, blk_off=0, inverse=1) s1g_tim_inverse_olb_test: octet 1 (ctrl) : 00000110 (0x06) s1g_tim_inverse_olb_test: octet 2 (len= 9) : 00001001 (0x09) s1g_tim_inverse_olb_test: octet 3 (SB 0) : 01000010 (0x42) s1g_tim_inverse_olb_test: octet 4 (SB 1) : 10100000 (0xa0) s1g_tim_inverse_olb_test: octet 5 (SB 2) : 01000010 (0x42) s1g_tim_inverse_olb_test: octet 6 (SB 3) : 10100000 (0xa0) s1g_tim_inverse_olb_test: octet 7 (SB 4) : 01000010 (0x42) s1g_tim_inverse_olb_test: octet 8 (SB 5) : 10100000 (0xa0) s1g_tim_inverse_olb_test: octet 9 (SB 6) : 01000010 (0x42) s1g_tim_inverse_olb_test: octet 10 (SB 7) : 10100000 (0xa0) s1g_tim_inverse_olb_test: octet 11 (SB 8) : 01000010 (0x42) ok 6 s1g_tim_inverse_olb_test mac80211-s1g-tim: pass:6 fail:0 skip:0 total:6 Totals: pass:6 fail:0 skip:0 total:6 Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20250725132221.258217-4-lachlan.hodges@morsemicro.com [make tim_push() void, non-inline] Signed-off-by: Johannes Berg --- net/mac80211/tests/Makefile | 2 +- net/mac80211/tests/s1g_tim.c | 356 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 net/mac80211/tests/s1g_tim.c diff --git a/net/mac80211/tests/Makefile b/net/mac80211/tests/Makefile index 3b0c08356fc5..3c7f874e5c41 100644 --- a/net/mac80211/tests/Makefile +++ b/net/mac80211/tests/Makefile @@ -1,3 +1,3 @@ -mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o +mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o s1g_tim.o obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o diff --git a/net/mac80211/tests/s1g_tim.c b/net/mac80211/tests/s1g_tim.c new file mode 100644 index 000000000000..642fa4ece89f --- /dev/null +++ b/net/mac80211/tests/s1g_tim.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for S1G TIM PVB decoding. This test suite covers + * IEEE80211-2024 Annex L figures 8, 9, 10, 12, 13, 14. ADE mode + * is not covered as it is an optional encoding format and is not + * currently supported by mac80211. + * + * Copyright (C) 2025 Morse Micro + */ +#include +#include +#include + +#define MAX_AID 128 + +#define BC(enc_mode, inverse, blk_off) \ + ((((blk_off) & 0x1f) << 3) | ((inverse) ? BIT(2) : 0) | \ + ((enc_mode) & 0x3)) + +static void byte_to_bitstr(u8 v, char *out) +{ + for (int b = 7; b >= 0; b--) + *out++ = (v & BIT(b)) ? '1' : '0'; + *out = '\0'; +} + +static void dump_tim_bits(struct kunit *test, + const struct ieee80211_tim_ie *tim, u8 tim_len) +{ + const u8 *ptr = tim->virtual_map; + const u8 *end = (const u8 *)tim + tim_len; + unsigned int oct = 1; + unsigned int blk = 0; + char bits[9]; + + while (ptr < end) { + u8 ctrl = *ptr++; + u8 mode = ctrl & 0x03; + bool inverse = ctrl & BIT(2); + u8 blk_off = ctrl >> 3; + + kunit_info( + test, "Block %u (ENC=%s, blk_off=%u, inverse=%u)", blk, + (mode == IEEE80211_S1G_TIM_ENC_MODE_BLOCK) ? "BLOCK" : + (mode == IEEE80211_S1G_TIM_ENC_MODE_SINGLE) ? "SINGLE" : + "OLB", + blk_off, inverse); + + byte_to_bitstr(ctrl, bits); + kunit_info(test, " octet %2u (ctrl) : %s (0x%02x)", oct, + bits, ctrl); + ++oct; + + switch (mode) { + case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: { + u8 blkmap = *ptr++; + + byte_to_bitstr(blkmap, bits); + kunit_info(test, " octet %2u (blk-map) : %s (0x%02x)", + oct, bits, blkmap); + ++oct; + + for (u8 sb = 0; sb < 8; sb++) { + if (!(blkmap & BIT(sb))) + continue; + u8 sub = *ptr++; + + byte_to_bitstr(sub, bits); + kunit_info( + test, + " octet %2u (SB %2u) : %s (0x%02x)", + oct, sb, bits, sub); + ++oct; + } + break; + } + case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: { + u8 single = *ptr++; + + byte_to_bitstr(single, bits); + kunit_info(test, " octet %2u (single) : %s (0x%02x)", + oct, bits, single); + ++oct; + break; + } + case IEEE80211_S1G_TIM_ENC_MODE_OLB: { + u8 len = *ptr++; + + byte_to_bitstr(len, bits); + kunit_info(test, " octet %2u (len=%2u) : %s (0x%02x)", + oct, len, bits, len); + ++oct; + + for (u8 i = 0; i < len && ptr < end; i++) { + u8 sub = *ptr++; + + byte_to_bitstr(sub, bits); + kunit_info( + test, + " octet %2u (SB %2u) : %s (0x%02x)", + oct, i, bits, sub); + ++oct; + } + break; + } + default: + kunit_info(test, " ** unknown encoding 0x%x **", mode); + return; + } + blk++; + } +} + +static void tim_push(u8 **p, u8 v) +{ + *(*p)++ = v; +} + +static void tim_begin(struct ieee80211_tim_ie *tim, u8 **p) +{ + tim->dtim_count = 0; + tim->dtim_period = 1; + tim->bitmap_ctrl = 0; + *p = tim->virtual_map; +} + +static u8 tim_end(struct ieee80211_tim_ie *tim, u8 *tail) +{ + return tail - (u8 *)tim; +} + +static void pvb_add_block_bitmap(u8 **p, u8 blk_off, bool inverse, u8 blk_bmap, + const u8 *subblocks) +{ + u8 enc = IEEE80211_S1G_TIM_ENC_MODE_BLOCK; + u8 n = hweight8(blk_bmap); + + tim_push(p, BC(enc, inverse, blk_off)); + tim_push(p, blk_bmap); + + for (u8 i = 0; i < n; i++) + tim_push(p, subblocks[i]); +} + +static void pvb_add_single_aid(u8 **p, u8 blk_off, bool inverse, u8 single6) +{ + u8 enc = IEEE80211_S1G_TIM_ENC_MODE_SINGLE; + + tim_push(p, BC(enc, inverse, blk_off)); + tim_push(p, single6 & GENMASK(5, 0)); +} + +static void pvb_add_olb(u8 **p, u8 blk_off, bool inverse, const u8 *subblocks, + u8 len) +{ + u8 enc = IEEE80211_S1G_TIM_ENC_MODE_OLB; + + tim_push(p, BC(enc, inverse, blk_off)); + tim_push(p, len); + for (u8 i = 0; i < len; i++) + tim_push(p, subblocks[i]); +} + +static void check_all_aids(struct kunit *test, + const struct ieee80211_tim_ie *tim, u8 tim_len, + const unsigned long *expected) +{ + for (u16 aid = 1; aid <= MAX_AID; aid++) { + bool want = test_bit(aid, expected); + bool got = ieee80211_s1g_check_tim(tim, tim_len, aid); + + KUNIT_ASSERT_EQ_MSG(test, got, want, + "AID %u mismatch (got=%d want=%d)", aid, + got, want); + } +} + +static void fill_bitmap(unsigned long *bm, const u16 *list, size_t n) +{ + size_t i; + + bitmap_zero(bm, MAX_AID + 1); + for (i = 0; i < n; i++) + __set_bit(list[i], bm); +} + +static void fill_bitmap_inverse(unsigned long *bm, u16 max_aid, + const u16 *except, size_t n_except) +{ + bitmap_zero(bm, MAX_AID + 1); + for (u16 aid = 1; aid <= max_aid; aid++) + __set_bit(aid, bm); + + for (size_t i = 0; i < n_except; i++) + if (except[i] <= max_aid) + __clear_bit(except[i], bm); +} + +static void s1g_tim_block_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + static const u8 subblocks[] = { + 0x42, /* SB m=0: AIDs 1,6 */ + 0xA0, /* SB m=2: AIDs 21,23 */ + }; + u8 blk_bmap = 0x05; /* bits 0 and 2 set */ + bool inverse = false; + static const u16 set_list[] = { 1, 6, 21, 23 }; + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks); + tim_len = tim_end(tim, p); + + fill_bitmap(exp, set_list, ARRAY_SIZE(set_list)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static void s1g_tim_single_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + bool inverse = false; + u8 blk_off = 0; + u8 single6 = 0x1f; /* 31 */ + static const u16 set_list[] = { 31 }; + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_single_aid(&p, blk_off, inverse, single6); + tim_len = tim_end(tim, p); + + fill_bitmap(exp, set_list, ARRAY_SIZE(set_list)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static void s1g_tim_olb_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + bool inverse = false; + u8 blk_off = 0; + static const u16 set_list[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33, + 38, 45, 47, 49, 54, 61, 63, 65, 70 }; + static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42, + 0xA0, 0x42, 0xA0, 0x42 }; + u8 len = ARRAY_SIZE(subblocks); + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_olb(&p, blk_off, inverse, subblocks, len); + tim_len = tim_end(tim, p); + + fill_bitmap(exp, set_list, ARRAY_SIZE(set_list)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static void s1g_tim_inverse_block_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + /* Same sub-block content as Figure L-8, but inverse = true */ + static const u8 subblocks[] = { + 0x42, /* SB m=0: AIDs 1,6 */ + 0xA0, /* SB m=2: AIDs 21,23 */ + }; + u8 blk_bmap = 0x05; + bool inverse = true; + /* All AIDs except 1,6,21,23 are set */ + static const u16 except[] = { 1, 6, 21, 23 }; + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks); + tim_len = tim_end(tim, p); + + fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static void s1g_tim_inverse_single_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + bool inverse = true; + u8 blk_off = 0; + u8 single6 = 0x1f; /* 31 */ + /* All AIDs except 31 are set */ + static const u16 except[] = { 31 }; + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_single_aid(&p, blk_off, inverse, single6); + tim_len = tim_end(tim, p); + + fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static void s1g_tim_inverse_olb_test(struct kunit *test) +{ + u8 buf[256] = {}; + struct ieee80211_tim_ie *tim = (void *)buf; + u8 *p, tim_len; + bool inverse = true; + u8 blk_off = 0, len; + /* All AIDs except the list below are set */ + static const u16 except[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33, + 38, 45, 47, 49, 54, 61, 63, 65, 70 }; + static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42, + 0xA0, 0x42, 0xA0, 0x42 }; + len = ARRAY_SIZE(subblocks); + DECLARE_BITMAP(exp, MAX_AID + 1); + + tim_begin(tim, &p); + pvb_add_olb(&p, blk_off, inverse, subblocks, len); + tim_len = tim_end(tim, p); + + fill_bitmap_inverse(exp, 127, except, ARRAY_SIZE(except)); + + dump_tim_bits(test, tim, tim_len); + check_all_aids(test, tim, tim_len, exp); +} + +static struct kunit_case s1g_tim_test_cases[] = { + KUNIT_CASE(s1g_tim_block_test), + KUNIT_CASE(s1g_tim_single_test), + KUNIT_CASE(s1g_tim_olb_test), + KUNIT_CASE(s1g_tim_inverse_block_test), + KUNIT_CASE(s1g_tim_inverse_single_test), + KUNIT_CASE(s1g_tim_inverse_olb_test), + {} +}; + +static struct kunit_suite s1g_tim = { + .name = "mac80211-s1g-tim", + .test_cases = s1g_tim_test_cases, +}; + +kunit_test_suite(s1g_tim); -- cgit v1.2.3 From 5f9d5fd8e08968e66d0212f782fc24d76e52800f Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 12 Aug 2025 12:53:28 +0530 Subject: wifi: cfg80211: fix return value in cfg80211_get_radio_idx_by_chan() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a valid radio index is not found, the function returns -ENOENT. If the channel argument itself is invalid, it returns -EINVAL. However, since the caller only checks for < 0, the distinction between these error codes is not utilized much. Also, handling these two distinct error codes throughout the codebase adds complexity, as both cases must be addressed separately. A subsequent change aims to simplify this by using a single error code for all invalid cases, making error handling more consistent and streamlined. To support this change, update the return value to -EINVAL when a valid radio index is not found. This is still appropriate because, even if the channel argument is structurally valid, the absence of a corresponding radio index implies that the argument is effectively invalid—otherwise, a valid index would have been found. Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250812-fix_scan_ap_flag_requirement_during_mlo-v4-1-383ffb6da213@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- net/wireless/util.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 406626ff6cc8..cb1c36be2749 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -9548,7 +9548,7 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, * @wiphy: the wiphy * @chan: channel for which the supported radio index is required * - * Return: radio index on success or a negative error code + * Return: radio index on success or -EINVAL otherwise */ int cfg80211_get_radio_idx_by_chan(struct wiphy *wiphy, const struct ieee80211_channel *chan); diff --git a/net/wireless/util.c b/net/wireless/util.c index 240c68baa3d1..d12d49134c88 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2584,7 +2584,7 @@ int cfg80211_get_radio_idx_by_chan(struct wiphy *wiphy, } } - return -ENOENT; + return -EINVAL; } EXPORT_SYMBOL(cfg80211_get_radio_idx_by_chan); -- cgit v1.2.3 From cfb58d5fc964e7e008d8d64c8fa8e9e28e501bc6 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 12 Aug 2025 12:53:29 +0530 Subject: wifi: mac80211: simplify return value handling of cfg80211_get_radio_idx_by_chan() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In several instances where cfg80211_get_radio_idx_by_chan() is called, redundant checks are performed across function — such as verifying if wiphy->n_radio < 2 or if the returned index is negative. These checks are unnecessary, as the return value can be directly compared. Moreover, the function can be safely called even when radio-level properties are not explicitly advertised since in such case in each call it is going to get same error value. Therefore, simplify the usage of this function across all such cases by removing redundant conditions and relying on the return value directly. Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250812-fix_scan_ap_flag_requirement_during_mlo-v4-2-383ffb6da213@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 13 ------------- net/mac80211/chan.c | 11 ----------- net/mac80211/util.c | 15 ++++++--------- 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4603350989c8..fca93cd36bd3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3683,12 +3683,7 @@ static bool ieee80211_is_scan_ongoing(struct wiphy *wiphy, if (list_empty(&local->roc_list) && !local->scanning) return false; - if (wiphy->n_radio < 2) - return true; - req_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chandef->chan); - if (req_radio_idx < 0) - return true; if (local->scanning) { scan_req = wiphy_dereference(wiphy, local->scan_req); @@ -3707,14 +3702,6 @@ static bool ieee80211_is_scan_ongoing(struct wiphy *wiphy, list_for_each_entry(roc, &local->roc_list, list) { chan_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, roc->chan); - /* - * The roc work is added but chan_radio_idx is invalid. - * Should not happen but if it does, let's not take - * risk and return true. - */ - if (chan_radio_idx < 0) - return true; - if (chan_radio_idx == req_radio_idx) return true; } diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index c9cea0e7ac16..57065714cf8c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -659,19 +659,8 @@ bool ieee80211_is_radar_required(struct ieee80211_local *local, for_each_sdata_link(local, link) { if (link->radar_required) { - if (wiphy->n_radio < 2) - return true; - chan = link->conf->chanreq.oper.chan; radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan); - /* - * The radio index (radio_idx) is expected to be valid, - * as it's derived from a channel tied to a link. If - * it's invalid (i.e., negative), return true to avoid - * potential issues with radar-sensitive operations. - */ - if (radio_idx < 0) - return true; if (ieee80211_is_radio_idx_in_scan_req(wiphy, req, radio_idx)) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 32f1bc5908c5..51e3e3c913f7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4022,16 +4022,13 @@ bool ieee80211_is_radio_idx_in_scan_req(struct wiphy *wiphy, for (i = 0; i < scan_req->n_channels; i++) { chan = scan_req->channels[i]; chan_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan); - /* - * The chan_radio_idx should be valid since it's taken from a - * valid scan request. - * However, if chan_radio_idx is unexpectedly invalid (negative), - * we take a conservative approach and assume the scan request - * might use the specified radio_idx. Hence, return true. - */ - if (WARN_ON(chan_radio_idx < 0)) - return true; + /* The radio index either matched successfully, or an error + * occurred. For example, if radio-level information is + * missing, the same error value is returned. This + * typically implies a single-radio setup, in which case + * the operation should not be allowed. + */ if (chan_radio_idx == radio_idx) return true; } -- cgit v1.2.3 From 36b75dcb1e25739a3a0975699208c98f4b55d012 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 12 Aug 2025 12:53:30 +0530 Subject: wifi: mac80211: consider links for validating SCAN_FLAG_AP in scan request during MLO Commit 78a7a126dc5b ("wifi: mac80211: validate SCAN_FLAG_AP in scan request during MLO") introduced a check that rejects scan requests if any link is already beaconing. This works fine when all links share the same radio, but breaks down in multi-radio setups. Consider a scenario where a 2.4 GHz link is beaconing and a scan is requested on a 5 GHz link, each backed by a different physical radio. The current logic still blocks the scan, even though it should be allowed. As a result, interface bring-up fails unnecessarily in valid configurations. Fix this by checking whether the scan is being requested on the same underlying radio as the beaconing link. Only reject the scan if it targets a link that is already beaconing and the NL80211_FEATURE_AP_SCAN is not set. This ensures correct behavior in multi-radio environments and avoids false rejections. Fixes: 78a7a126dc5b ("wifi: mac80211: validate SCAN_FLAG_AP in scan request during MLO") Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250812-fix_scan_ap_flag_requirement_during_mlo-v4-3-383ffb6da213@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fca93cd36bd3..b26f61f13605 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3007,6 +3007,9 @@ static int ieee80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *req) { struct ieee80211_sub_if_data *sdata; + struct ieee80211_link_data *link; + struct ieee80211_channel *chan; + int radio_idx; sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev); @@ -3034,10 +3037,20 @@ static int ieee80211_scan(struct wiphy *wiphy, * the frames sent while scanning on other channel will be * lost) */ - if (ieee80211_num_beaconing_links(sdata) && - (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || - !(req->flags & NL80211_SCAN_FLAG_AP))) - return -EOPNOTSUPP; + for_each_link_data(sdata, link) { + /* if the link is not beaconing, ignore it */ + if (!sdata_dereference(link->u.ap.beacon, sdata)) + continue; + + chan = link->conf->chanreq.oper.chan; + radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan); + + if (ieee80211_is_radio_idx_in_scan_req(wiphy, req, + radio_idx) && + (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || + !(req->flags & NL80211_SCAN_FLAG_AP))) + return -EOPNOTSUPP; + } break; case NL80211_IFTYPE_NAN: default: -- cgit v1.2.3 From d0bf06158c39e7129524dd8b43b82aed84d68faa Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Fri, 15 Aug 2025 14:30:11 -0700 Subject: wifi: nl80211: Add EHT fixed Tx rate support Add new attributes to support EHT MCS/NSS Tx rates and EHT GI/LTF. Parse EHT fixed MCS/NSS Tx rates and EHT GI/LTF values passed by the userspace, validate and add as part of cfg80211_bitrate_mask. MCS mask is constructed by new function, eht_build_mcs_mask(). Max NSS supported for MCS rates of 7, 9, 11 and 13 is utilized to set MCS bitmask for each NSS. MCS rates 14, and 15 if supported, are set only for NSS = 0. Co-developed-by: Aloka Dixit Signed-off-by: Aloka Dixit Signed-off-by: Muna Sinada Link: https://patch.msgid.link/20250815213011.2704803-1-muna.sinada@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 + include/uapi/linux/nl80211.h | 41 +++++++- net/wireless/nl80211.c | 229 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 266 insertions(+), 7 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index cb1c36be2749..7d881aa7e48b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -841,9 +841,12 @@ struct cfg80211_bitrate_mask { u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; u16 vht_mcs[NL80211_VHT_NSS_MAX]; u16 he_mcs[NL80211_HE_NSS_MAX]; + u16 eht_mcs[NL80211_EHT_NSS_MAX]; enum nl80211_txrate_gi gi; enum nl80211_he_gi he_gi; + enum nl80211_eht_gi eht_gi; enum nl80211_he_ltf he_ltf; + enum nl80211_eht_ltf eht_ltf; } control[NUM_NL80211_BANDS]; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index a4bc0c2729f6..4f08264bbc8e 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1943,8 +1943,9 @@ enum nl80211_commands { * The driver must also specify support for this with the extended * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, * NL80211_EXT_FEATURE_BEACON_RATE_HT, - * NL80211_EXT_FEATURE_BEACON_RATE_VHT and - * NL80211_EXT_FEATURE_BEACON_RATE_HE. + * NL80211_EXT_FEATURE_BEACON_RATE_VHT, + * NL80211_EXT_FEATURE_BEACON_RATE_HE and + * NL80211_EXT_FEATURE_BEACON_RATE_EHT. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. @@ -3736,6 +3737,22 @@ enum nl80211_eht_gi { NL80211_RATE_INFO_EHT_GI_3_2, }; +/** + * enum nl80211_eht_ltf - EHT long training field + * @NL80211_RATE_INFO_EHT_1XLTF: 3.2 usec + * @NL80211_RATE_INFO_EHT_2XLTF: 6.4 usec + * @NL80211_RATE_INFO_EHT_4XLTF: 12.8 usec + * @NL80211_RATE_INFO_EHT_6XLTF: 19.2 usec + * @NL80211_RATE_INFO_EHT_8XLTF: 25.6 usec + */ +enum nl80211_eht_ltf { + NL80211_RATE_INFO_EHT_1XLTF, + NL80211_RATE_INFO_EHT_2XLTF, + NL80211_RATE_INFO_EHT_4XLTF, + NL80211_RATE_INFO_EHT_6XLTF, + NL80211_RATE_INFO_EHT_8XLTF, +}; + /** * enum nl80211_eht_ru_alloc - EHT RU allocation values * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation @@ -5482,6 +5499,10 @@ enum nl80211_key_attributes { * see &struct nl80211_txrate_he * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us. * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF. + * @NL80211_TXRATE_EHT: EHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_eht + * @NL80211_TXRATE_EHT_GI: configure EHT GI, (u8, see &enum nl80211_eht_gi) + * @NL80211_TXRATE_EHT_LTF: configure EHT LTF, (u8, see &enum nl80211_eht_ltf) * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -5494,6 +5515,9 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_HE, NL80211_TXRATE_HE_GI, NL80211_TXRATE_HE_LTF, + NL80211_TXRATE_EHT, + NL80211_TXRATE_EHT_GI, + NL80211_TXRATE_EHT_LTF, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -5526,6 +5550,15 @@ enum nl80211_txrate_gi { NL80211_TXRATE_FORCE_LGI, }; +#define NL80211_EHT_NSS_MAX 16 +/** + * struct nl80211_txrate_eht - EHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_eht { + __u16 mcs[NL80211_EHT_NSS_MAX]; +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band @@ -6650,6 +6683,9 @@ enum nl80211_feature_flags { * (signaling and payload protected) A-MSDUs and this shall be advertised * in the RSNXE. * + * @NL80211_EXT_FEATURE_BEACON_RATE_EHT: Driver supports beacon rate + * configuration (AP/mesh) with EHT rates. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6725,6 +6761,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_OWE_OFFLOAD_AP, NL80211_EXT_FEATURE_DFS_CONCURRENT, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, + NL80211_EXT_FEATURE_BEACON_RATE_EHT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89519aa52893..364d83fb9992 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -411,6 +411,14 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8, NL80211_RATE_INFO_HE_1XLTF, NL80211_RATE_INFO_HE_4XLTF), + [NL80211_TXRATE_EHT] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_eht)), + [NL80211_TXRATE_EHT_GI] = NLA_POLICY_RANGE(NLA_U8, + NL80211_RATE_INFO_EHT_GI_0_8, + NL80211_RATE_INFO_EHT_GI_3_2), + [NL80211_TXRATE_EHT_LTF] = NLA_POLICY_RANGE(NLA_U8, + NL80211_RATE_INFO_EHT_1XLTF, + NL80211_RATE_INFO_EHT_8XLTF), + }; static const struct nla_policy @@ -5393,6 +5401,164 @@ static bool he_set_mcs_mask(struct genl_info *info, return true; } +static void eht_build_mcs_mask(struct genl_info *info, + const struct ieee80211_sta_eht_cap *eht_cap, + u8 mcs_nss_len, u16 *mcs_mask) +{ + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 nss, mcs_7 = 0, mcs_9 = 0, mcs_11 = 0, mcs_13 = 0; + unsigned int link_id = nl80211_link_id(info->attrs); + + if (mcs_nss_len == 4) { + const struct ieee80211_eht_mcs_nss_supp_20mhz_only *mcs = + &eht_cap->eht_mcs_nss_supp.only_20mhz; + + mcs_7 = u8_get_bits(mcs->rx_tx_mcs7_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_9 = u8_get_bits(mcs->rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_11 = u8_get_bits(mcs->rx_tx_mcs11_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_13 = u8_get_bits(mcs->rx_tx_mcs13_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + + } else { + const struct ieee80211_eht_mcs_nss_supp_bw *mcs; + enum nl80211_chan_width width; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + width = wdev->u.ibss.chandef.width; + break; + case NL80211_IFTYPE_MESH_POINT: + width = wdev->u.mesh.chandef.width; + break; + case NL80211_IFTYPE_OCB: + width = wdev->u.ocb.chandef.width; + break; + default: + if (wdev->valid_links) + width = wdev->links[link_id].ap.chandef.width; + else + width = wdev->u.ap.preset_chandef.width; + break; + } + + switch (width) { + case NL80211_CHAN_WIDTH_320: + mcs = &eht_cap->eht_mcs_nss_supp.bw._320; + break; + case NL80211_CHAN_WIDTH_160: + mcs = &eht_cap->eht_mcs_nss_supp.bw._160; + break; + default: + mcs = &eht_cap->eht_mcs_nss_supp.bw._80; + break; + } + + mcs_7 = u8_get_bits(mcs->rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_9 = u8_get_bits(mcs->rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_11 = u8_get_bits(mcs->rx_tx_mcs11_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + mcs_13 = u8_get_bits(mcs->rx_tx_mcs13_max_nss, + IEEE80211_EHT_MCS_NSS_TX); + } + + /* Enable MCS 14 for NSS 0 */ + if (eht_cap->eht_cap_elem.phy_cap_info[6] & + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP) + mcs_mask[0] |= 0x4000; + + /* Enable MCS 15 for NSS 0 */ + mcs_mask[0] |= 0x8000; + + for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) { + if (!mcs_7) + continue; + mcs_mask[nss] |= 0x00FF; + mcs_7--; + + if (!mcs_9) + continue; + mcs_mask[nss] |= 0x0300; + mcs_9--; + + if (!mcs_11) + continue; + mcs_mask[nss] |= 0x0C00; + mcs_11--; + + if (!mcs_13) + continue; + mcs_mask[nss] |= 0x3000; + mcs_13--; + } +} + +static bool eht_set_mcs_mask(struct genl_info *info, struct wireless_dev *wdev, + struct ieee80211_supported_band *sband, + struct nl80211_txrate_eht *txrate, + u16 mcs[NL80211_EHT_NSS_MAX]) +{ + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + u16 tx_mcs_mask[NL80211_EHT_NSS_MAX] = { 0 }; + u8 i, mcs_nss_len; + + he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype); + if (!he_cap) + return false; + + eht_cap = ieee80211_get_eht_iftype_cap(sband, wdev->iftype); + if (!eht_cap) + return false; + + /* Checks for MCS 14 */ + if (txrate->mcs[0] & 0x4000) { + if (sband->band != NL80211_BAND_6GHZ) + return false; + + if (!(eht_cap->eht_cap_elem.phy_cap_info[6] & + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP)) + return false; + } + + mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem, + wdev->iftype == + NL80211_IFTYPE_STATION); + + if (mcs_nss_len == 3) { + /* Supported iftypes for setting non-20 MHZ only EHT MCS */ + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: + break; + default: + return false; + } + } + + /* Build eht_mcs_mask from EHT and HE capabilities */ + eht_build_mcs_mask(info, eht_cap, mcs_nss_len, tx_mcs_mask); + + memset(mcs, 0, sizeof(u16) * NL80211_EHT_NSS_MAX); + for (i = 0; i < NL80211_EHT_NSS_MAX; i++) { + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) + mcs[i] = txrate->mcs[i]; + else + return false; + } + + return true; +} + static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, struct nlattr *attrs[], enum nl80211_attrs attr, @@ -5413,6 +5579,8 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, /* Default to all rates enabled */ for (i = 0; i < NUM_NL80211_BANDS; i++) { const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + u8 mcs_nss_len; if (!default_all_enabled) break; @@ -5441,6 +5609,21 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, mask->control[i].he_gi = 0xFF; mask->control[i].he_ltf = 0xFF; + + eht_cap = ieee80211_get_eht_iftype_cap(sband, wdev->iftype); + if (!eht_cap) + continue; + + mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, + &eht_cap->eht_cap_elem, + wdev->iftype == + NL80211_IFTYPE_STATION); + + eht_build_mcs_mask(info, eht_cap, mcs_nss_len, + mask->control[i].eht_mcs); + + mask->control[i].eht_gi = 0xFF; + mask->control[i].eht_ltf = 0xFF; } /* if no rates are given set it back to the defaults */ @@ -5512,13 +5695,27 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, mask->control[band].he_ltf = nla_get_u8(tb[NL80211_TXRATE_HE_LTF]); + if (tb[NL80211_TXRATE_EHT] && + !eht_set_mcs_mask(info, wdev, sband, + nla_data(tb[NL80211_TXRATE_EHT]), + mask->control[band].eht_mcs)) + return -EINVAL; + + if (tb[NL80211_TXRATE_EHT_GI]) + mask->control[band].eht_gi = + nla_get_u8(tb[NL80211_TXRATE_EHT_GI]); + if (tb[NL80211_TXRATE_EHT_LTF]) + mask->control[band].eht_ltf = + nla_get_u8(tb[NL80211_TXRATE_EHT_LTF]); + if (mask->control[band].legacy == 0) { - /* don't allow empty legacy rates if HT, VHT or HE + /* don't allow empty legacy rates if HT, VHT, HE or EHT * are not even supported. */ if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || rdev->wiphy.bands[band]->vht_cap.vht_supported || - ieee80211_get_he_iftype_cap(sband, wdev->iftype))) + ieee80211_get_he_iftype_cap(sband, wdev->iftype) || + ieee80211_get_eht_iftype_cap(sband, wdev->iftype))) return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) @@ -5533,6 +5730,10 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, if (mask->control[band].he_mcs[i]) goto out; + for (i = 0; i < NL80211_EHT_NSS_MAX; i++) + if (mask->control[band].eht_mcs[i]) + goto out; + /* legacy and mcs rates may not be both empty */ return -EINVAL; } @@ -5546,7 +5747,7 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, enum nl80211_band band, struct cfg80211_bitrate_mask *beacon_rate) { - u32 count_ht, count_vht, count_he, i; + u32 count_ht, count_vht, count_he, count_eht, i; u32 rate = beacon_rate->control[band].legacy; /* Allow only one rate */ @@ -5592,8 +5793,21 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, return -EINVAL; } - if ((count_ht && count_vht && count_he) || - (!rate && !count_ht && !count_vht && !count_he)) + count_eht = 0; + for (i = 0; i < NL80211_EHT_NSS_MAX; i++) { + if (hweight16(beacon_rate->control[band].eht_mcs[i]) > 1) { + return -EINVAL; + } else if (beacon_rate->control[band].eht_mcs[i]) { + count_eht++; + if (count_eht > 1) + return -EINVAL; + } + if (count_eht && rate) + return -EINVAL; + } + + if ((count_ht && count_vht && count_he && count_eht) || + (!rate && !count_ht && !count_vht && !count_he && !count_eht)) return -EINVAL; if (rate && @@ -5613,6 +5827,11 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, NL80211_EXT_FEATURE_BEACON_RATE_HE)) return -EINVAL; + if (count_eht && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_BEACON_RATE_EHT)) + return -EINVAL; + return 0; } -- cgit v1.2.3 From 24185534915b5d926ded098336f47bdcca333aec Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 17 Aug 2025 21:04:32 +0200 Subject: wifi: nl80211: allow drivers to support subset of NL80211_CMD_SET_BSS The so-called fullmac devices rely on firmware functionality and/or API to change BSS parameters. Today there are limited drivers supporting the nl80211 primitive, but they only handle a subset of the bss parameters passed if any. The mac80211 driver does handle all parameters and stores their configured values. Some of the BSS parameters were already conditional by wiphy->features. For these the wiphy->bss_param_support and wiphy->features fields are silently aligned in wiphy_register(). Maybe better to issue a warning instead when they are misaligned. Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20250817190435.1495094-2-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 29 +++++++++++++++++++++++++++++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/core.c | 9 +++++++++ net/wireless/nl80211.c | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7d881aa7e48b..4072a67c9cc9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2459,6 +2459,29 @@ struct mpath_info { int generation; }; +/** + * enum wiphy_bss_param_flags - bit positions for supported bss parameters. + * + * @WIPHY_BSS_PARAM_CTS_PROT: support changing CTS protection. + * @WIPHY_BSS_PARAM_SHORT_PREAMBLE: support changing short preamble usage. + * @WIPHY_BSS_PARAM_SHORT_SLOT_TIME: support changing short slot time usage. + * @WIPHY_BSS_PARAM_BASIC_RATES: support reconfiguring basic rates. + * @WIPHY_BSS_PARAM_AP_ISOLATE: support changing AP isolation. + * @WIPHY_BSS_PARAM_HT_OPMODE: support changing HT operating mode. + * @WIPHY_BSS_PARAM_P2P_CTWINDOW: support reconfiguring ctwindow. + * @WIPHY_BSS_PARAM_P2P_OPPPS: support changing P2P opportunistic power-save. + */ +enum wiphy_bss_param_flags { + WIPHY_BSS_PARAM_CTS_PROT = BIT(0), + WIPHY_BSS_PARAM_SHORT_PREAMBLE = BIT(1), + WIPHY_BSS_PARAM_SHORT_SLOT_TIME = BIT(2), + WIPHY_BSS_PARAM_BASIC_RATES = BIT(3), + WIPHY_BSS_PARAM_AP_ISOLATE = BIT(4), + WIPHY_BSS_PARAM_HT_OPMODE = BIT(5), + WIPHY_BSS_PARAM_P2P_CTWINDOW = BIT(6), + WIPHY_BSS_PARAM_P2P_OPPPS = BIT(7), +}; + /** * struct bss_parameters - BSS parameters * @@ -5785,6 +5808,11 @@ struct wiphy_radio { * and probe responses. This value should be set if the driver * wishes to limit the number of csa counters. Default (0) means * infinite. + * @bss_param_support: bitmask indicating which bss_parameters as defined in + * &struct bss_parameters the driver can actually handle in the + * .change_bss() callback. The bit positions are defined in &enum + * wiphy_bss_param_flags. + * * @bss_select_support: bitmask indicating the BSS selection criteria supported * by the driver in the .connect() callback. The bit position maps to the * attribute indices defined in &enum nl80211_bss_select_attr. @@ -5970,6 +5998,7 @@ struct wiphy { u8 max_num_csa_counters; + u32 bss_param_support; u32 bss_select_support; u8 nan_supported_bands; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 4f08264bbc8e..6c07100fc01f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2930,6 +2930,9 @@ enum nl80211_commands { * required alongside this attribute. Refer to * @enum nl80211_s1g_short_beacon_attrs for the attribute definitions. * + * @NL80211_ATTR_BSS_PARAM: nested attribute used with %NL80211_CMD_GET_WIPHY + * which indicates which BSS parameters can be modified. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3491,6 +3494,7 @@ enum nl80211_attrs { NL80211_ATTR_S1G_LONG_BEACON_PERIOD, NL80211_ATTR_S1G_SHORT_BEACON, + NL80211_ATTR_BSS_PARAM, /* add attributes here, update the policy in nl80211.c */ diff --git a/net/wireless/core.c b/net/wireless/core.c index a7e2931ffb2e..797f9f2004a6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1018,6 +1018,15 @@ int wiphy_register(struct wiphy *wiphy) rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; + if (rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW) + rdev->wiphy.features |= NL80211_FEATURE_P2P_GO_CTWIN; + else if (rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN) + rdev->wiphy.bss_param_support |= WIPHY_BSS_PARAM_P2P_CTWINDOW; + if (rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS) + rdev->wiphy.features |= NL80211_FEATURE_P2P_GO_OPPPS; + else if (rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS) + rdev->wiphy.bss_param_support |= WIPHY_BSS_PARAM_P2P_OPPPS; + rtnl_lock(); wiphy_lock(&rdev->wiphy); res = device_add(&rdev->wiphy.dev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 364d83fb9992..153644a04072 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3027,6 +3027,40 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.ext_features)) goto nla_put_failure; + if (rdev->wiphy.bss_param_support) { + struct nlattr *nested; + u32 parsup = rdev->wiphy.bss_param_support; + + nested = nla_nest_start(msg, NL80211_ATTR_BSS_PARAM); + if (!nested) + goto nla_put_failure; + + if ((parsup & WIPHY_BSS_PARAM_CTS_PROT) && + nla_put_flag(msg, NL80211_ATTR_BSS_CTS_PROT)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_SHORT_PREAMBLE) && + nla_put_flag(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_SHORT_SLOT_TIME) && + nla_put_flag(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_BASIC_RATES) && + nla_put_flag(msg, NL80211_ATTR_BSS_BASIC_RATES)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_AP_ISOLATE) && + nla_put_flag(msg, NL80211_ATTR_AP_ISOLATE)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_HT_OPMODE) && + nla_put_flag(msg, NL80211_ATTR_BSS_HT_OPMODE)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_P2P_CTWINDOW) && + nla_put_flag(msg, NL80211_ATTR_P2P_CTWINDOW)) + goto nla_put_failure; + if ((parsup & WIPHY_BSS_PARAM_P2P_OPPPS) && + nla_put_flag(msg, NL80211_ATTR_P2P_OPPPS)) + goto nla_put_failure; + nla_nest_end(msg, nested); + } if (rdev->wiphy.bss_select_support) { struct nlattr *nested; u32 bss_select_support = rdev->wiphy.bss_select_support; @@ -9048,6 +9082,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct bss_parameters params; + u32 bss_param_support = rdev->wiphy.bss_param_support; memset(¶ms, 0, sizeof(params)); params.link_id = nl80211_link_id_or_invalid(info->attrs); @@ -9087,7 +9122,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.p2p_ctwindow = nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); if (params.p2p_ctwindow != 0 && - !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) + !(bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW)) return -EINVAL; } @@ -9099,7 +9134,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); params.p2p_opp_ps = tmp; if (params.p2p_opp_ps && - !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) + !(bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) return -EINVAL; } -- cgit v1.2.3 From 18abf7a05f1e171a290d8abc3078189ca0fe2db0 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 17 Aug 2025 21:04:33 +0200 Subject: wifi: drivers: indicate support for attributes in NL80211_CMD_SET_BSS The command NL80211_CMD_SET_BSS has a number of individual attributes and the driver can advertise which of those it will handle when it is changed by user-space. For drivers providing an empty .change_bss() the callback has been removed. Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20250817190435.1495094-3-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/microchip/wilc1000/cfg80211.c | 7 ------- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 -------- net/mac80211/main.c | 8 ++++++++ 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 7703a0933a14..7218fe70f3bc 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -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; diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index a395829ebadf..c39e7f313ea1 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -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, diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index ac3d085808e9..315bab373729 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -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, }; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index beee51354931..e8c85aa77c56 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -862,6 +862,14 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, if (emulate_chanctx || ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + wiphy->bss_param_support = WIPHY_BSS_PARAM_CTS_PROT | + WIPHY_BSS_PARAM_SHORT_PREAMBLE | + WIPHY_BSS_PARAM_SHORT_SLOT_TIME | + WIPHY_BSS_PARAM_BASIC_RATES | + WIPHY_BSS_PARAM_AP_ISOLATE | + WIPHY_BSS_PARAM_HT_OPMODE | + WIPHY_BSS_PARAM_P2P_CTWINDOW | + WIPHY_BSS_PARAM_P2P_OPPPS; wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | -- cgit v1.2.3 From 4f652a390db4246c5d3c51bf25d03ed0e4178fdc Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 17 Aug 2025 21:04:34 +0200 Subject: wifi: nl80211: strict checking attributes for NL80211_CMD_SET_BSS Assure user-space only modifies attributes for NL80211_CMD_SET_BSS that are supported by the driver. This stricter checking is only done when user-space commits to it by including NL80211_ATTR_BSS_PARAM. Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20250817190435.1495094-4-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 5 ++++- net/wireless/nl80211.c | 52 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6c07100fc01f..aed0b4c5d5e8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2931,7 +2931,10 @@ enum nl80211_commands { * @enum nl80211_s1g_short_beacon_attrs for the attribute definitions. * * @NL80211_ATTR_BSS_PARAM: nested attribute used with %NL80211_CMD_GET_WIPHY - * which indicates which BSS parameters can be modified. + * which indicates which BSS parameters can be modified. The attribute can + * also be used as flag attribute by user-space in %NL80211_CMD_SET_BSS to + * indicate that it wants strict checking on the BSS parameters to be + * modified. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 153644a04072..99e2aadc65f7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -879,6 +879,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2), [NL80211_ATTR_S1G_SHORT_BEACON] = NLA_POLICY_NESTED(nl80211_s1g_short_beacon), + [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -9083,6 +9084,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; struct bss_parameters params; u32 bss_param_support = rdev->wiphy.bss_param_support; + u32 changed = 0; + bool strict; memset(¶ms, 0, sizeof(params)); params.link_id = nl80211_link_id_or_invalid(info->attrs); @@ -9095,26 +9098,54 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.p2p_ctwindow = -1; params.p2p_opp_ps = -1; - if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) + strict = nla_get_flag(info->attrs[NL80211_ATTR_BSS_PARAM]); + if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) { + if (strict && !(bss_param_support & WIPHY_BSS_PARAM_CTS_PROT)) + return -EINVAL; params.use_cts_prot = nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) + changed |= WIPHY_BSS_PARAM_CTS_PROT; + } + if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) { + if (strict && + !(bss_param_support & WIPHY_BSS_PARAM_SHORT_PREAMBLE)) + return -EINVAL; params.use_short_preamble = nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) + changed |= WIPHY_BSS_PARAM_SHORT_PREAMBLE; + } + if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) { + if (strict && + !(bss_param_support & WIPHY_BSS_PARAM_SHORT_SLOT_TIME)) + return -EINVAL; params.use_short_slot_time = nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); + changed |= WIPHY_BSS_PARAM_SHORT_SLOT_TIME; + } if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { + if (strict && + !(bss_param_support & WIPHY_BSS_PARAM_BASIC_RATES)) + return -EINVAL; params.basic_rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); params.basic_rates_len = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + changed |= WIPHY_BSS_PARAM_BASIC_RATES; } - if (info->attrs[NL80211_ATTR_AP_ISOLATE]) - params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); - if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) + if (info->attrs[NL80211_ATTR_AP_ISOLATE]) { + if (strict && !(bss_param_support & WIPHY_BSS_PARAM_AP_ISOLATE)) + return -EINVAL; + params.ap_isolate = + !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); + changed |= WIPHY_BSS_PARAM_AP_ISOLATE; + } + if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) { + if (strict && !(bss_param_support & WIPHY_BSS_PARAM_HT_OPMODE)) + return -EINVAL; params.ht_opmode = nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); + changed |= WIPHY_BSS_PARAM_HT_OPMODE; + } if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) @@ -9124,6 +9155,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) if (params.p2p_ctwindow != 0 && !(bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW)) return -EINVAL; + changed |= WIPHY_BSS_PARAM_P2P_CTWINDOW; } if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { @@ -9132,9 +9164,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); + if (tmp && !(bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) + return -EINVAL; params.p2p_opp_ps = tmp; if (params.p2p_opp_ps && - !(bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) + !(rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) return -EINVAL; } @@ -9145,6 +9179,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; + changed &= rdev->wiphy.bss_param_support; + if (!changed) + return 0; + return rdev_change_bss(rdev, dev, ¶ms); } -- cgit v1.2.3 From d358795df908bb58d95cc85c25ed0424932e393c Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Sun, 17 Aug 2025 21:04:35 +0200 Subject: wifi: brcmfmac: support AP isolation to restrict reachability between stations hostapd & wpa_supplicant userspace daemons exposes an AP mode specific config file parameter "ap_isolate" to the user, which is used to control low-level bridging of frames between the stations associated in the BSS. In driver, handle this user setting in the newly defined cfg80211_ops function brcmf_cfg80211_change_bss() by enabling "ap_isolate" IOVAR in the firmware. In AP mode, the "ap_isolate" value from the cfg80211 layer 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 Signed-off-by: Wright Feng Signed-off-by: Chi-hsien Lin Signed-off-by: Gokul Sivakumar [arend: indicate ap_isolate support in struct wiphy::bss_param_support] Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20250817190435.1495094-5-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 8af402555b5e..8afaffe31031 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -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 | -- cgit v1.2.3 From 937d6aea5c6211fe2c8eb6f99b5baa6887a696c4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 3 Sep 2025 11:39:03 +0300 Subject: wifi: mac80211: reduce the scope of link_id Reduce the scope of the link_id variable in sta_set_sinfo to the 'if' scope. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250903083904.1972284-2-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8c550aab9bdc..8e275f0a1238 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2962,7 +2962,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; u32 thr = 0; - int i, ac, cpu, link_id; + int i, ac, cpu; struct ieee80211_sta_rx_stats *last_rxstats; last_rxstats = sta_get_last_rx_stats(sta, -1); @@ -3204,6 +3204,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, if (sta->sta.valid_links) { struct ieee80211_link_data *link; struct link_sta_info *link_sta; + int link_id; ether_addr_copy(sinfo->mld_addr, sta->addr); for_each_valid_link(sinfo, link_id) { -- cgit v1.2.3 From 7a7458ed0df90d4870f902fddc85b4a413ef7de4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 3 Sep 2025 11:39:04 +0300 Subject: wifi: mac80211: reduce the scope of rts_threshold This is only needed within the 'if' scope, not in the function scope. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250903083904.1972284-3-miriam.rachel.korenblit@intel.com Signed-off-by: Johannes Berg --- net/mac80211/util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 51e3e3c913f7..9eb35e3b9e52 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1756,7 +1756,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) bool sched_scan_stopped = false; bool suspended = local->suspended; bool in_reconfig = false; - u32 rts_threshold; lockdep_assert_wiphy(local->hw.wiphy); @@ -1832,7 +1831,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* setup RTS threshold */ if (hw->wiphy->n_radio > 0) { for (i = 0; i < hw->wiphy->n_radio; i++) { - rts_threshold = hw->wiphy->radio_cfg[i].rts_threshold; + u32 rts_threshold = + hw->wiphy->radio_cfg[i].rts_threshold; + drv_set_rts_threshold(local, i, rts_threshold); } } else { -- cgit v1.2.3 From e53f8b12a21c2974b66fa8c706090182da06fff3 Mon Sep 17 00:00:00 2001 From: Ramya Gnanasekar Date: Fri, 6 Jun 2025 16:14:36 +0530 Subject: wifi: mac80211: Fix 6 GHz Band capabilities element advertisement in lower bands Currently, when adding the 6 GHz Band Capabilities element, the channel list of the wiphy is checked to determine if 6 GHz is supported for a given virtual interface. However, in a multi-radio wiphy (e.g., one that has both lower bands and 6 GHz combined), the wiphy advertises support for all bands. As a result, the 6 GHz Band Capabilities element is incorrectly included in mesh beacon and station's association request frames of interfaces operating in lower bands, without verifying whether the interface is actually operating in a 6 GHz channel. Fix this by verifying if the interface operates on 6 GHz channel before adding the element. Note that this check cannot be placed directly in ieee80211_put_he_6ghz_cap() as the same function is used to add probe request elements while initiating scan in which case the interface may not be operating in any band's channel. Signed-off-by: Ramya Gnanasekar Signed-off-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20250606104436.326654-1-rameshkumar.sundaram@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 3 +++ net/mac80211/mlme.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a4a715f6f1c3..f37068a533f4 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -624,6 +624,9 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata, if (!sband) return -EINVAL; + if (sband->band != NL80211_BAND_6GHZ) + return 0; + iftd = ieee80211_get_sband_iftype_data(sband, NL80211_IFTYPE_MESH_POINT); /* The device doesn't support HE in mesh mode or at all */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9568cc95a7ff..83a9986dd1c4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1842,7 +1842,8 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata, ieee80211_put_he_cap(skb, sdata, sband, &assoc_data->link[link_id].conn); ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY); - ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode); + if (sband->band == NL80211_BAND_6GHZ) + ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode); } /* -- cgit v1.2.3 From e3ac93e9d916ebae0711a42f524429dad89c4887 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 5 Sep 2025 11:02:14 +0300 Subject: wifi: mwifiex: fix double free in mwifiex_send_rgpower_table() The "hostcmd" is freed using cleanup.h, so calling kfree() will lead to a double free. Delete the kfree(). Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support") Signed-off-by: Dan Carpenter Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/aLqZBh5_dSHUb4AE@stanley.mountain Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 6d9e2af29a69..91d5098081e8 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1521,10 +1521,8 @@ int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data, return -ENOMEM; _data = kmemdup(data, size, GFP_KERNEL); - if (!_data) { - kfree(hostcmd); + if (!_data) return -ENOMEM; - } pos = _data; ptr = hostcmd->cmd; -- cgit v1.2.3 From c67a2470922f03bdecc43ec1ae989b85757c62b6 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 23 Jul 2025 15:05:22 +0800 Subject: wifi: iwlwifi: Remove duplicated include in trans.c The header files fw/api/tx.h is included twice in gen1_2/trans.c, so one inclusion of each can be removed. Reported-by: Abaci Robot Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=22932 Signed-off-by: Yang Li Link: https://patch.msgid.link/20250723070522.2195817-1-yang.lee@linux.alibaba.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 234a02223c20..cda7c51b307c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -25,7 +25,6 @@ #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" -- cgit v1.2.3 From b4b34ba66443696cc5f3e95493f9d7597259b728 Mon Sep 17 00:00:00 2001 From: Liao Yuanhong Date: Tue, 19 Aug 2025 20:11:51 +0800 Subject: wifi: iwlwifi: Remove redundant header files The header file "fw/img.h" is already included on line 9. Remove the redundant include. Fixes: 2594e4d9e1a2d ("wifi: iwlwifi: prepare for reading SAR tables from UEFI") Signed-off-by: Liao Yuanhong Link: https://patch.msgid.link/20250819121201.608770-2-liaoyuanhong@vivo.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index a07c512b6ed4..735482e7adf5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -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 -- cgit v1.2.3 From 0d2ab5f922e75d10162e7199826e14df9cfae5cc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 27 Aug 2025 10:15:54 +0200 Subject: wifi: iwlwifi: mei: Remove unused flexible-array member in struct iwl_sap_hdr Remove unused fexible-array member and avoid 14 of the following type of warnings: drivers/net/wireless/intel/iwlwifi/mei/sap.h:318:28: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] Signed-off-by: Gustavo A. R. Silva Link: https://patch.msgid.link/aK6-usANI1UPtFVo@kspp Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mei/sap.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/sap.h b/drivers/net/wireless/intel/iwlwifi/mei/sap.h index ba1f75f739c2..f985ab90d41c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/sap.h +++ b/drivers/net/wireless/intel/iwlwifi/mei/sap.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[]; }; /** -- cgit v1.2.3 From ff46e2e7034c78489fa7a6bc35f7c9dd8ab82905 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 2 Sep 2025 12:09:49 +0800 Subject: wifi: iwlwifi: Fix dentry reference leak in iwl_mld_add_link_debugfs The debugfs_lookup() function increases the dentry reference count. Add missing dput() call to release the reference when the "iwlmld" directory already exists. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Cc: stable@vger.kernel.org Signed-off-by: Miaoqian Lin Link: https://patch.msgid.link/20250902040955.2362472-1-linmq006@gmail.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index cc052b0aa53f..372204bf8452 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -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, -- cgit v1.2.3 From 728d92a341bd74de0b803b7a0ed8f730832c0b22 Mon Sep 17 00:00:00 2001 From: Rotem Kerem Date: Tue, 9 Sep 2025 06:21:14 +0300 Subject: wifi: iwlwifi: add STATUS_FW_ERROR API Add iwl_trans_notify_fw_error() and iwl_trans_is_fw_error() for use by op modes. These helpers provide a clean interface for marking and checking firmware error state. This hides the trans internal implementation details from callers. Signed-off-by: Rotem Kerem Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.23f5160b3265.Iba325ffa4c6c6f7fc3a702fb6c1827b0857d0db3@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 14 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mld/d3.c | 6 ++---- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 5 ++--- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 10d016308d77..2ce55859641c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -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); @@ -3116,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, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 9f09629e2ac5..b9e41b8e2f15 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1209,6 +1209,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 *****************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 8cd9d61a92e8..f10732d31242 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1213,8 +1213,7 @@ static int iwl_mld_wait_d3_notif(struct iwl_mld *mld, ret = iwl_trans_d3_resume(mld->trans, false); if (ret) { /* Avoid sending commands if the FW is dead */ - mld->trans->state = IWL_TRANS_NO_FW; - set_bit(STATUS_FW_ERROR, &mld->trans->status); + iwl_trans_notify_fw_error(mld->trans); iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); return ret; } @@ -1267,8 +1266,7 @@ int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) 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 */ - mld->trans->state = IWL_TRANS_NO_FW; - set_bit(STATUS_FW_ERROR, &mld->trans->status); + 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); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 38832f5e4068..431504195e33 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -3102,7 +3102,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm) 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); @@ -3272,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"); @@ -3284,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; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index cda7c51b307c..f281d91475b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -3539,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); -- cgit v1.2.3 From b7a962584f2e98e5fb995dd6497db880c4dd30e9 Mon Sep 17 00:00:00 2001 From: Rotem Kerem Date: Tue, 9 Sep 2025 06:21:15 +0300 Subject: wifi: iwlwifi: replace SUPPRESS_CMD_ERROR_ONCE status bit with a boolean Convert STATUS_SUPPRESS_CMD_ERROR_ONCE from a status bit to a simple bool field in struct iwl_trans, as atomicity is not needed. Signed-off-by: Rotem Kerem Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.b33366c11a45.I75aac05afd8c4d8ef217d03327c1a027d6e7667e@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 11 +++-------- drivers/net/wireless/intel/iwlwifi/mld/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c | 5 +++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index b9e41b8e2f15..3d5b47aaa4dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -284,8 +284,6 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) * @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 @@ -302,7 +300,6 @@ 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, @@ -849,6 +846,8 @@ struct iwl_trans_info { * @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 @@ -883,6 +882,7 @@ 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; @@ -1194,11 +1194,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); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index 372204bf8452..b9c9cd3f44e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -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 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 289a0db1f91f..683c0ba5fb39 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -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 */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c index 84a05cc1c27a..24f1849d9eac 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c @@ -2601,8 +2601,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(); } -- cgit v1.2.3 From da4234c0c4057f2c6dac5e181a6e2759074611fa Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:16 +0300 Subject: wifi: iwlwifi: rename iwl_finish_nic_init The function is called so because for older devices it sets a bit called "init_done". But for the latest devices it sets a different bit, "mac_init". Since this name is not clear anyway, rename it such that it indicates the logic of the newer devices. Also add the 'trans' prefix so iw will be clear from the name that this is a transport API. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.a1de688e574b.Ibd41b0c8f7fbae77026e76dbbc085df3eecec538@changeid --- drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c | 2 +- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/iwl-io.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 12 ++++++------ 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c index 9f8cdb027839..d337ab543eb0 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index a39c038db08e..ddd714cff2f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -178,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; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index fb645dafea38..b1944584c693 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -396,11 +396,11 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf) return 0; } -int iwl_finish_nic_init(struct iwl_trans *trans) +int iwl_trans_activate_nic(struct iwl_trans *trans) { - return iwl_pcie_gen1_2_finish_nic_init(trans); + return iwl_pcie_gen1_2_activate_nic(trans); } -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) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 731cda1a4e66..5bcec239ffc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 5cb0b519e762..e68dce21df64 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1243,7 +1243,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); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 79893e2d2701..36fe5863f735 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1131,7 +1131,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_finish_nic_init(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, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c index 1951be3a30b7..b15c5d486527 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index f281d91475b4..922397408138 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -267,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; @@ -340,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 */ @@ -1542,7 +1542,7 @@ 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); + ret = iwl_trans_activate_nic(trans); if (ret) { IWL_ERR(trans, "Failed to init nic upon resume. err = %d\n", ret); @@ -1766,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; @@ -4188,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)) { @@ -4308,7 +4308,7 @@ void iwl_pcie_gen1_2_remove(struct iwl_trans *trans) iwl_trans_pcie_free(trans); } -int iwl_pcie_gen1_2_finish_nic_init(struct iwl_trans *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; -- cgit v1.2.3 From 579c6a6e28a18a6bf90667cb93fbdc3e8aee0466 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:17 +0300 Subject: wifi: iwlwifi: pcie: move pm_support to the specific transport Currently it is under iwl_trans, which is the bus agnostic part of the transport. But really it is relevant for pcie only, so move it to the iwl_trans_pcie and export it via an API to the opmode. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.997193cabd04.Ic6648f040430c94150d0fa11601f50a6a630b862@changeid --- drivers/net/wireless/intel/iwlwifi/dvm/power.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 9 +++++++++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 10 ++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c index 6b42d6e5f30f..e7dbba7134f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c @@ -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; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 26aafaf19eda..b428cb522d0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -807,3 +807,12 @@ 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); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3d5b47aaa4dc..e5d38b3bd76a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -844,7 +844,6 @@ 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 @@ -884,7 +883,6 @@ struct iwl_trans { 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; @@ -1262,4 +1260,6 @@ 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); + #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 36fe5863f735..2ee3bf4869d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -403,6 +403,7 @@ struct iwl_pcie_txqs { * @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 */ struct iwl_trans_pcie { struct iwl_rxq *rxq; @@ -512,6 +513,8 @@ struct iwl_trans_pcie { struct kmem_cache *dev_cmd_pool; char dev_cmd_pool_name[50]; + + bool pm_support; }; static inline struct iwl_trans_pcie * @@ -1151,4 +1154,11 @@ 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; +} + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 922397408138..c1fe87fb64c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -214,7 +214,7 @@ 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; -- cgit v1.2.3 From 6a5114d8bf7c55788d96e26592b08053e57b9058 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:18 +0300 Subject: wifi: iwlwifi: pcie: move ltr_enabled to the specific transport Currently it is under iwl_trans, which is the bus agnostic part of the transport. But really it is relevant for pcie only, so move it to the iwl_trans_pcie and export it via an API to the opmode. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.9f9dc80ab06c.I3eeca8b17abeba6ed30f0d681518c81ede0acf30@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 6 ++++++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h | 8 ++++++++ drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 4 ++-- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index b428cb522d0d..5232f66c2d52 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -816,3 +816,9 @@ bool iwl_trans_is_pm_supported(struct iwl_trans *trans) 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); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index e5d38b3bd76a..a0cc5d7745e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -844,7 +844,6 @@ 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 - * @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 @@ -883,7 +882,6 @@ struct iwl_trans { bool step_urm; bool suppress_cmd_error_once; - bool ltr_enabled; u8 pnvm_loaded:1; u8 fail_to_parse_pnvm_image:1; u8 reduce_power_loaded:1; @@ -1262,4 +1260,6 @@ static inline u16 iwl_trans_get_device_id(struct iwl_trans *trans) bool iwl_trans_is_pm_supported(struct iwl_trans *trans); +bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans); + #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d931c6eaf12f..865f973f677d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -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, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 2ee3bf4869d2..207c56e338dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -404,6 +404,7 @@ struct iwl_pcie_txqs { * 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; @@ -515,6 +516,7 @@ struct iwl_trans_pcie { char dev_cmd_pool_name[50]; bool pm_support; + bool ltr_enabled; }; static inline struct iwl_trans_pcie * @@ -1161,4 +1163,10 @@ static inline bool iwl_pcie_gen1_is_pm_supported(struct iwl_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__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index c1fe87fb64c6..59307b5df441 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -217,10 +217,10 @@ void iwl_pcie_apm_config(struct iwl_trans *trans) 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"); } /* -- cgit v1.2.3 From 59cb902371227c2cd7932a565eda97ac7e4707bf Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:19 +0300 Subject: wifi: iwlwifi: api: add a flag to iwl_link_ctx_modify_flags Add a new flag which, when set, will indicate that the UHR parameters in the link command have changed. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.b399aebbc384.I8a5a2728e71d92db67d4a4e0f4c358ca7b16ff51@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index b9f559dac39f..f76cea6e9ec8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -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 */ -- cgit v1.2.3 From 7a7c52645ce62314cdd69815e9d8fcb33e0042d5 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Tue, 9 Sep 2025 06:21:20 +0300 Subject: wifi: iwlwifi: add kunit tests for nvm parse nvm flags is part of nvm parse. Add test for VLP AP/CLIENT enable/disable scenarios. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.a7a4713fbfee.Ia0caf8865e63b40826c9b66084bd20438554f15c@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 76 ++-------------------- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 74 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/tests/Makefile | 2 +- .../net/wireless/intel/iwlwifi/tests/nvm_parse.c | 72 ++++++++++++++++++++ 4 files changed, 152 insertions(+), 72 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index c03f057ecddd..23465e4c4b39 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -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) { @@ -1596,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; @@ -1688,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) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 9ce9fa4e78fd..cbc92abf9f87 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -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 * diff --git a/drivers/net/wireless/intel/iwlwifi/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/tests/Makefile index 1b49241c578f..b996c45d43e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/tests/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/tests/Makefile @@ -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)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c b/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c new file mode 100644 index 000000000000..853911900bfd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/tests/nvm_parse.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for NVM parse + * + * Copyright (C) 2025 Intel Corporation + */ +#include +#include +#include + +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); -- cgit v1.2.3 From c5318e6e1c6436ce35ba521d96975e13cc5119f7 Mon Sep 17 00:00:00 2001 From: Nidhish A N Date: Tue, 9 Sep 2025 06:21:21 +0300 Subject: wifi: iwlwifi: fw: Add ASUS to PPAG and TAS list Add ASUS to the list of OEMs that are allowed to use the PPAG and TAS feature. Signed-off-by: Nidhish A N Reviewed-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.499af6568e89.Iafb2cb1c83ff82712c0e9d5529f76bc226ed12dd@changeid --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 6e98ac341997..e1f28b053253 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -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"), -- cgit v1.2.3 From 639401f62962f982168bad050e27a5af3feff31f Mon Sep 17 00:00:00 2001 From: Rotem Kerem Date: Tue, 9 Sep 2025 06:21:22 +0300 Subject: wifi: iwlwifi: implement wowlan status notification API update Add per key status indication in the WOWLAN status notification. This update is required for fips. Each key entry now includes a status field. Keys are now processed as follows: 0: no key, ignore entry 1: old, use only metadata 2: new, use key material and metadata While at it, fix tid_offloaded_tx error message to print the actual variable being validated in iwl_mld_handle_wowlan_info_notif. Signed-off-by: Rotem Kerem Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.0a2e7a62504c.Id195c9c83f9f767d1e3e458468af2d933774daa1@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 112 +++++++++- .../net/wireless/intel/iwlwifi/fw/api/offload.h | 2 +- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 233 +++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 8 +- 4 files changed, 306 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index 9103f70c41c0..d3bed0216df4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -632,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 @@ -645,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 @@ -701,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; @@ -736,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; @@ -818,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; @@ -833,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 @@ -855,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; @@ -870,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 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h b/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h index 2a1c2b0f19e4..bb801650a565 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/offload.h @@ -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, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index f10732d31242..1d4282a21f09 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -281,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; @@ -293,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 = @@ -304,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 @@ -317,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++; } } @@ -370,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 = @@ -382,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); } @@ -396,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; @@ -409,8 +428,17 @@ 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++; @@ -453,34 +481,165 @@ iwl_mld_convert_mlo_keys(struct iwl_mld *mld, } } +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; + 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); - u32 len_with_mlo_keys; - - if (IWL_FW_CHECK(mld, len < sizeof(*notif), - "Invalid wowlan_info_notif (expected=%zu got=%u)\n", - sizeof(*notif), len)) - return true; - - /* Now that we know that we have at least sizeof(notif), - * check also the variable length part - */ - len_with_mlo_keys = sizeof(*notif) + - notif->num_mlo_link_keys * sizeof(notif->mlo_gtks[0]); + int wowlan_info_ver = iwl_fw_lookup_notif_ver(mld->fw, + PROT_OFFLOAD_GROUP, + WOWLAN_INFO_NOTIFICATION, + IWL_FW_CMD_VER_UNKNOWN); + + if (wowlan_info_ver == 5) { + /* v5 format - validate before conversion */ + const struct iwl_wowlan_info_notif_v5 *notif_v5 = (void *)pkt->data; + + 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; + } - if (IWL_FW_CHECK(mld, len < len_with_mlo_keys, - "Invalid wowlan_info_notif (expected=%ud got=%u)\n", - len_with_mlo_keys, len)) + 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, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 431504195e33..07f1a84c274e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2082,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; @@ -2106,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; @@ -2137,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) { @@ -2907,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, -- cgit v1.2.3 From e5e8d86eb110838b0a06dc41093f8510e10e7d31 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:23 +0300 Subject: wifi: iwlwifi: mld: don't consider phy cmd version 5 It was planed that iwlmld will be loaded also for HR and GF, which has versions < 6. But eventually it was decided to keep use iwlmvm for those devices, so iwlmld doesn't need to support those versions. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.42501c7b0baa.I207ff53d259cc90781a0082320e2646b35925e5f@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index debfb986a387..5725104a53bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -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; /* -- cgit v1.2.3 From d243f5c112817762582e33c515aeb7c12d6f8146 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:24 +0300 Subject: wifi: iwlwifi: mld: remove support of mac cmd ver 2 The last FW API that supported ver 2 is API 99 (core 96) Since we no longer support it in any device that loads iwlmld, we can remove support of it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.a56bf370d31a.Ie1e93654ce9ee52e6ae3fda9bc898d611456ec41@changeid --- drivers/net/wireless/intel/iwlwifi/mld/iface.c | 30 +++++++------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index c4738400ee11..ed379825a923 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -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; -- cgit v1.2.3 From 9175f32e3f9869fd592158ae1a55c11860d7feed Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:25 +0300 Subject: wifi: iwlwifi: mld: remove support of roc cmd version 5 The last FW API that supports version 5 is 97. Since this API is no longer supported on any device that loads iwlmld, we can remove support of it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.e53bd8553360.I6978c216b52b818b879d076a85c5f9edafcf2e99@changeid --- drivers/net/wireless/intel/iwlwifi/mld/roc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c index e85f45bce79a..4136c98030d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/roc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -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"); -- cgit v1.2.3 From 79c0faefefbcf13991825ad0dba7aa2670fa8253 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:26 +0300 Subject: wifi: iwlwifi: mld: remove support from of sta cmd version 1 The last FW API that supports version 1 is 99. Since this API is no longer supported on any device that loads iwlmld, we can remove support of it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.4d6689d5c4b2.I5d2bf7302eea8ac7a805f58e4e60e527d6f5b346@changeid --- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 8fb51209b4a6..5cdbfa29a202 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -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; -- cgit v1.2.3 From 9a8a37c14f66eba5c75efa524afe7c983c27b477 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:27 +0300 Subject: wifi: iwlwifi: mld: remove support of iwl_esr_mode_notif version 1 The last FW API that supports version 5 is 99. Since this API is no longer supported on any device that loads iwlmld, we can remove support of it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.e15a7fa15c70.Ibb8636e826136c8c31931e77f5ffa853372b4301@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 28 ++++++++------------------ drivers/net/wireless/intel/iwlwifi/mld/notif.c | 1 - 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 99a9219438a6..241a6271d13d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -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: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index f17aeca4fae6..884973d0b344 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -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)) -- cgit v1.2.3 From bc4a45f8da56cd02bc6b861b7a1051a7d7665596 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 9 Sep 2025 06:21:28 +0300 Subject: wifi: iwlwifi: mld: CHANNEL_SURVEY_NOTIF is always supported This notification is supported in FW since API 100. Since we don't support any API older than that, we don't need to check whether the notification is supported. Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250909061931.b43f7da0d63b.I7386aeef5fae70dc4b0901cfb650eeaecb4c1575@changeid --- drivers/net/wireless/intel/iwlwifi/mld/scan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index 62f97a18a16c..fd1022ddc912 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -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; -- cgit v1.2.3 From eebccbfea4184feb758c104783b870ec4ddb6aec Mon Sep 17 00:00:00 2001 From: Sarika Sharma Date: Thu, 4 Sep 2025 16:10:54 +0530 Subject: wifi: mac80211: fix reporting of all valid links in sta_set_sinfo() Currently, sta_set_sinfo() fails to populate link-level station info when sinfo->valid_links is initially 0 and sta->sta.valid_links has bits set for links other than link 0. This typically occurs when association happens on a non-zero link or link 0 deleted dynamically. In such cases, the for_each_valid_link(sinfo, link_id) loop only executes for link 0 and terminates early, since sinfo->valid_links remains 0. As a result, only MLD-level information is reported to userspace. Hence to fix, initialize sinfo->valid_links with sta->sta.valid_links before entering the loop to ensure loop executes for each valid link. During iteration, mask out invalid links from sinfo->valid_links if any of sta->link[link_id], sdata->link[link_id], or sinfo->links[link_id] are not present, to report only valid link information. Fixes: 505991fba9ec ("wifi: mac80211: extend support to fill link level sinfo structure") Signed-off-by: Sarika Sharma Link: https://patch.msgid.link/20250904104054.790321-1-quic_sarishar@quicinc.com [clarify comment] Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8e275f0a1238..1bd75e0375a0 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3207,16 +3207,20 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, int link_id; ether_addr_copy(sinfo->mld_addr, sta->addr); + + /* assign valid links first for iteration */ + sinfo->valid_links = sta->sta.valid_links; + for_each_valid_link(sinfo, link_id) { link_sta = wiphy_dereference(sta->local->hw.wiphy, sta->link[link_id]); link = wiphy_dereference(sdata->local->hw.wiphy, sdata->link[link_id]); - if (!link_sta || !sinfo->links[link_id] || !link) + if (!link_sta || !sinfo->links[link_id] || !link) { + sinfo->valid_links &= ~BIT(link_id); continue; - - sinfo->valid_links = sta->sta.valid_links; + } sta_set_link_sinfo(sta, sinfo->links[link_id], link, tidstats); } -- cgit v1.2.3 From 906a5a8c7152ec2f76280f7224bb13adab64118c Mon Sep 17 00:00:00 2001 From: Sarika Sharma Date: Fri, 22 Aug 2025 10:51:10 +0530 Subject: wifi: mac80211: add tx_handlers_drop statistics to ethtool Currently tx_handlers_drop statistics are handled only for slow TX path and only at radio level. This also requires CONFIG_MAC80211_DEBUG_COUNTERS to be enabled to account the dropped packets. There is no way to check these stats for fast TX, at interface level and monitor without enabling the debug configuration. Hence, add a new counter at the sdata level to track packets dropped with reason as TX_DROP during transmission for fast path, slow path and other tx management packets. Expose this via ethtool statistics, to improve visibility into transmission failures at interface level and aid debugging and performance monitoring. Place the counter in ethtool with other available tx_* stats for better readability and accurate tracking. Sample output: root@buildroot:~# ethtool -S wlan0 NIC statistics: rx_packets: 5904 rx_bytes: 508122 rx_duplicates: 12 rx_fragments: 5900 rx_dropped: 12 tx_packets: 391487 tx_bytes: 600423383 tx_filtered: 0 tx_retry_failed: 10332 tx_retries: 1548 tx_handlers_drop: 4 .... Co-developed-by: Hari Chandrakanthan Signed-off-by: Hari Chandrakanthan Signed-off-by: Sarika Sharma Link: https://patch.msgid.link/20250822052110.513804-1-quic_sarishar@quicinc.com Signed-off-by: Johannes Berg --- net/mac80211/ethtool.c | 6 ++++-- net/mac80211/ieee80211_i.h | 1 + net/mac80211/tx.c | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 0397755a3bd1..3d365626faa4 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -48,8 +48,8 @@ static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { "rx_duplicates", "rx_fragments", "rx_dropped", "tx_packets", "tx_bytes", "tx_filtered", "tx_retry_failed", "tx_retries", - "sta_state", "txrate", "rxrate", "signal", - "channel", "noise", "ch_time", "ch_time_busy", + "tx_handlers_drop", "sta_state", "txrate", "rxrate", + "signal", "channel", "noise", "ch_time", "ch_time_busy", "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" }; #define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) @@ -120,6 +120,7 @@ static void ieee80211_get_stats(struct net_device *dev, i = 0; ADD_STA_STATS(&sta->deflink); + data[i++] = sdata->tx_handlers_drop; data[i++] = sta->sta_state; @@ -145,6 +146,7 @@ static void ieee80211_get_stats(struct net_device *dev, sta_set_sinfo(sta, &sinfo, false); i = 0; ADD_STA_STATS(&sta->deflink); + data[i++] = sdata->tx_handlers_drop; } } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 07f5fb11569b..8a666faeb1ec 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1218,6 +1218,7 @@ struct ieee80211_sub_if_data { } debugfs; #endif + u32 tx_handlers_drop; /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0ece8d89e094..a27e2af5d569 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1814,6 +1814,7 @@ static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { + tx->sdata->tx_handlers_drop++; I802_DEBUG_INC(tx->local->tx_handlers_drop); if (tx->skb) ieee80211_free_txskb(&tx->local->hw, tx->skb); @@ -1858,6 +1859,7 @@ static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { + tx->sdata->tx_handlers_drop++; I802_DEBUG_INC(tx->local->tx_handlers_drop); if (tx->skb) ieee80211_free_txskb(&tx->local->hw, tx->skb); @@ -1942,6 +1944,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, if (unlikely(res_prepare == TX_DROP)) { ieee80211_free_txskb(&local->hw, skb); + tx.sdata->tx_handlers_drop++; return true; } else if (unlikely(res_prepare == TX_QUEUED)) { return true; @@ -3728,8 +3731,10 @@ void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, fast_tx->key, &tx); tx.skb = NULL; - if (r == TX_DROP) + if (r == TX_DROP) { + tx.sdata->tx_handlers_drop++; goto free; + } if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, -- cgit v1.2.3 From ea928544f3215fdeac24d66bef85e10bb638b8c1 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 7 Sep 2025 11:51:17 +0300 Subject: wifi: mac80211: Fix HE capabilities element check The element data length check did not account for the extra octet used for the extension ID. Fix it. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250907115109.8da0012e2286.I8c0c69a0011f7153c13b365b14dfef48cfe7c3e3@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 83a9986dd1c4..f73e3222981b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5725,7 +5725,7 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ies->data, ies->len); - if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap)) + if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap) + 1) return chains; /* skip one byte ext_tag_id */ -- cgit v1.2.3 From 185cc2352cb1ef2178fe4e9a220a73c94007b8bb Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 7 Sep 2025 11:51:43 +0300 Subject: wifi: cfg80211: update the time stamps in hidden ssid In hidden SSID we have separate BSS entries for the beacon and for the probe response(s). The BSS entry time stamps represent the age of the BSS; when was the last time we heard the BSS. When we receive a beacon of a hidden SSID it means that we heard that BSS, so it makes sense to indicate that in the probe response entries. Do that. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250907115135.712745e498c0.I38186abf5d20dec6f6f2d42d2e1cdb50c6bfea25@changeid Signed-off-by: Johannes Berg --- net/wireless/scan.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a8339ed52404..52a3d32c16fe 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1816,6 +1816,9 @@ static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known, WARN_ON(ies != old_ies); rcu_assign_pointer(bss->pub.beacon_ies, new_ies); + + bss->ts = known->ts; + bss->pub.ts_boottime = known->pub.ts_boottime; } } @@ -1882,6 +1885,10 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, { lockdep_assert_held(&rdev->bss_lock); + /* Update time stamps */ + known->ts = new->ts; + known->pub.ts_boottime = new->pub.ts_boottime; + /* Update IEs */ if (rcu_access_pointer(new->pub.proberesp_ies)) { const struct cfg80211_bss_ies *old; @@ -1944,8 +1951,6 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, if (signal_valid) known->pub.signal = new->pub.signal; known->pub.capability = new->pub.capability; - known->ts = new->ts; - known->pub.ts_boottime = new->pub.ts_boottime; known->parent_tsf = new->parent_tsf; known->pub.chains = new->pub.chains; memcpy(known->pub.chain_signal, new->pub.chain_signal, -- cgit v1.2.3 From 691009b7ef08f7c9adee19706c8865a7e4d87037 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Mon, 8 Sep 2025 12:27:45 +0300 Subject: wifi: mac80211: fix incorrect comment As opposed to what the comment says, we don't count in the skb size of the association request frame the length of the Per STA Profile of the association link. Fix the comment. Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20250908122652.7022f33b1f33.Iac0d35744df883e8b96d71bbe8da518cc5d514bf@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f73e3222981b..43a53da42e52 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2183,11 +2183,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) 2 + /* ext capa & op */ 2; /* EML capa */ - /* - * The capability elements were already considered above; - * note this over-estimates a bit because there's no - * STA profile for the assoc link. - */ + /* The capability elements were already considered above */ size += (n_links - 1) * (1 + 1 + /* subelement ID/length */ 2 + /* STA control */ -- cgit v1.2.3 From b2422712d15db99c6e11a31a8df53e603604c494 Mon Sep 17 00:00:00 2001 From: Zheng tan Date: Wed, 10 Sep 2025 09:55:56 +0800 Subject: wifi: cfg80211: Remove the redundant wiphy_dev There is no need to call wiphy_dev again.Simplifying the code makes it more readable. Signed-off-by: Zheng tan Reviewed-by: Arend van Spriel Link: https://patch.msgid.link/20250910015556.219298-1-tanzheng@kylinos.cn Signed-off-by: Johannes Berg --- net/wireless/ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index 2613d6ac0fda..46e4317cbd7e 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -23,7 +23,7 @@ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) else strscpy(info->fw_version, "N/A", sizeof(info->fw_version)); - strscpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), + strscpy(info->bus_info, dev_name(pdev), sizeof(info->bus_info)); } EXPORT_SYMBOL(cfg80211_get_drvinfo); -- cgit v1.2.3 From 1b2e9feb351435299863b5e7ebeb02634381c883 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Wed, 10 Sep 2025 15:03:32 +0200 Subject: wifi: mwifiex: fix endianness handling in mwifiex_send_rgpower_table The length of the host command is a u16 stored in little endian byte order, which needs byte order conversion to work correctly on big endian systems. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-wireless/aLqZI4FfOI4iJZtf@stanley.mountain Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support") Signed-off-by: Stefan Kerkmann Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20250910-for-next-v1-1-3ee311706231@pengutronix.de Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 91d5098081e8..dcca71158fc6 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1537,7 +1537,7 @@ int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data, } if (*pos == '}' && start_raw) { - memcpy(&hostcmd->len, &hostcmd->cmd[2], sizeof(u16)); + hostcmd->len = get_unaligned_le16(&hostcmd->cmd[2]); ret = mwifiex_send_cmd(priv, 0, 0, 0, hostcmd, false); if (ret) { mwifiex_dbg(adapter, ERROR, -- cgit v1.2.3