* [PATCH v2 iwlwifi-next 12/15] wifi: iwlwifi: mld: correctly set wifi generation data
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Johannes Berg <johannes.berg@intel.com>
In each MAC context, the firmware expects the wifi generation
data, i.e. whether or not HE/EHT (and in the future UHR) is
enabled on that MAC.
However, this is currently handled wrong in two ways:
- EHT is only enabled when the interface is also an MLD, but
we currently allow (despite the spec) connecting with EHT
but without MLO.
- when HE or EHT are used by TDLS peers, the firmware needs
to have them enabled regardless of the AP
Fix this by iterating setting up the data depending on the
interface type:
- for AP, just set it according to the BSS configuration
- for monitor, set it according to HW capabilities
- otherwise, particularly for client, iterate all stations
and then their links on the interface in question and set
according to their capabilities, this handles the AP and
TDLS peers. Re-calculate this whenever a TDLS station is
marked associated or removed so that it's kept updated,
for the AP it's already updated on assoc/disassoc.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++++++++++------
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++++
2 files changed, 88 insertions(+), 32 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 29df747c8938..9215fc7e2eca 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
IEEE80211_HE_MAC_CAP2_ACK_EN);
}
-static void iwl_mld_set_he_support(struct iwl_mld *mld,
- struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd)
+struct iwl_mld_mac_wifi_gen_sta_iter_data {
+ struct ieee80211_vif *vif;
+ struct iwl_mac_wifi_gen_support *support;
+};
+
+static void iwl_mld_mac_wifi_gen_sta_iter(void *_data,
+ struct ieee80211_sta *sta)
{
- if (vif->type == NL80211_IFTYPE_AP)
- cmd->wifi_gen.he_ap_support = 1;
- else
- cmd->wifi_gen.he_support = 1;
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ if (mld_sta->vif != data->vif)
+ return;
+
+ for_each_sta_active_link(data->vif, sta, link_sta, link_id) {
+ if (link_sta->he_cap.has_he)
+ data->support->he_support = 1;
+ if (link_sta->eht_cap.has_eht)
+ data->support->eht_support = 1;
+ }
+}
+
+static void iwl_mld_set_wifi_gen(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_wifi_gen_support *support)
+{
+ struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = {
+ .vif = vif,
+ .support = support,
+ };
+ struct ieee80211_bss_conf *link_conf;
+ unsigned int link_id;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MONITOR:
+ /* for sniffer, set to HW capabilities */
+ support->he_support = 1;
+ support->eht_support = mld->trans->cfg->eht_supported;
+ break;
+ case NL80211_IFTYPE_AP:
+ /* for AP set according to the link configs */
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ support->he_ap_support |= link_conf->he_support;
+ support->eht_support |= link_conf->eht_support;
+ }
+ break;
+ default:
+ /*
+ * If we have MLO enabled, then the firmware needs to enable
+ * address translation for the station(s) we add. That depends
+ * on having EHT enabled in firmware, which in turn depends on
+ * mac80211 in the iteration below.
+ * However, mac80211 doesn't enable capabilities on the AP STA
+ * until it has parsed the association response successfully,
+ * so set EHT (and HE as a pre-requisite for EHT) when the vif
+ * is an MLD.
+ */
+ if (ieee80211_vif_is_mld(vif)) {
+ support->he_support = 1;
+ support->eht_support = 1;
+ }
+
+ ieee80211_iterate_stations_mtx(mld->hw,
+ iwl_mld_mac_wifi_gen_sta_iter,
+ &sta_iter_data);
+ break;
+ }
}
/* fill the common part for all interface types */
@@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
u32 action)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- struct ieee80211_bss_conf *link_conf;
- unsigned int link_id;
lockdep_assert_wiphy(mld->wiphy);
@@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
cmd->nic_not_ack_enabled =
cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
- /* If we have MLO enabled, then the firmware needs to enable
- * address translation for the station(s) we add. That depends
- * on having EHT enabled in firmware, which in turn depends on
- * mac80211 in the code below.
- * However, mac80211 doesn't enable HE/EHT until it has parsed
- * the association response successfully, so just skip all that
- * and enable both when we have MLO.
- */
- if (ieee80211_vif_is_mld(vif)) {
- iwl_mld_set_he_support(mld, vif, cmd);
- cmd->wifi_gen.eht_support = 1;
- return;
- }
-
- for_each_vif_active_link(vif, link_conf, link_id) {
- if (!link_conf->he_support)
- continue;
-
- iwl_mld_set_he_support(mld, vif, cmd);
-
- /* EHT, if supported, was already set above */
- break;
- }
+ iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen);
}
static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 0c53d6bd9651..71a9a72c9ac0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1761,6 +1761,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
if (vif->type == NL80211_IFTYPE_STATION)
iwl_mld_link_set_2mhz_block(mld, vif, sta);
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on association to the AP via the
+ * vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
+
/* Now the link_sta's capabilities are set, update the FW */
iwl_mld_config_tlc(mld, vif, sta);
@@ -1873,6 +1883,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
/* just removed last TDLS STA, so enable PM */
iwl_mld_update_mac_power(mld, vif, false);
}
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on disassociation to the AP via
+ * the vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
} else {
return -EINVAL;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 11/15] wifi: iwlwifi: mld: add support for sta command version 3
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
In this version, the link_id becomes a link_mask to support multiple
links that are used to communicate with the station in question.
This is needed for NAN, in which we can communicate on multiple channels
with the same station.
Also add a new STA type - NAN peer.
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 94 ++++++++++++++++++-
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 42 +++++++--
.../net/wireless/intel/iwlwifi/mvm/mld-sta.c | 6 +-
3 files changed, 129 insertions(+), 13 deletions(-)
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 c7a833f8041a..444d60a05a98 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -42,7 +42,8 @@ enum iwl_mac_conf_subcmd_ids {
*/
LINK_CONFIG_CMD = 0x9,
/**
- * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd
+ * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd_v1,
+ * &struct iwl_sta_cfg_cmd_v2, or struct iwl_sta_cfg_cmd
*/
STA_CONFIG_CMD = 0xA,
/**
@@ -664,13 +665,21 @@ struct iwl_link_config_cmd {
* power save state and the DTIM timing
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
* for the aux sta, so this type is only for driver - internal use.
+ * @STATION_TYPE_NAN_PEER_NMI: NAN management peer station type. A station
+ * of this type can have any number of links (even none) set in the
+ * link_mask. (Supported since version 3.)
+ * @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
+ * of this type can have any number of links (even none) set in the
+ * link_mask. (Supported since version 3.)
*/
enum iwl_fw_sta_type {
STATION_TYPE_PEER,
STATION_TYPE_BCAST_MGMT,
STATION_TYPE_MCAST,
STATION_TYPE_AUX,
-}; /* STATION_TYPE_E_VER_1 */
+ STATION_TYPE_NAN_PEER_NMI,
+ STATION_TYPE_NAN_PEER_NDI,
+}; /* STATION_TYPE_E_VER_1, _VER_2 */
/**
* struct iwl_sta_cfg_cmd_v1 - cmd structure to add a peer sta to the uCode's
@@ -729,7 +738,7 @@ struct iwl_sta_cfg_cmd_v1 {
} __packed; /* STA_CMD_API_S_VER_1 */
/**
- * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's
+ * struct iwl_sta_cfg_cmd_v2 - cmd structure to add a peer sta to the uCode's
* station table
* ( STA_CONFIG_CMD = 0xA )
*
@@ -769,7 +778,7 @@ struct iwl_sta_cfg_cmd_v1 {
* @mic_compute_pad_delay: MIC compute time padding
* @reserved: Reserved for alignment
*/
-struct iwl_sta_cfg_cmd {
+struct iwl_sta_cfg_cmd_v2 {
__le32 sta_id;
__le32 link_id;
u8 peer_mld_address[ETH_ALEN];
@@ -799,6 +808,83 @@ struct iwl_sta_cfg_cmd {
u8 reserved[2];
} __packed; /* STA_CMD_API_S_VER_2 */
+/**
+ * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's
+ * station table
+ * ( STA_CONFIG_CMD = 0xA )
+ *
+ * @sta_id: index of station in uCode's station table
+ * @link_mask: bitmap of link FW IDs used with this STA
+ * @peer_mld_address: the peers mld address
+ * @reserved_for_peer_mld_address: reserved
+ * @peer_link_address: the address of the link that is used to communicate
+ * with this sta
+ * @reserved_for_peer_link_address: reserved
+ * @station_type: type of this station. See &enum iwl_fw_sta_type
+ * @assoc_id: for GO only
+ * @beamform_flags: beam forming controls
+ * @mfp: indicates whether the STA uses management frame protection or not.
+ * @mimo: indicates whether the sta uses mimo or not
+ * @mimo_protection: indicates whether the sta uses mimo protection or not
+ * @ack_enabled: indicates that the AP supports receiving ACK-
+ * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG
+ * @trig_rnd_alloc: indicates that trigger based random allocation
+ * is enabled according to UORA element existence
+ * @tx_ampdu_spacing: minimum A-MPDU spacing:
+ * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density
+ * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K,
+ * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K.
+ * @sp_length: the size of the SP in actual number of frames
+ * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver
+ * enabled ACs.
+ * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY
+ * capa
+ * @htc_flags: which features are supported in HTC
+ * @use_ldpc_x2_cw: Indicates whether to use LDPC with double CW
+ * @use_icf: Indicates whether to use ICF instead of RTS
+ * @dps_pad_time: DPS (Dynamic Power Save) padding delay resolution to ensure
+ * proper timing alignment
+ * @dps_trans_delay: DPS minimal time that takes the peer to return to low power
+ * @dps_enabled: flag indicating whether or not DPS is enabled
+ * @mic_prep_pad_delay: MIC prep time padding
+ * @mic_compute_pad_delay: MIC compute time padding
+ * @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to
+ * @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address
+ * @reserved: Reserved for alignment
+ */
+struct iwl_sta_cfg_cmd {
+ __le32 sta_id;
+ __le32 link_mask;
+ u8 peer_mld_address[ETH_ALEN];
+ __le16 reserved_for_peer_mld_address;
+ u8 peer_link_address[ETH_ALEN];
+ __le16 reserved_for_peer_link_address;
+ __le32 station_type;
+ __le32 assoc_id;
+ __le32 beamform_flags;
+ __le32 mfp;
+ __le32 mimo;
+ __le32 mimo_protection;
+ __le32 ack_enabled;
+ __le32 trig_rnd_alloc;
+ __le32 tx_ampdu_spacing;
+ __le32 tx_ampdu_max_size;
+ __le32 sp_length;
+ __le32 uapsd_acs;
+ struct iwl_he_pkt_ext_v2 pkt_ext;
+ __le32 htc_flags;
+ u8 use_ldpc_x2_cw;
+ u8 use_icf;
+ u8 dps_pad_time;
+ u8 dps_trans_delay;
+ u8 dps_enabled;
+ u8 mic_prep_pad_delay;
+ u8 mic_compute_pad_delay;
+ u8 nmi_sta_id;
+ u8 ndi_local_addr[ETH_ALEN];
+ u8 reserved[2];
+} __packed; /* STA_CMD_API_S_VER_3 */
+
/**
* struct iwl_aux_sta_cmd - command for AUX STA configuration
* ( AUX_STA_CMD = 0xB )
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index 6b7a89e050e6..f40c49377466 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -398,12 +398,42 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta)
return htc_flags;
}
+/* Note: modifies the command depending on FW command version */
static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
- const struct iwl_sta_cfg_cmd *cmd)
+ struct iwl_sta_cfg_cmd *cmd)
{
- int ret = iwl_mld_send_cmd_pdu(mld,
- WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
- cmd);
+ int cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
+ int len = sizeof(*cmd);
+ int ret;
+
+ if (cmd_ver < 2) {
+ IWL_ERR(mld, "Unsupported STA_CONFIG_CMD version %d\n",
+ cmd_ver);
+ return -EINVAL;
+ } else if (cmd_ver == 2) {
+ struct iwl_sta_cfg_cmd_v2 *cmd_v2 = (void *)cmd;
+
+ if (WARN_ON(cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) ||
+ cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) ||
+ hweight32(le32_to_cpu(cmd->link_mask)) != 1))
+ return -EINVAL;
+ /*
+ * These fields are located in a different place in the struct of v2.
+ * The assumption is that UHR won't be used with FW that has v2.
+ */
+ if (WARN_ON(cmd->mic_prep_pad_delay || cmd->mic_compute_pad_delay))
+ return -EINVAL;
+
+ len = sizeof(struct iwl_sta_cfg_cmd_v2);
+ cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
+ } else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
+ cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
+ hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
+ return -EINVAL;
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len);
if (ret)
IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
return ret;
@@ -431,8 +461,8 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
return -EINVAL;
cmd.sta_id = cpu_to_le32(fw_id);
+ cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id));
cmd.station_type = cpu_to_le32(mld_sta->sta_type);
- cmd.link_id = cpu_to_le32(mld_link->fw_id);
memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
@@ -982,7 +1012,7 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
- cmd.link_id = cpu_to_le32(fw_link_id);
+ cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
cmd.station_type = cpu_to_le32(internal_sta->sta_type);
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
index 44e16ee9514e..da7ed4639a93 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
@@ -20,7 +20,7 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
}
static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
- struct iwl_sta_cfg_cmd *cmd)
+ struct iwl_sta_cfg_cmd_v2 *cmd)
{
u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
int cmd_len = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) > 1 ?
@@ -41,7 +41,7 @@ static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta,
const u8 *addr, int link_id)
{
- struct iwl_sta_cfg_cmd cmd;
+ struct iwl_sta_cfg_cmd_v2 cmd;
lockdep_assert_held(&mvm->mutex);
@@ -416,7 +416,7 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_vif_link_info *link_info =
mvm_vif->link[link_conf->link_id];
- struct iwl_sta_cfg_cmd cmd = {
+ struct iwl_sta_cfg_cmd_v2 cmd = {
.sta_id = cpu_to_le32(mvm_link_sta->sta_id),
.station_type = cpu_to_le32(mvm_sta->sta_type),
};
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 10/15] wifi: iwlwifi: bring iwl_fill_ppag_table to the iwlmvm
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
iwl_fill_ppag_table fills a command that is sent to the firmware. This
command has several versions and handling those different versions is
the responsibility of the op_mode.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/regulatory.c | 126 -----------------
.../wireless/intel/iwlwifi/fw/regulatory.h | 4 -
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 129 +++++++++++++++++-
3 files changed, 128 insertions(+), 131 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 5793c267daf7..9e834cc1b054 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -304,132 +304,6 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt,
}
IWL_EXPORT_SYMBOL(iwl_sar_fill_profile);
-static bool iwl_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
- int subband)
-{
- s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband];
-
- if ((subband == 0 &&
- (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) ||
- (subband != 0 &&
- (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) {
- IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val);
- return false;
- }
- 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)
-{
- u8 cmd_ver;
- int i, j, num_sub_bands;
- s8 *gain;
- bool send_ppag_always;
-
- /* many firmware images for JF lie about this */
- if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
- CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
- return -EOPNOTSUPP;
-
- if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
- IWL_DEBUG_RADIO(fwrt,
- "PPAG capability not supported by FW, command not sent.\n");
- return -EINVAL;
- }
-
- cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
- WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD), 1);
- /*
- * Starting from ver 4, driver needs to send the PPAG CMD regardless
- * if PPAG is enabled/disabled or valid/invalid.
- */
- send_ppag_always = cmd_ver > 3;
-
- /* Don't send PPAG if it is disabled */
- if (!send_ppag_always && !fwrt->ppag_flags) {
- IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
- return -EINVAL;
- }
-
- IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
- if (cmd_ver == 1) {
- 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 & 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 == 5) {
- num_sub_bands = IWL_NUM_SUB_BANDS_V2;
- 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,
- "PPAG table rev is 0, send padded table\n");
- }
- } else if (cmd_ver == 7) {
- num_sub_bands = IWL_NUM_SUB_BANDS_V2;
- gain = cmd->v7.gain[0];
- *cmd_size = sizeof(cmd->v7);
- cmd->v7.ppag_config_info.hdr.table_source =
- fwrt->ppag_bios_source;
- cmd->v7.ppag_config_info.hdr.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;
- }
-
- /* ppag mode */
- IWL_DEBUG_RADIO(fwrt,
- "PPAG MODE bits were read from bios: %d\n",
- fwrt->ppag_flags);
-
- if (cmd_ver == 1 &&
- !fw_has_capa(&fwrt->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
- cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
- IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
- } else {
- IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
- }
-
- /* The 'flags' field is the same in v1 and 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->v7.ppag_config_info.value));
-
- for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
- for (j = 0; j < num_sub_bands; j++) {
- if (!send_ppag_always &&
- !iwl_ppag_value_valid(fwrt, i, j))
- return -EINVAL;
-
- gain[i * num_sub_bands + j] =
- fwrt->ppag_chains[i].subbands[j];
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table: chain[%d] band[%d]: gain = %d\n",
- i, j, gain[i * num_sub_bands + j]);
- }
- }
-
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_fill_ppag_table);
-
bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt)
{
if (!dmi_check_system(dmi_ppag_approved_list)) {
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
index 1489031687b7..8e04b0e2d507 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
@@ -190,10 +190,6 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt,
__le16 *per_chain, u32 n_tables, u32 n_subbands,
int prof_a, int prof_b);
-int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
- union iwl_ppag_table_cmd *cmd,
- int *cmd_size);
-
bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt);
bool iwl_is_tas_approved(void);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index f5e5c10cc581..d46715abd7a5 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -1034,12 +1034,139 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);
}
+static bool iwl_mvm_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
+ int subband)
+{
+ s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband];
+
+ if ((subband == 0 &&
+ (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) ||
+ (subband != 0 &&
+ (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) {
+ IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val);
+ return false;
+ }
+ return true;
+}
+
+static int iwl_mvm_fill_ppag_table(struct iwl_fw_runtime *fwrt,
+ union iwl_ppag_table_cmd *cmd,
+ int *cmd_size)
+{
+ u8 cmd_ver;
+ int i, j, num_sub_bands;
+ s8 *gain;
+ bool send_ppag_always;
+
+ /* many firmware images for JF lie about this */
+ if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
+ CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
+ return -EOPNOTSUPP;
+
+ if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG capability not supported by FW, command not sent.\n");
+ return -EINVAL;
+ }
+
+ cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
+ WIDE_ID(PHY_OPS_GROUP,
+ PER_PLATFORM_ANT_GAIN_CMD), 1);
+ /*
+ * Starting from ver 4, driver needs to send the PPAG CMD regardless
+ * if PPAG is enabled/disabled or valid/invalid.
+ */
+ send_ppag_always = cmd_ver > 3;
+
+ /* Don't send PPAG if it is disabled */
+ if (!send_ppag_always && !fwrt->ppag_flags) {
+ IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
+ return -EINVAL;
+ }
+
+ IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
+ if (cmd_ver == 1) {
+ 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 & 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 == 5) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V2;
+ 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,
+ "PPAG table rev is 0, send padded table\n");
+ }
+ } else if (cmd_ver == 7) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V2;
+ gain = cmd->v7.gain[0];
+ *cmd_size = sizeof(cmd->v7);
+ cmd->v7.ppag_config_info.hdr.table_source =
+ fwrt->ppag_bios_source;
+ cmd->v7.ppag_config_info.hdr.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;
+ }
+
+ /* ppag mode */
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG MODE bits were read from bios: %d\n",
+ fwrt->ppag_flags);
+
+ if (cmd_ver == 1 &&
+ !fw_has_capa(&fwrt->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
+ cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
+ IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
+ } else {
+ IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
+ }
+
+ /* The 'flags' field is the same in v1 and 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->v7.ppag_config_info.value));
+
+ for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
+ for (j = 0; j < num_sub_bands; j++) {
+ if (!send_ppag_always &&
+ !iwl_mvm_ppag_value_valid(fwrt, i, j))
+ return -EINVAL;
+
+ gain[i * num_sub_bands + j] =
+ fwrt->ppag_chains[i].subbands[j];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ i, j, gain[i * num_sub_bands + j]);
+ }
+ }
+
+ return 0;
+}
+
int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)
{
union iwl_ppag_table_cmd cmd;
int ret, cmd_size;
- ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size);
+ ret = iwl_mvm_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size);
/* Not supporting PPAG table is a valid scenario */
if (ret < 0)
return 0;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 08/15] wifi: iwlwifi: mld: Introduce scan command version 18
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Ilan Peer <ilan.peer@intel.com>
The FW scan logic was extended to support new channels in the
7 GHz band, as such, the scan command was modified to support
scanning more PSC channels.
Introduce scan command version 18 handling, which is different
from scan command version 17 only in the number of supported
channel configurations.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/fw/api/scan.h | 45 ++++++++++
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 87 +++++++++++++++----
2 files changed, 115 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 60f0a4924ddf..c2bb400c834c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -985,6 +985,7 @@ struct iwl_scan_probe_params_v4 {
} __packed; /* SCAN_PROBE_PARAMS_API_S_VER_4 */
#define SCAN_MAX_NUM_CHANS_V3 67
+#define SCAN_MAX_NUM_CHANS_V4 68
/**
* struct iwl_scan_channel_params_v4 - channel params
@@ -1027,6 +1028,24 @@ struct iwl_scan_channel_params_v7 {
struct iwl_scan_channel_cfg_umac channel_config[SCAN_MAX_NUM_CHANS_V3];
} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_6 */
+/**
+ * struct iwl_scan_channel_params_v8 - channel params
+ * @flags: channel flags &enum iwl_scan_channel_flags
+ * @count: num of channels in scan request
+ * @n_aps_override: override the number of APs the FW uses to calculate dwell
+ * time when adaptive dwell is used.
+ * Channel k will use n_aps_override[i] when BIT(20 + i) is set in
+ * channel_config[k].flags
+ * @channel_config: array of explicit channel configurations
+ * for 2.4Ghz and 5.2Ghz bands
+ */
+struct iwl_scan_channel_params_v8 {
+ u8 flags;
+ u8 count;
+ u8 n_aps_override[2];
+ struct iwl_scan_channel_cfg_umac channel_config[SCAN_MAX_NUM_CHANS_V4];
+} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_8 */
+
/**
* struct iwl_scan_general_params_v11 - channel params
* @flags: &enum iwl_umac_scan_general_flags_v2
@@ -1109,6 +1128,20 @@ struct iwl_scan_req_params_v17 {
struct iwl_scan_probe_params_v4 probe_params;
} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_17 - 14 */
+/**
+ * struct iwl_scan_req_params_v18 - scan request parameters (v18)
+ * @general_params: &struct iwl_scan_general_params_v11
+ * @channel_params: &struct iwl_scan_channel_params_v8
+ * @periodic_params: &struct iwl_scan_periodic_parms_v1
+ * @probe_params: &struct iwl_scan_probe_params_v4
+ */
+struct iwl_scan_req_params_v18 {
+ struct iwl_scan_general_params_v11 general_params;
+ struct iwl_scan_channel_params_v8 channel_params;
+ struct iwl_scan_periodic_parms_v1 periodic_params;
+ struct iwl_scan_probe_params_v4 probe_params;
+} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_18 */
+
/**
* struct iwl_scan_req_umac_v12 - scan request command (v12)
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
@@ -1133,6 +1166,18 @@ struct iwl_scan_req_umac_v17 {
struct iwl_scan_req_params_v17 scan_params;
} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_17 - 14 */
+/**
+ * struct iwl_scan_req_umac_v18 - scan request command (v18)
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @scan_params: scan parameters
+ */
+struct iwl_scan_req_umac_v18 {
+ __le32 uid;
+ __le32 ooc_priority;
+ struct iwl_scan_req_params_v18 scan_params;
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_18 */
+
/**
* struct iwl_umac_scan_abort - scan abort command
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index 7f4679134def..96cd970cceb4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -118,7 +118,7 @@ struct iwl_mld_scan_params {
struct iwl_scan_req_params_ptrs {
struct iwl_scan_general_params_v11 *general_params;
- struct iwl_scan_channel_params_v7 *channel_params;
+ struct iwl_scan_channel_params_v8 *channel_params;
struct iwl_scan_periodic_parms_v1 *periodic_params;
struct iwl_scan_probe_params_v4 *probe_params;
};
@@ -840,7 +840,7 @@ iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld,
int n_channels, u32 flags,
enum nl80211_iftype vif_type)
{
- struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
for (int i = 0; i < n_channels; i++) {
enum nl80211_band band = channels[i]->band;
@@ -883,7 +883,7 @@ iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld,
enum nl80211_iftype vif_type)
{
struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
- struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
u32 i;
@@ -1083,7 +1083,7 @@ iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct iwl_scan_req_params_ptrs *scan_ptrs)
{
- struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
/* Explicitly clear the flags since most of them are not
* relevant for 6 GHz scan.
@@ -1111,7 +1111,7 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
enum iwl_mld_scan_status scan_status,
u32 channel_cfg_flags)
{
- struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
struct ieee80211_supported_band *sband =
&mld->nvm_data->bands[NL80211_BAND_6GHZ];
@@ -1173,39 +1173,89 @@ struct iwl_scan_umac_handler {
.handler = iwl_mld_scan_umac_v##_ver, \
}
-static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif,
+static int iwl_mld_scan_umac_common(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
+ enum iwl_mld_scan_status scan_status,
+ bool low_latency)
+{
+ u32 bitmap_ssid = 0;
+ int ret;
+
+ iwl_mld_scan_cmd_set_gen_params(mld, params, vif, scan_ptrs,
+ scan_status);
+
+ ret = iwl_mld_scan_cmd_set_sched_params(params, scan_ptrs);
+ if (ret)
+ return ret;
+
+ iwl_mld_scan_cmd_set_probe_params(params, scan_ptrs, &bitmap_ssid);
+
+ return iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_ptrs,
+ low_latency, scan_status,
+ bitmap_ssid);
+}
+
+static int iwl_mld_scan_umac_v18(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct iwl_mld_scan_params *params,
enum iwl_mld_scan_status scan_status,
int uid, u32 ooc_priority, bool low_latency)
{
- struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
+ struct iwl_scan_req_umac_v18 *cmd = mld->scan.cmd;
struct iwl_scan_req_params_ptrs scan_ptrs = {
.general_params = &cmd->scan_params.general_params,
.probe_params = &cmd->scan_params.probe_params,
.channel_params = &cmd->scan_params.channel_params,
.periodic_params = &cmd->scan_params.periodic_params
};
- u32 bitmap_ssid = 0;
int ret;
- if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V3))
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V4))
return -EINVAL;
cmd->uid = cpu_to_le32(uid);
cmd->ooc_priority = cpu_to_le32(ooc_priority);
- iwl_mld_scan_cmd_set_gen_params(mld, params, vif, &scan_ptrs,
- scan_status);
-
- ret = iwl_mld_scan_cmd_set_sched_params(params, &scan_ptrs);
+ ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs,
+ scan_status, low_latency);
if (ret)
return ret;
- iwl_mld_scan_cmd_set_probe_params(params, &scan_ptrs, &bitmap_ssid);
+ return uid;
+}
+
+static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ int uid, u32 ooc_priority, bool low_latency)
+{
+ struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
+ struct iwl_scan_req_params_ptrs scan_ptrs = {
+ .general_params = &cmd->scan_params.general_params,
+ .probe_params = &cmd->scan_params.probe_params,
+
+ /* struct iwl_scan_channel_params_v8 and struct
+ * iwl_scan_channel_params_v7 are almost identical. The only
+ * difference is that the newer version allows configuration of
+ * more channels. So casting here is ok as long as we ensure
+ * that we don't exceed the max number of channels supported by
+ * the older version (see the WARN_ON below).
+ */
+ .channel_params = (struct iwl_scan_channel_params_v8 *)
+ &cmd->scan_params.channel_params,
+ .periodic_params = &cmd->scan_params.periodic_params
+ };
+ int ret;
+
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V3))
+ return -EINVAL;
+
+ cmd->uid = cpu_to_le32(uid);
+ cmd->ooc_priority = cpu_to_le32(ooc_priority);
- ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, &scan_ptrs,
- low_latency, scan_status,
- bitmap_ssid);
+ ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs,
+ scan_status, low_latency);
if (ret)
return ret;
@@ -1214,6 +1264,7 @@ static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif,
static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = {
/* set the newest version first to shorten the list traverse time */
+ IWL_SCAN_UMAC_HANDLER(18),
IWL_SCAN_UMAC_HANDLER(17),
};
@@ -2095,6 +2146,8 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
if (scan_cmd_ver == 17) {
scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17);
+ } else if (scan_cmd_ver == 18) {
+ scan_cmd_size = sizeof(struct iwl_scan_req_umac_v18);
} else {
IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver);
return -EINVAL;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 09/15] wifi: iwlwifi: uefi: open code the PPAG table store operation
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
The structure in firmware runtime will need to grow because we're adding
a subband for UNII-9.
This means that we will soon no longer be able to just memcpy the data
from the UEFI table. The layout of the array will change.
Tediously copy the data byte-byte to make sure things get to the right
place even when we'll increase the number of subbands.
Make it easier for the uefi_cnv_var_ppag structure to grow by
simpiflying the layout it becomes an array of s8.
The layout of the structure becomes less obvious from the structure's
declaration, but then the code is more flexible.
Don't use UEFI_SAR_MAX_SUB_BANDS_NUM for the number of bands for PPAG.
Of course, SAR related structures will grow in future patches, but
decouple SAR and PPAG to make the work easier.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 23 ++++++++++++++++----
drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 16 +++++---------
2 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
index d4e1ab1f7c84..38f9d9adf90e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
@@ -571,9 +571,11 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_ppag *data;
int ret = 0;
+ int data_sz = sizeof(*data) + sizeof(data->vals[0]) *
+ IWL_NUM_CHAIN_LIMITS * UEFI_PPAG_SUB_BANDS_NUM;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
- "PPAG", sizeof(*data), NULL);
+ "PPAG", data_sz, NULL);
if (IS_ERR(data))
return -EINVAL;
@@ -589,9 +591,22 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
fwrt->ppag_bios_rev);
- BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));
- memcpy(&fwrt->ppag_chains, &data->ppag_chains,
- sizeof(data->ppag_chains));
+ /*
+ * Make sure fwrt has enough room to hold
+ * data coming from the UEFI table
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(fwrt->ppag_chains) *
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands) <
+ IWL_NUM_CHAIN_LIMITS * UEFI_PPAG_SUB_BANDS_NUM);
+
+ for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) {
+ for (int subband = 0;
+ subband < UEFI_PPAG_SUB_BANDS_NUM;
+ subband++)
+ fwrt->ppag_chains[chain].subbands[subband] =
+ data->vals[chain * UEFI_PPAG_SUB_BANDS_NUM + subband];
+ }
+
fwrt->ppag_bios_source = BIOS_SOURCE_UEFI;
out:
kfree(data);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index c6940a3c03ea..4f0ce068a589 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -77,6 +77,7 @@ struct uefi_cnv_common_step_data {
} __packed;
#define UEFI_SAR_MAX_SUB_BANDS_NUM 11
+#define UEFI_PPAG_SUB_BANDS_NUM 11
#define UEFI_SAR_MAX_CHAINS_PER_PROFILE 4
/*
@@ -136,24 +137,19 @@ struct uefi_cnv_var_wgds {
struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
} __packed;
-/*
- * struct uefi_ppag_chain - PPAG table for a specific chain
- * @subbands: the PPAG values for band
- */
-struct uefi_ppag_chain {
- s8 subbands[UEFI_SAR_MAX_SUB_BANDS_NUM];
-};
-
/*
* struct uefi_cnv_var_ppag - PPAG table as defined in UEFI
* @revision: the revision of the table
* @ppag_modes: values from &enum iwl_ppag_flags
- * @ppag_chains: the PPAG values per chain and band
+ * @vals: the PPAG values per chain and band as an array.
+ * vals[chain * num_of_subbands + subband] will return the right value.
+ * num_of_subbands is %UEFI_PPAG_SUB_BANDS_NUM.
+ * the max number of chains is currently 2
*/
struct uefi_cnv_var_ppag {
u8 revision;
u32 ppag_modes;
- struct uefi_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
+ s8 vals[];
} __packed;
/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 07/15] wifi: iwlwifi: mld: Refactor scan command handling
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Ilan Peer <ilan.peer@intel.com>
As a preparation for a new scan command version, refactor
the scan command building such that it would allow introducing
new scan command structures in a simpler way.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 159 ++++++++++++------
drivers/net/wireless/intel/iwlwifi/mld/scan.h | 2 +
2 files changed, 114 insertions(+), 47 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index a1a4cf3ab3d3..7f4679134def 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -116,6 +116,13 @@ struct iwl_mld_scan_params {
u8 bssid[ETH_ALEN] __aligned(2);
};
+struct iwl_scan_req_params_ptrs {
+ struct iwl_scan_general_params_v11 *general_params;
+ struct iwl_scan_channel_params_v7 *channel_params;
+ struct iwl_scan_periodic_parms_v1 *periodic_params;
+ struct iwl_scan_probe_params_v4 *probe_params;
+};
+
struct iwl_mld_scan_respect_p2p_go_iter_data {
struct ieee80211_vif *current_vif;
bool p2p_go;
@@ -512,9 +519,10 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
static void
iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld,
- struct iwl_scan_general_params_v11 *gp,
- struct iwl_mld_scan_params *params)
+ struct iwl_mld_scan_params *params,
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
+ struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params;
const struct iwl_mld_scan_timing_params *timing =
&scan_timing[params->type];
@@ -551,9 +559,10 @@ static void
iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_general_params_v11 *gp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
enum iwl_mld_scan_status scan_status)
{
+ struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params;
u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
scan_status);
u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif,
@@ -566,7 +575,7 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
gp->flags = cpu_to_le16(gen_flags);
gp->flags2 = gen_flags2;
- iwl_mld_scan_cmd_set_dwell(mld, gp, params);
+ iwl_mld_scan_cmd_set_dwell(mld, params, scan_ptrs);
if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1)
gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS;
@@ -577,9 +586,12 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
static int
iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params,
- struct iwl_scan_umac_schedule *schedule,
- __le16 *delay)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
+ struct iwl_scan_umac_schedule *schedule =
+ scan_ptrs->periodic_params->schedule;
+ __le16 *delay = &scan_ptrs->periodic_params->delay;
+
if (WARN_ON(!params->n_scan_plans ||
params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
return -EINVAL;
@@ -657,11 +669,12 @@ iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params,
static void
iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
- struct iwl_scan_probe_params_v4 *pp)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
int j, idex_s = 0, idex_b = 0;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
for (j = 0;
j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE;
@@ -725,13 +738,15 @@ iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
static void
iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params,
- struct iwl_scan_probe_params_v4 *pp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
u32 *bitmap_ssid)
{
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
+
pp->preq = params->preq;
if (params->scan_6ghz) {
- iwl_mld_scan_fill_6g_chan_list(params, pp);
+ iwl_mld_scan_fill_6g_chan_list(params, scan_ptrs);
return;
}
@@ -821,10 +836,12 @@ static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id)
static void
iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld,
struct ieee80211_channel **channels,
- struct iwl_scan_channel_params_v7 *cp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
int n_channels, u32 flags,
enum nl80211_iftype vif_type)
{
+ struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
+
for (int i = 0; i < n_channels; i++) {
enum nl80211_band band = channels[i]->band;
struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i];
@@ -862,10 +879,11 @@ static u8
iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
u32 n_channels,
- struct iwl_scan_probe_params_v4 *pp,
- struct iwl_scan_channel_params_v7 *cp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
enum nl80211_iftype vif_type)
{
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
+ struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
u32 i;
@@ -1063,25 +1081,23 @@ static int
iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_req_params_v17 *scan_p)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
- struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params;
- struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params;
+ struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
/* Explicitly clear the flags since most of them are not
* relevant for 6 GHz scan.
*/
- chan_p->flags = 0;
- chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params,
- params->n_channels,
- probe_p, chan_p,
- vif->type);
- if (!chan_p->count)
+ cp->flags = 0;
+ cp->count = iwl_mld_scan_cfg_channels_6g(mld, params,
+ params->n_channels,
+ scan_ptrs, vif->type);
+ if (!cp->count)
return -EINVAL;
if (!params->n_ssids ||
(params->n_ssids == 1 && !params->ssids[0].ssid_len))
- chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
+ cp->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
return 0;
}
@@ -1090,12 +1106,12 @@ static int
iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_req_params_v17 *scan_p,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
bool low_latency,
enum iwl_mld_scan_status scan_status,
u32 channel_cfg_flags)
{
- struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params;
+ struct iwl_scan_channel_params_v7 *cp = scan_ptrs->channel_params;
struct ieee80211_supported_band *sband =
&mld->nvm_data->bands[NL80211_BAND_6GHZ];
@@ -1107,14 +1123,14 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
if (params->scan_6ghz)
return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params,
- vif, scan_p);
+ vif, scan_ptrs);
/* relevant only for 2.4 GHz/5 GHz scan */
cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif,
low_latency);
cp->count = params->n_channels;
- iwl_mld_scan_cmd_set_channels(mld, params->channels, cp,
+ iwl_mld_scan_cmd_set_channels(mld, params->channels, scan_ptrs,
params->n_channels, channel_cfg_flags,
vif->type);
@@ -1144,41 +1160,50 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
return 0;
}
-static int
-iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
+struct iwl_scan_umac_handler {
+ u8 version;
+ int (*handler)(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct iwl_mld_scan_params *params,
enum iwl_mld_scan_status scan_status,
- bool low_latency)
+ int uid, u32 ooc_priority, bool low_latency);
+};
+
+#define IWL_SCAN_UMAC_HANDLER(_ver) { \
+ .version = _ver, \
+ .handler = iwl_mld_scan_umac_v##_ver, \
+}
+
+static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ int uid, u32 ooc_priority, bool low_latency)
{
struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
- struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params;
+ struct iwl_scan_req_params_ptrs scan_ptrs = {
+ .general_params = &cmd->scan_params.general_params,
+ .probe_params = &cmd->scan_params.probe_params,
+ .channel_params = &cmd->scan_params.channel_params,
+ .periodic_params = &cmd->scan_params.periodic_params
+ };
u32 bitmap_ssid = 0;
- int uid, ret;
-
- memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+ int ret;
- /* find a free UID entry */
- uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
- if (uid < 0)
- return uid;
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V3))
+ return -EINVAL;
cmd->uid = cpu_to_le32(uid);
- cmd->ooc_priority =
- cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status));
+ cmd->ooc_priority = cpu_to_le32(ooc_priority);
- iwl_mld_scan_cmd_set_gen_params(mld, params, vif,
- &scan_p->general_params, scan_status);
+ iwl_mld_scan_cmd_set_gen_params(mld, params, vif, &scan_ptrs,
+ scan_status);
- ret = iwl_mld_scan_cmd_set_sched_params(params,
- scan_p->periodic_params.schedule,
- &scan_p->periodic_params.delay);
+ ret = iwl_mld_scan_cmd_set_sched_params(params, &scan_ptrs);
if (ret)
return ret;
- iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params,
- &bitmap_ssid);
+ iwl_mld_scan_cmd_set_probe_params(params, &scan_ptrs, &bitmap_ssid);
- ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p,
+ ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, &scan_ptrs,
low_latency, scan_status,
bitmap_ssid);
if (ret)
@@ -1187,6 +1212,45 @@ iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
return uid;
}
+static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = {
+ /* set the newest version first to shorten the list traverse time */
+ IWL_SCAN_UMAC_HANDLER(17),
+};
+
+static int
+iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ bool low_latency)
+{
+ int uid, err;
+ u32 ooc_priority;
+
+ memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
+ if (uid < 0)
+ return uid;
+
+ ooc_priority = iwl_mld_scan_ooc_priority(scan_status);
+
+ for (size_t i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) {
+ const struct iwl_scan_umac_handler *ver_handler =
+ &iwl_scan_umac_handlers[i];
+
+ if (ver_handler->version != mld->scan.cmd_ver)
+ continue;
+
+ err = ver_handler->handler(mld, vif, params, scan_status,
+ uid, ooc_priority, low_latency);
+ return err ? : uid;
+ }
+
+ IWL_ERR(mld, "No handler for UMAC scan cmd version %d\n",
+ mld->scan.cmd_ver);
+
+ return -EINVAL;
+}
+
static bool
iwl_mld_scan_pass_all(struct iwl_mld *mld,
struct cfg80211_sched_scan_request *req)
@@ -2041,6 +2105,7 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
return -ENOMEM;
mld->scan.cmd_size = scan_cmd_size;
+ mld->scan.cmd_ver = scan_cmd_ver;
return 0;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
index 69110f0cfc8e..772b3a02c4c4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -109,6 +109,7 @@ enum iwl_mld_traffic_load {
* @traffic_load.status: The current traffic load status, see
* &enum iwl_mld_traffic_load
* @cmd_size: size of %cmd.
+ * @cmd_ver: version of the scan command format.
* @cmd: pointer to scan cmd buffer (allocated once in op mode start).
* @last_6ghz_passive_jiffies: stores the last 6GHz passive scan time
* in jiffies.
@@ -134,6 +135,7 @@ struct iwl_mld_scan {
/* And here fields that survive a fw restart */
size_t cmd_size;
void *cmd;
+ u8 cmd_ver;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
u64 last_mlo_scan_time;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 06/15] wifi: iwlwifi: mld: remove unused scan expire time constants
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu, Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Remove the unused IWL_MLD_SCAN_EXPIRE_TIME_SEC constant from
constants.h and its corresponding IWL_MLD_SCAN_EXPIRE_TIME
macro definition from mlo.c. These definitions are no longer
referenced.
Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/constants.h | 1 -
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 1 -
2 files changed, 2 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
index 5d23a618ae3c..e2a5eecc18c3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
@@ -36,7 +36,6 @@
#define IWL_MLD_PS_HEAVY_RX_THLD_PACKETS 8
#define IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC 30
-#define IWL_MLD_SCAN_EXPIRE_TIME_SEC 20
#define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index f842f5183223..f693f92e42b4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -110,7 +110,6 @@ void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy,
}
#define IWL_MLD_TRIGGER_LINK_SEL_TIME (HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC)
-#define IWL_MLD_SCAN_EXPIRE_TIME (HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC)
/* Exit reasons that can cause longer EMLSR prevention */
#define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 05/15] wifi: iwlwifi: mvm: cleanup some more MLO code
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Nidhish A N, Pagadala Yesu Anjaneyulu
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Nidhish A N <nidhish.a.n@intel.com>
iwlmld is now the op mode that is used for EHT devices,
so iwlmvm code can never run in MLO.
Clean up some more MLO code.
Signed-off-by: Nidhish A N <nidhish.a.n@intel.com>
Reviewed-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/mvm/mld-key.c | 46 ---
.../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 132 --------
.../net/wireless/intel/iwlwifi/mvm/mld-sta.c | 285 ------------------
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 -
drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 4 -
5 files changed, 472 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
index 9bb253dcf4a7..4869a5fa8abc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
@@ -121,52 +121,6 @@ struct iwl_mvm_sta_key_update_data {
int err;
};
-static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- void *_data)
-{
- u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
- struct iwl_mvm_sta_key_update_data *data = _data;
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_sec_key_cmd cmd = {
- .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
- .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
- .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
- .u.modify.key_id = cpu_to_le32(key->keyidx),
- .u.modify.key_flags =
- cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)),
- };
- int err;
-
- /* only need to do this for pairwise keys (link_id == -1) */
- if (sta != data->sta || key->link_id >= 0)
- return;
-
- err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
-
- if (err)
- data->err = err;
-}
-
-int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_mvm_sta_key_update_data data = {
- .sta = sta,
- .old_sta_mask = old_sta_mask,
- .new_sta_mask = new_sta_mask,
- };
-
- ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key,
- &data);
- return data.err;
-}
-
static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,
u32 key_flags, u32 keyidx, u32 flags)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
index 896ed9823021..f1dbfeae20bc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
@@ -886,133 +886,6 @@ static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops);
}
-static int
-iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u16 old_links, u16 new_links,
- struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
-{
- struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {};
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u16 removed = old_links & ~new_links;
- u16 added = new_links & ~old_links;
- int err, i;
-
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- break;
-
- if (!(added & BIT(i)))
- continue;
- new_link[i] = kzalloc_obj(*new_link[i]);
- if (!new_link[i]) {
- err = -ENOMEM;
- goto free;
- }
-
- new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
- iwl_mvm_init_link(new_link[i]);
- }
-
- mutex_lock(&mvm->mutex);
-
- /* If we're in RESTART flow, the default link wasn't added in
- * drv_add_interface(), and link[0] doesn't point to it.
- */
- if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status)) {
- err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf);
- if (err)
- goto out_err;
- mvmvif->link[0] = NULL;
- }
-
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- if (removed & BIT(i)) {
- struct ieee80211_bss_conf *link_conf = old[i];
-
- err = iwl_mvm_disable_link(mvm, vif, link_conf);
- if (err)
- goto out_err;
- kfree(mvmvif->link[i]);
- mvmvif->link[i] = NULL;
- } else if (added & BIT(i)) {
- struct ieee80211_bss_conf *link_conf;
-
- link_conf = link_conf_dereference_protected(vif, i);
- if (WARN_ON(!link_conf))
- continue;
-
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status))
- mvmvif->link[i] = new_link[i];
- new_link[i] = NULL;
- err = iwl_mvm_add_link(mvm, vif, link_conf);
- if (err)
- goto out_err;
- }
- }
-
- err = 0;
- if (new_links == 0) {
- mvmvif->link[0] = &mvmvif->deflink;
- err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);
- }
-
-out_err:
- /* we really don't have a good way to roll back here ... */
- mutex_unlock(&mvm->mutex);
-
-free:
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
- kfree(new_link[i]);
- return err;
-}
-
-static int
-iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- guard(mvm)(mvm);
- return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links);
-}
-
-static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u16 desired_links)
-{
- int n_links = hweight16(desired_links);
-
- if (n_links <= 1)
- return true;
-
- WARN_ON(1);
- return false;
-}
-
-static enum ieee80211_neg_ttlm_res
-iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_neg_ttlm *neg_ttlm)
-{
- u16 map;
- u8 i;
-
- /* Verify all TIDs are mapped to the same links set */
- map = neg_ttlm->downlink[0];
- for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) {
- if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] ||
- neg_ttlm->uplink[i] != map)
- return NEG_TTLM_RES_REJECT;
- }
-
- return NEG_TTLM_RES_ACCEPT;
-}
-
const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.tx = iwl_mvm_mac_tx,
.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
@@ -1102,9 +975,4 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs,
#endif
.set_hw_timestamp = iwl_mvm_set_hw_timestamp,
-
- .change_vif_links = iwl_mvm_mld_change_vif_links,
- .change_sta_links = iwl_mvm_mld_change_sta_links,
- .can_activate_links = iwl_mvm_mld_can_activate_links,
- .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm,
};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
index 3359e02e151f..44e16ee9514e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
@@ -913,288 +913,3 @@ void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
rcu_read_unlock();
}
-
-static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_scd_queue_cfg_cmd cmd = {
- .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY),
- .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask),
- .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask),
- };
- struct iwl_host_cmd hcmd = {
- .id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD),
- .len[0] = sizeof(cmd),
- .data[0] = &cmd
- };
- int tid;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) {
- struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid];
- int txq_id = tid_data->txq_id;
-
- if (txq_id == IWL_MVM_INVALID_QUEUE)
- continue;
-
- if (tid == IWL_MAX_TID_COUNT)
- cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID);
- else
- cmd.u.modify.tid = cpu_to_le32(tid);
-
- ret = iwl_mvm_send_cmd(mvm, &hcmd);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_rx_baid_cfg_cmd cmd = {
- .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),
- .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),
- .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),
- };
- u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);
- int baid;
-
- /* mac80211 will remove sessions later, but we ignore all that */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- return 0;
-
- BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));
-
- for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) {
- struct iwl_mvm_baid_data *data;
- int ret;
-
- data = rcu_dereference_protected(mvm->baid_map[baid],
- lockdep_is_held(&mvm->mutex));
- if (!data)
- continue;
-
- if (!(data->sta_mask & old_sta_mask))
- continue;
-
- WARN_ONCE(data->sta_mask != old_sta_mask,
- "BAID data for %d corrupted - expected 0x%x found 0x%x\n",
- baid, old_sta_mask, data->sta_mask);
-
- cmd.modify.tid = cpu_to_le32(data->tid);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL,
- sizeof(cmd), &cmd);
- data->sta_mask = new_sta_mask;
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- int ret;
-
- ret = iwl_mvm_mld_update_sta_queues(mvm, sta,
- old_sta_mask,
- new_sta_mask);
- if (ret)
- return ret;
-
- ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta,
- old_sta_mask,
- new_sta_mask);
- if (ret)
- return ret;
-
- return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask);
-}
-
-int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_link_sta *mvm_sta_link;
- struct iwl_mvm_vif_link_info *mvm_vif_link;
- unsigned long links_to_add = ~old_links & new_links;
- unsigned long links_to_rem = old_links & ~new_links;
- unsigned long old_links_long = old_links;
- u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0;
- unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0;
- unsigned int link_id;
- int ret;
-
- lockdep_assert_wiphy(mvm->hw->wiphy);
- lockdep_assert_held(&mvm->mutex);
-
- for_each_set_bit(link_id, &old_links_long,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- if (WARN_ON(!mvm_sta_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- current_sta_mask |= BIT(mvm_sta_link->sta_id);
- if (links_to_rem & BIT(link_id))
- sta_mask_to_rem |= BIT(mvm_sta_link->sta_id);
- }
-
- if (sta_mask_to_rem) {
- ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
- current_sta_mask,
- current_sta_mask &
- ~sta_mask_to_rem);
- if (WARN_ON(ret))
- goto err;
-
- current_sta_mask &= ~sta_mask_to_rem;
- }
-
- for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
- mvm_vif_link = mvm_vif->link[link_id];
-
- if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
- if (WARN_ON(ret))
- goto err;
-
- if (vif->type == NL80211_IFTYPE_STATION)
- mvm_vif_link->ap_sta_id = IWL_INVALID_STA;
-
- iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id);
- }
-
- for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
- struct ieee80211_bss_conf *link_conf =
- link_conf_dereference_protected(vif, link_id);
- struct ieee80211_link_sta *link_sta =
- link_sta_dereference_protected(sta, link_id);
- mvm_vif_link = mvm_vif->link[link_id];
-
- if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta)) {
- ret = -EINVAL;
- goto err;
- }
-
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- struct iwl_mvm_link_sta *mvm_link_sta =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
- u32 sta_id;
-
- if (WARN_ON(!mvm_link_sta)) {
- ret = -EINVAL;
- goto err;
- }
-
- sta_id = mvm_link_sta->sta_id;
-
- rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
- rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id],
- link_sta);
- } else {
- if (WARN_ON(mvm_sta->link[link_id])) {
- ret = -EINVAL;
- goto err;
- }
- ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta,
- link_id);
- if (WARN_ON(ret))
- goto err;
- }
-
- link_sta->agg.max_rc_amsdu_len = 1;
- ieee80211_sta_recalc_aggregates(sta);
-
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- if (WARN_ON(!mvm_sta_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- if (vif->type == NL80211_IFTYPE_STATION)
- iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link,
- mvm_sta_link);
-
- link_sta_allocated |= BIT(link_id);
-
- sta_mask_added |= BIT(mvm_sta_link->sta_id);
-
- ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
- mvm_sta_link);
- if (WARN_ON(ret))
- goto err;
-
- link_sta_added_to_fw |= BIT(link_id);
-
- iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link);
-
- iwl_mvm_rs_rate_init(mvm, vif, sta, link_conf, link_sta,
- link_conf->chanreq.oper.chan->band);
- }
-
- if (sta_mask_added) {
- ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
- current_sta_mask,
- current_sta_mask |
- sta_mask_added);
- if (WARN_ON(ret))
- goto err;
- }
-
- return 0;
-
-err:
- /* remove all already allocated stations in FW */
- for_each_set_bit(link_id, &link_sta_added_to_fw,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
- }
-
- /* remove all already allocated station links in driver */
- for_each_set_bit(link_id, &link_sta_allocated,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id);
- }
-
- return ret;
-}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 46a9dfa58a53..402ba5dee8b2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -2450,11 +2450,6 @@ void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_mvm_vif_link_info *link,
unsigned int link_id);
-int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask);
int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags,
struct ieee80211_key_conf *keyconf);
u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
index c25edc7c1813..ff099aec7886 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -637,10 +637,6 @@ void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm,
struct iwl_mvm_link_sta *mvm_sta_link,
unsigned int link_id);
int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id);
-int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links);
u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int filter_link_id);
int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm,
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 04/15] wifi: iwlwifi: acpi: better use ARRAY_SIZE than a define
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Since we'll have to change things in this area, use the safer option to
define the size of an array.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index de9aef0d924c..b64abb8439b7 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
@@ -504,7 +504,8 @@ iwl_acpi_parse_chains_table(union acpi_object *table,
u8 num_chains, u8 num_sub_bands)
{
for (u8 chain = 0; chain < num_chains; chain++) {
- for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;
+ for (u8 subband = 0;
+ subband < ARRAY_SIZE(chains[chain].subbands);
subband++) {
/* if we don't have the values, use the default */
if (subband >= num_sub_bands) {
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 03/15] wifi: iwlwifi: uefi: decouple UEFI and firmware APIs
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
The APIs in uefi.h are not firmware API files nor are they pure software
objects. They really reflect a specific layout we expect to see in the
UEFI tables.
Since the UEFI objects are encoded into the BIOS, we can't use the same
values for the declaration of the UEFI objects and for the pure software
object like iwl_sar_profile in the firmware runtime object.
Decouple the two types.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 23 ++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index 99170a72c3f1..c6940a3c03ea 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -76,13 +76,24 @@ struct uefi_cnv_common_step_data {
u8 radio2;
} __packed;
+#define UEFI_SAR_MAX_SUB_BANDS_NUM 11
+#define UEFI_SAR_MAX_CHAINS_PER_PROFILE 4
+
+/*
+ * struct uefi_sar_profile_chain - per-chain values of a SAR profile
+ * @subbands: the SAR value for each subband
+ */
+struct uefi_sar_profile_chain {
+ u8 subbands[UEFI_SAR_MAX_SUB_BANDS_NUM];
+};
+
/*
* struct uefi_sar_profile - a SAR profile as defined in UEFI
*
* @chains: a per-chain table of SAR values
*/
struct uefi_sar_profile {
- struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE];
+ struct uefi_sar_profile_chain chains[UEFI_SAR_MAX_CHAINS_PER_PROFILE];
} __packed;
/*
@@ -125,6 +136,14 @@ struct uefi_cnv_var_wgds {
struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
} __packed;
+/*
+ * struct uefi_ppag_chain - PPAG table for a specific chain
+ * @subbands: the PPAG values for band
+ */
+struct uefi_ppag_chain {
+ s8 subbands[UEFI_SAR_MAX_SUB_BANDS_NUM];
+};
+
/*
* struct uefi_cnv_var_ppag - PPAG table as defined in UEFI
* @revision: the revision of the table
@@ -134,7 +153,7 @@ struct uefi_cnv_var_wgds {
struct uefi_cnv_var_ppag {
u8 revision;
u32 ppag_modes;
- struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
+ struct uefi_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
} __packed;
/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 02/15] wifi: iwlwifi: ensure we don't read SAR values past the limit
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
When we fill the SAR values, we read values from the BIOS store in the
firmware runtime object and write them into the command that we send to
the firmware.
We assumed that the size of the firmware command is not longer than the
BIOS tables. This has been true until now, but this is not really safe.
We will soon have an firmware API change that will increase the size of
the table in the command and we want to make sure that we don't have a
buffer overrun when we read the firmware runtime object.
Add this safety measure.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 958e71a3c958..5793c267daf7 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -241,6 +241,10 @@ static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b };
int i, j;
+ if (WARN_ON_ONCE(n_subbands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands)))
+ return -EINVAL;
+
for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) {
struct iwl_sar_profile *prof;
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 01/15] wifi: iwlwifi: mld: add support for iwl_mcc_allowed_ap_type_cmd v2
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach, Johannes Berg
In-Reply-To: <20260319090927.1090112-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
There is a new version of this command to indicate which AP type in
UNII-9 is supported per country.
This adds support for a new UEFI table that will include that data to be
filled in the new AP type table.
Rename the uats_table field in firmware_runtime structure since it
includes now the UATS and the new UNEB table coming from UEFI.
For the same reason, rename iwl_mld_init_uats.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 14 ++++++--
.../net/wireless/intel/iwlwifi/fw/runtime.h | 8 ++---
drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 36 +++++++++++++++++--
drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 11 ++++++
drivers/net/wireless/intel/iwlwifi/mld/fw.c | 2 +-
.../wireless/intel/iwlwifi/mld/regulatory.c | 32 ++++++++++++++---
.../wireless/intel/iwlwifi/mld/regulatory.h | 2 +-
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 24 +++++++------
8 files changed, 102 insertions(+), 27 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
index bd6bf931866f..25c860a05b0e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -701,13 +701,23 @@ struct iwl_pnvm_init_complete_ntfy {
#define UATS_TABLE_COL_SIZE 13
/**
- * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD
+ * struct iwl_mcc_allowed_ap_type_cmd_v1 - struct for MCC_ALLOWED_AP_TYPE_CMD
* @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS)
* @reserved: reserved
*/
-struct iwl_mcc_allowed_ap_type_cmd {
+struct iwl_mcc_allowed_ap_type_cmd_v1 {
u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
__le16 reserved;
} __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_1 */
+/**
+ * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD
+ * @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS)
+ * @mcc_to_ap_type_unii9_map: mapping an MCC to UNII-9 AP type support allowed
+ */
+struct iwl_mcc_allowed_ap_type_cmd {
+ u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
+ u8 mcc_to_ap_type_unii9_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
+} __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_2 */
+
#endif /* __iwl_fw_api_nvm_reg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index ff186fb2e0da..411e75b45530 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -106,8 +106,8 @@ struct iwl_txf_iter_data {
* @cur_fw_img: current firmware image, must be maintained by
* the driver by calling &iwl_fw_set_current_image()
* @dump: debug dump data
- * @uats_table: AP type table
- * @uats_valid: is AP type table valid
+ * @ap_type_cmd: AP type tables (for enablement on 6 GHz)
+ * @ap_type_cmd_valid: if &ap_type_cmd is valid
* @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock:
* 0: Unlocked, 1 and 2: Locked.
* Only read the UEFI variables if locked.
@@ -213,8 +213,8 @@ struct iwl_fw_runtime {
u8 ppag_bios_source;
struct iwl_sar_offset_mapping_cmd sgom_table;
bool sgom_enabled;
- struct iwl_mcc_allowed_ap_type_cmd uats_table;
- bool uats_valid;
+ struct iwl_mcc_allowed_ap_type_cmd ap_type_cmd;
+ bool ap_type_cmd_valid;
u8 uefi_tables_lock_status;
struct iwl_phy_specific_cfg phy_filters;
enum bios_source dsm_source;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
index a7ba86e06c09..d4e1ab1f7c84 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
@@ -402,11 +402,11 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
if (uats_data->revision != 1)
return -EINVAL;
- memcpy(fwrt->uats_table.mcc_to_ap_type_map,
+ memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_map,
uats_data->mcc_to_ap_type_map,
- sizeof(fwrt->uats_table.mcc_to_ap_type_map));
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_map));
- fwrt->uats_valid = true;
+ fwrt->ap_type_cmd_valid = true;
return 0;
}
@@ -429,6 +429,36 @@ void iwl_uefi_get_uats_table(struct iwl_trans *trans,
}
IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
+void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt)
+{
+ struct uefi_cnv_wlan_uneb_data *data;
+
+ data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UNEB_NAME,
+ "UNEB", sizeof(*data), NULL);
+ if (IS_ERR(data))
+ return;
+
+ if (data->revision != 1) {
+ IWL_DEBUG_RADIO(fwrt,
+ "Cannot read UNEB table. rev is invalid\n");
+ goto out;
+ }
+
+ BUILD_BUG_ON(sizeof(data->mcc_to_ap_type_map) !=
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
+
+ memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map,
+ data->mcc_to_ap_type_map,
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
+
+ fwrt->ap_type_cmd_valid = true;
+
+out:
+ kfree(data);
+}
+IWL_EXPORT_SYMBOL(iwl_uefi_get_uneb_table);
+
static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
struct uefi_sar_profile *uefi_sar_prof,
u8 prof_index, bool enabled)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index 349ac1505ad7..99170a72c3f1 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -25,6 +25,7 @@
#define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing"
#define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR"
#define IWL_UEFI_WPFC_NAME L"WPFC"
+#define IWL_UEFI_UNEB_NAME L"CnvUefiWlanUNEB"
#define IWL_SGOM_MAP_SIZE 339
@@ -63,6 +64,9 @@ struct uefi_cnv_wlan_uats_data {
u8 mcc_to_ap_type_map[IWL_UATS_MAP_SIZE - 1];
} __packed;
+/* UNEB's layout is identical to UATS's */
+#define uefi_cnv_wlan_uneb_data uefi_cnv_wlan_uats_data
+
struct uefi_cnv_common_step_data {
u8 revision;
u8 step_mode;
@@ -274,6 +278,8 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt);
void iwl_uefi_get_uats_table(struct iwl_trans *trans,
struct iwl_fw_runtime *fwrt);
+void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value);
int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt);
@@ -373,6 +379,11 @@ iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
{
}
+static inline void
+iwl_uefi_get_uneb_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
+{
+}
+
static inline
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
index 19da521a4bab..7b1fb84a641c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
@@ -513,7 +513,7 @@ static int iwl_mld_config_fw(struct iwl_mld *mld)
return ret;
iwl_mld_init_tas(mld);
- iwl_mld_init_uats(mld);
+ iwl_mld_init_ap_type_tables(mld);
return 0;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index 6ab5a3410353..d1a55b565898 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -64,6 +64,7 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld)
}
iwl_uefi_get_uats_table(mld->trans, &mld->fwrt);
+ iwl_uefi_get_uneb_table(mld->trans, &mld->fwrt);
iwl_bios_get_phy_filters(&mld->fwrt);
}
@@ -352,21 +353,42 @@ void iwl_mld_configure_lari(struct iwl_mld *mld)
ret);
}
-void iwl_mld_init_uats(struct iwl_mld *mld)
+void iwl_mld_init_ap_type_tables(struct iwl_mld *mld)
{
int ret;
struct iwl_host_cmd cmd = {
.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
MCC_ALLOWED_AP_TYPE_CMD),
- .data[0] = &mld->fwrt.uats_table,
- .len[0] = sizeof(mld->fwrt.uats_table),
+ .data[0] = &mld->fwrt.ap_type_cmd,
+ .len[0] = sizeof(mld->fwrt.ap_type_cmd),
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
- if (!mld->fwrt.uats_valid)
+ if (!mld->fwrt.ap_type_cmd_valid)
return;
- ret = iwl_mld_send_cmd(mld, &cmd);
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd.id, 1) == 1) {
+ struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd_v1 =
+ kzalloc(sizeof(*cmd_v1), GFP_KERNEL);
+
+ if (!cmd_v1)
+ return;
+
+ BUILD_BUG_ON(sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map) !=
+ sizeof(cmd_v1->mcc_to_ap_type_map));
+
+ memcpy(cmd_v1->mcc_to_ap_type_map,
+ mld->fwrt.ap_type_cmd.mcc_to_ap_type_map,
+ sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map));
+
+ cmd.data[0] = cmd_v1;
+ cmd.len[0] = sizeof(*cmd_v1);
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ kfree(cmd_v1);
+ } else {
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ }
+
if (ret)
IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
ret);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
index 3b01c645adda..5498c19789f4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
@@ -9,7 +9,7 @@
void iwl_mld_get_bios_tables(struct iwl_mld *mld);
void iwl_mld_configure_lari(struct iwl_mld *mld);
-void iwl_mld_init_uats(struct iwl_mld *mld);
+void iwl_mld_init_ap_type_tables(struct iwl_mld *mld);
void iwl_mld_init_tas(struct iwl_mld *mld);
int iwl_mld_init_ppag(struct iwl_mld *mld);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 43cf94c9a36b..f5e5c10cc581 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -459,23 +459,18 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
{
+ int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
+ MCC_ALLOWED_AP_TYPE_CMD);
+ struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {};
u8 cmd_ver;
int ret;
- struct iwl_host_cmd cmd = {
- .id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
- MCC_ALLOWED_AP_TYPE_CMD),
- .flags = 0,
- .data[0] = &mvm->fwrt.uats_table,
- .len[0] = sizeof(mvm->fwrt.uats_table),
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- };
if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}
- cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
+ cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
IWL_FW_CMD_VER_UNKNOWN);
if (cmd_ver != 1) {
IWL_DEBUG_RADIO(mvm,
@@ -486,10 +481,17 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
- if (!mvm->fwrt.uats_valid)
+ if (!mvm->fwrt.ap_type_cmd_valid)
return;
- ret = iwl_mvm_send_cmd(mvm, &cmd);
+ BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) !=
+ sizeof(cmd.mcc_to_ap_type_map));
+
+ memcpy(cmd.mcc_to_ap_type_map,
+ mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map,
+ sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map));
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
if (ret < 0)
IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
ret);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 iwlwifi-next 00/15] wifi: iwlwifi: updates - 2025-03-18
From: Miri Korenblit @ 2026-03-19 9:09 UTC (permalink / raw)
To: linux-wireless
Hi,
features and cleanups from our internal tree.
Since the remote next branch wasn't updated, this series didn't apply. Sending
again after updating the branch.
Thanks,
Miri
---
Emmanuel Grumbach (9):
wifi: iwlwifi: mld: add support for iwl_mcc_allowed_ap_type_cmd v2
wifi: iwlwifi: ensure we don't read SAR values past the limit
wifi: iwlwifi: uefi: decouple UEFI and firmware APIs
wifi: iwlwifi: acpi: better use ARRAY_SIZE than a define
wifi: iwlwifi: uefi: open code the PPAG table store operation
wifi: iwlwifi: bring iwl_fill_ppag_table to the iwlmvm
wifi: iwlwifi: regulatory: support a new command for PPAG
wifi: iwlwifi: acpi: check the size of the ACPI PPAG tables
wifi: iwlwifi: acpi: add support for PPAG rev5
Ilan Peer (2):
wifi: iwlwifi: mld: Refactor scan command handling
wifi: iwlwifi: mld: Introduce scan command version 18
Johannes Berg (1):
wifi: iwlwifi: mld: correctly set wifi generation data
Miri Korenblit (1):
wifi: iwlwifi: mld: add support for sta command version 3
Nidhish A N (1):
wifi: iwlwifi: mvm: cleanup some more MLO code
Pagadala Yesu Anjaneyulu (1):
wifi: iwlwifi: mld: remove unused scan expire time constants
drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 31 +-
drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 21 +-
.../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 94 +++++-
.../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 14 +-
.../net/wireless/intel/iwlwifi/fw/api/power.h | 8 +
.../net/wireless/intel/iwlwifi/fw/api/scan.h | 45 +++
.../wireless/intel/iwlwifi/fw/regulatory.c | 151 ++-------
.../wireless/intel/iwlwifi/fw/regulatory.h | 10 +-
.../net/wireless/intel/iwlwifi/fw/runtime.h | 8 +-
drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 60 +++-
drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 32 +-
.../wireless/intel/iwlwifi/mld/constants.h | 1 -
drivers/net/wireless/intel/iwlwifi/mld/fw.c | 2 +-
.../net/wireless/intel/iwlwifi/mld/iface.c | 101 ++++--
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 19 ++
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 1 -
.../wireless/intel/iwlwifi/mld/regulatory.c | 98 +++++-
.../wireless/intel/iwlwifi/mld/regulatory.h | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 218 ++++++++++---
drivers/net/wireless/intel/iwlwifi/mld/scan.h | 2 +
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 42 ++-
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 153 ++++++++-
.../net/wireless/intel/iwlwifi/mvm/mld-key.c | 46 ---
.../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 132 --------
.../net/wireless/intel/iwlwifi/mvm/mld-sta.c | 291 +-----------------
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 -
drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 4 -
27 files changed, 834 insertions(+), 757 deletions(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH v2 4/4] ALSA: usb-audio: qcom: Use the unified QMI service ID instead of defining it locally
From: Daniel Lezcano @ 2026-03-19 9:04 UTC (permalink / raw)
To: andersson, konradybcio
Cc: Takashi Iwai, linux-kernel, Alex Elder, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jeff Johnson, Mathieu Poirier, Srinivas Kandagatla,
Jaroslav Kysela, Takashi Iwai, Kees Cook, Greg Kroah-Hartman,
Arnd Bergmann, Mark Brown, Wesley Cheng, netdev, linux-wireless,
ath10k, ath11k, ath12k, linux-arm-msm, linux-remoteproc,
linux-sound, Dmitry Baryshkov, Dan Carpenter
In-Reply-To: <875x6ug6yf.wl-tiwai@suse.de>
Hi Bjorn,
On 3/17/26 10:07, Takashi Iwai wrote:
> On Mon, 16 Mar 2026 18:14:14 +0100,
> Daniel Lezcano wrote:
>>
>> Instead of defining a local macro with a custom name for the QMI
>> service identifier, use the one provided in qmi.h and remove the
>> locally defined macro.
>>
>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
>> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
>
> For the sound bits,
>
> Reviewed-by: Takashi Iwai <tiwai@suse.de>
This one can be picked up for qcom/for-next
Thanks
-- Daniel
^ permalink raw reply
* Re: (subset) [PATCH v1 4/8] remoteproc: qcom: Use the unified QMI service ID instead of defining it locally
From: Daniel Lezcano @ 2026-03-19 9:02 UTC (permalink / raw)
To: Bjorn Andersson, konradybcio
Cc: linux-kernel, Alex Elder, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jeff Johnson,
Mathieu Poirier, Srinivas Kandagatla, Jaroslav Kysela,
Takashi Iwai, Kees Cook, Greg Kroah-Hartman, Arnd Bergmann,
Mark Brown, Wesley Cheng, netdev, linux-wireless, ath10k, ath11k,
ath12k, linux-arm-msm, linux-remoteproc, linux-sound
In-Reply-To: <177362699063.8490.5627616886098038132.b4-ty@kernel.org>
Hi,
On 3/16/26 03:09, Bjorn Andersson wrote:
>
> On Tue, 10 Mar 2026 00:03:33 +0100, Daniel Lezcano wrote:
>> Instead of defining a local macro with a custom name for the QMI
>> service identifier, use the one provided in qmi.h and remove the
>> locally defined macro.
>>
>>
>
> Applied, thanks!
>
> [4/8] remoteproc: qcom: Use the unified QMI service ID instead of defining it locally
> commit: 95b6c029e56e4d75e2957ce7ac795da29415865b
Just a head up I'm not seeing it in qcom/for-next
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH net-next v2 08/13] bnxt: use snapshot in bnxt_cfg_rx_mode
From: Loktionov, Aleksandr @ 2026-03-19 8:31 UTC (permalink / raw)
To: Stanislav Fomichev, netdev@vger.kernel.org
Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, corbet@lwn.net,
skhan@linuxfoundation.org, andrew+netdev@lunn.ch,
michael.chan@broadcom.com, pavan.chebbi@broadcom.com,
Nguyen, Anthony L, Kitszel, Przemyslaw, saeedm@nvidia.com,
tariqt@nvidia.com, mbloch@nvidia.com, alexanderduyck@fb.com,
kernel-team@meta.com, johannes@sipsolutions.net,
sd@queasysnail.net, jianbol@nvidia.com, dtatulea@nvidia.com,
mohsin.bashr@gmail.com, Keller, Jacob E, willemb@google.com,
skhawaja@google.com, bestswngs@gmail.com,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
intel-wired-lan@lists.osuosl.org, linux-rdma@vger.kernel.org,
linux-wireless@vger.kernel.org, linux-kselftest@vger.kernel.org,
leon@kernel.org
In-Reply-To: <20260318150305.123900-9-sdf@fomichev.me>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Stanislav Fomichev
> Sent: Wednesday, March 18, 2026 4:03 PM
> To: netdev@vger.kernel.org
> Cc: davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; horms@kernel.org; corbet@lwn.net;
> skhan@linuxfoundation.org; andrew+netdev@lunn.ch;
> michael.chan@broadcom.com; pavan.chebbi@broadcom.com; Nguyen, Anthony
> L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; saeedm@nvidia.com; tariqt@nvidia.com;
> mbloch@nvidia.com; alexanderduyck@fb.com; kernel-team@meta.com;
> johannes@sipsolutions.net; sd@queasysnail.net; jianbol@nvidia.com;
> dtatulea@nvidia.com; sdf@fomichev.me; mohsin.bashr@gmail.com; Keller,
> Jacob E <jacob.e.keller@intel.com>; willemb@google.com;
> skhawaja@google.com; bestswngs@gmail.com; linux-doc@vger.kernel.org;
> linux-kernel@vger.kernel.org; intel-wired-lan@lists.osuosl.org; linux-
> rdma@vger.kernel.org; linux-wireless@vger.kernel.org; linux-
> kselftest@vger.kernel.org; leon@kernel.org
> Subject: [Intel-wired-lan] [PATCH net-next v2 08/13] bnxt: use
> snapshot in bnxt_cfg_rx_mode
>
> With the introduction of ndo_set_rx_mode_async (as discussed in [0])
> we can call bnxt_cfg_rx_mode directly. Convert bnxt_cfg_rx_mode to use
> uc/mc snapshots and move its call in bnxt_sp_task to the section that
> resets BNXT_STATE_IN_SP_TASK. Switch to direct call in
> bnxt_set_rx_mode.
>
> 0:
> https://lore.kernel.org/netdev/CACKFLi=5vj8hPqEUKDd8RTw3au5G+zRgQEqjF+
> 6NZnyoNm90KA@mail.gmail.com/
>
> Cc: Michael Chan <michael.chan@broadcom.com>
> Cc: Pavan Chebbi <pavan.chebbi@broadcom.com>
> Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
> ---
> drivers/net/ethernet/broadcom/bnxt/bnxt.c | 24 ++++++++++++++--------
> -
> 1 file changed, 15 insertions(+), 9 deletions(-)
...
> -static int bnxt_cfg_rx_mode(struct bnxt *bp)
> +static int bnxt_cfg_rx_mode(struct bnxt *bp, struct
> netdev_hw_addr_list *uc,
> + struct netdev_hw_addr_list *mc)
> {
> struct net_device *dev = bp->dev;
> struct bnxt_vnic_info *vnic = &bp-
> >vnic_info[BNXT_VNIC_DEFAULT];
> @@ -13623,7 +13625,7 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp)
> bool uc_update;
>
> netif_addr_lock_bh(dev);
> - uc_update = bnxt_uc_list_updated(bp, &dev->uc);
> + uc_update = bnxt_uc_list_updated(bp, uc);
> netif_addr_unlock_bh(dev);
>
> if (!uc_update)
> @@ -13642,7 +13644,7 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp)
> if (netdev_uc_count(dev) > (BNXT_MAX_UC_ADDRS - 1)) {
> vnic->rx_mask |=
> CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
This limit check uses the live device list, dev->uc.
In the new async model, the live list can differ from the snapshot.
> } else {
> - netdev_for_each_uc_addr(ha, dev) {
> + netdev_hw_addr_list_for_each(ha, uc) {
This loop iterates the snapshot list, uc. */
So, the guard above and the loop below are checking different data.
> memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN);
> off += ETH_ALEN;
> vnic->uc_filter_count++;
> @@ -14600,6 +14602,7 @@ static void bnxt_ulp_restart(struct bnxt *bp)
> static void bnxt_sp_task(struct work_struct *work) {
> struct bnxt *bp = container_of(work, struct bnxt, sp_task);
> + struct net_device *dev = bp->dev;
>
> set_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
> smp_mb__after_atomic();
> @@ -14613,9 +14616,6 @@ static void bnxt_sp_task(struct work_struct
> *work)
> bnxt_reenable_sriov(bp);
> }
...
> 2.53.0
^ permalink raw reply
* [GIT PULL] wireless-next-2026-03-19
From: Johannes Berg @ 2026-03-19 8:22 UTC (permalink / raw)
To: netdev; +Cc: linux-wireless
Hi,
We have only a couple of things for -next right now, though I think
we'll have a bigger pull request soon with NAN and ranging APIs and
likely driver work. But I want to be able to get the cross-merge in
since the ranging will depend on that, so here is this for now.
Please pull and let us know if there's any problem.
Thanks,
johannes
The following changes since commit 0b1324cdd8de9f54f9daf689a4ae59783c333510:
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-03-05 12:11:05 -0800)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git tags/wireless-next-2026-03-19
for you to fetch changes up to dee55bc7cb8ad70b8c8598df60f378b7aed2e41b:
qtnfmac: use alloc_netdev macro for single queue devices (2026-03-19 09:08:09 +0100)
----------------------------------------------------------------
Aside from various small improvements/cleanups, not much:
- cfg80211/mac80211: S1G and UHR improvements
- hwsim: incumbent signal report test support
----------------------------------------------------------------
Aditya Kumar Singh (1):
wifi: mac80211_hwsim: add incumbent signal interference detection support
Heitor Alves de Siqueira (2):
wifi: libertas: use USB anchors for tracking in-flight URBs
wifi: libertas: don't kill URBs in interrupt context
Johan Hovold (5):
wifi: at76c50x: drop redundant device reference
wifi: libertas: drop redundant device reference
wifi: libertas_tf: drop redundant device reference
wifi: rt2x00: drop redundant device reference
wifi: mwifiex: drop redundant device reference
Johannes Berg (13):
wifi: move action code from per-type frame structs
wifi: mac80211: remove stale TODO item
wifi: mac80211: remove AID bit stripping for print
wifi: nl80211: fix UHR capability validation
wifi: ieee80211: fix UHR operation DBE vs. P-EDCA order
wifi: nl80211: split out UHR operation information
wifi: mac80211: validate HE 6 GHz operation when EHT is used
wifi: mac80211: refactor chandef tracing macros
wifi: mac80211: always use full chanctx compatible check
wifi: cfg80211: split control freq check from chandef check
wifi: nl80211: reject S1G/60G with HT chantype
wifi: mac80211: fix STA link removal during link removal
wifi: nl80211: use int for band coming from netlink
Joshua Peisach (1):
wifi: b43: use register definitions in nphy_op_software_rfkill
Lachlan Hodges (3):
wifi: mac80211: don't use cfg80211_chandef_create() for default chandef
wifi: cfg80211: restrict cfg80211_chandef_create() to only HT-based bands
wifi: cfg80211: check non-S1G width with S1G chandef
Lorenzo Bianconi (1):
wifi: mac80211: Remove deleted sta links in ieee80211_ml_reconf_work()
Ria Thomas (1):
wifi: mac80211: add support for NDP ADDBA/DELBA for S1G
Roi L (1):
qtnfmac: use alloc_netdev macro for single queue devices
Shayne Chen (1):
wifi: ieee80211: fix definition of EHT-MCS 15 in MRU
Tim Bird (1):
wifi: Add SPDX ids to some files in the wireless subsystem
Ville Nummela (1):
wifi: rsi_91x_usb: do not pause rfkill polling when stopping mac80211
drivers/net/wireless/ath/ath11k/mac.c | 4 +-
drivers/net/wireless/ath/ath12k/mac.c | 4 +-
drivers/net/wireless/ath/ath12k/wifi7/hw.c | 2 +-
drivers/net/wireless/atmel/at76c50x-usb.c | 12 +-
drivers/net/wireless/broadcom/b43/phy_n.c | 24 ++--
drivers/net/wireless/intel/iwlwifi/mld/time_sync.c | 6 +-
.../net/wireless/intel/iwlwifi/mvm/ftm-initiator.c | 7 +-
drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c | 6 +-
drivers/net/wireless/marvell/libertas/if_usb.c | 35 ++++--
drivers/net/wireless/marvell/libertas/if_usb.h | 3 +
drivers/net/wireless/marvell/libertas_tf/if_usb.c | 2 -
drivers/net/wireless/marvell/mwifiex/tdls.c | 12 +-
drivers/net/wireless/marvell/mwifiex/usb.c | 4 -
drivers/net/wireless/marvell/mwl8k.c | 4 +-
.../net/wireless/mediatek/mt76/mt76_connac_mac.c | 6 +-
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 4 +-
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 4 +-
drivers/net/wireless/quantenna/qtnfmac/core.c | 4 +-
drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | 12 +-
drivers/net/wireless/realtek/rtl8xxxu/core.c | 14 +--
drivers/net/wireless/realtek/rtlwifi/base.c | 28 ++---
drivers/net/wireless/realtek/rtlwifi/pci.c | 2 +-
drivers/net/wireless/rsi/rsi_91x_mac80211.c | 17 ++-
drivers/net/wireless/rsi/rsi_91x_usb.c | 2 +
drivers/net/wireless/rsi/rsi_common.h | 1 +
drivers/net/wireless/silabs/wfx/data_rx.c | 8 +-
drivers/net/wireless/virtual/mac80211_hwsim.c | 64 ++++++++++
include/linux/ieee80211-eht.h | 4 +-
include/linux/ieee80211-ht.h | 3 +
include/linux/ieee80211-uhr.h | 4 +-
include/linux/ieee80211.h | 85 +++++--------
include/net/mac80211.h | 4 +
include/uapi/linux/nl80211.h | 6 +
net/mac80211/agg-rx.c | 49 +++++---
net/mac80211/agg-tx.c | 39 +++---
net/mac80211/chan.c | 90 ++++++--------
net/mac80211/debugfs.c | 1 +
net/mac80211/eht.c | 21 ++--
net/mac80211/ht.c | 37 +++---
net/mac80211/ibss.c | 18 +--
net/mac80211/ieee80211_i.h | 6 +-
net/mac80211/iface.c | 21 ++--
net/mac80211/main.c | 18 ++-
net/mac80211/mesh.c | 14 +--
net/mac80211/mesh_hwmp.c | 20 ++-
net/mac80211/mesh_plink.c | 21 ++--
net/mac80211/mlme.c | 113 +++++++++--------
net/mac80211/rx.c | 134 +++++++++------------
net/mac80211/s1g.c | 36 +++---
net/mac80211/spectmgmt.c | 31 ++---
net/mac80211/sta_info.h | 3 +-
net/mac80211/tdls.c | 29 ++---
net/mac80211/trace.h | 94 ++++++---------
net/mac80211/util.c | 5 +-
net/mac80211/vht.c | 10 +-
net/wireless/chan.c | 110 ++++++++++-------
net/wireless/nl80211.c | 37 ++++--
net/wireless/of.c | 13 +-
net/wireless/radiotap.c | 10 +-
net/wireless/reg.c | 13 +-
net/wireless/reg.h | 13 +-
net/wireless/trace.c | 1 +
net/wireless/wext-core.c | 3 +-
net/wireless/wext-priv.c | 3 +-
net/wireless/wext-proc.c | 3 +-
65 files changed, 733 insertions(+), 680 deletions(-)
^ permalink raw reply
* Re: [PATCH] wifi: ath12k: fix MAC address copy on big endian
From: Alexander Wilhelm @ 2026-03-19 8:07 UTC (permalink / raw)
To: Baochen Qiang; +Cc: Jeff Johnson, linux-wireless, ath12k, linux-kernel
In-Reply-To: <abufS5-aJhi63zs5@FUE-ALEWI-WINX>
On Thu, Mar 19, 2026 at 08:01:31AM +0100, Alexander Wilhelm wrote:
> On Thu, Mar 19, 2026 at 11:00:22AM +0800, Baochen Qiang wrote:
> >
> >
> > On 3/17/2026 7:22 PM, Alexander Wilhelm wrote:
> > > The ath12k_dp_get_mac_addr function performs a simple memcpy from a
> > > CPU-native data types into an u8 array. On a big-endian architecture, this
> > > later results in a null‑pointer dereference. Convert the data to
> >
> > curious how could this happen? how matter the endian, it is just six bytes which are not a
> > pointer hence can not be dereferenced, no?
>
> You are right, the wrong shuffling of the MAC address on big-endian platform
> itself does not immediately cause the null-pointer dereference. But later in the
> code execution this address is used, which does lead to a null pointer. The
> execution do not handle the error and continues despite the null pointer, so
> this may be an additional bug. I need some time to find the exact location
> again, but here are the logs that show the triggered null-pointer dereference:
>
> user@host:~# hostapd /mnt/custom/hostapd.conf
> Kernel attempted to read user page (8) - exploit attempt? (uid: 0)
> BUG: Kernel NULL pointer dereference on read at 0x00000008
> Faulting instruction address: 0xe2077f38
> Oops: Kernel access of bad area, sig: 11 [#1]
> BE PAGE_SIZE=4K SMP NR_CPUS=4 CoreNet Generic
> Modules linked in: ath12k(O) mac80211(O) cfg80211(O) compat(O) ...
> CPU: 1 PID: 8455 Comm: hostapd Tainted: G O 6.6.73 #0
> Hardware name: CyBoxAP-A e5500 0x80241021 CoreNet Generic
> NIP: e2077f38 LR: e2077e74 CTR: c00833f0
> REGS: d0e7bac0 TRAP: 0300 Tainted: G O (6.6.73)
> MSR: 0002b002 <CE,EE,FP,ME> CR: 28004484 XER: 00000000
> DEAR: 00000008 ESR: 00000000
> GPR00: e2077e74 d0e7bbb0 c1dc4a00 00000000 0002b002 00000058 0934edc0 001c0000
> GPR08: d0e7bb58 00000000 c9370948 00000000 c00833f0 035012ac 00000000 04e04690
> GPR16: 00000000 00000000 00000000 bf9d2070 00000000 03493d36 04e04660 c9349600
> GPR24: 00000000 00000000 00000000 c8b1d1f8 c8b1d248 d09917c0 c8b1d614 0000000e
> NIP [e2077f38] ath12k_mac_11d_scan_stop+0x1c98/0x31d0 [ath12k]
> LR [e2077e74] ath12k_mac_11d_scan_stop+0x1bd4/0x31d0 [ath12k]
> Call Trace:
> [d0e7bbb0] [e2077e74] ath12k_mac_11d_scan_stop+0x1bd4/0x31d0 [ath12k] (unreliable)
> [d0e7bc10] [e20793b4] ath12k_mac_11d_scan_stop+0x3114/0x31d0 [ath12k]
> [d0e7bc40] [e1f5b41c] ieee80211_do_open+0x13c/0x8b0 [mac80211]
> [d0e7bc70] [e1f5bb40] ieee80211_do_open+0x860/0x8b0 [mac80211]
> [d0e7bc90] [c0675318] __dev_open+0x108/0x1c0
> [d0e7bcc0] [c06758ac] __dev_change_flags+0x1dc/0x270
> [d0e7bd00] [c067596c] dev_change_flags+0x2c/0x90
> [d0e7bd20] [c0774838] devinet_ioctl+0x2c8/0x990
> [d0e7bd80] [c0776f60] inet_ioctl+0x1a0/0x270
> [d0e7be00] [c0639750] sock_ioctl+0xa0/0x580
> [d0e7be60] [c02042c4] sys_ioctl+0x4e4/0xc90
> [d0e7bee0] [c000dbac] system_call_exception+0xac/0x1f0
> [d0e7bf00] [c00110e8] ret_from_syscall+0x0/0x28
> --- interrupt: c00 at 0x2ad109c
> NIP: 02ad109c LR: 02bc3958 CTR: c0249eb0
> REGS: d0e7bf10 TRAP: 0c00 Tainted: G O (6.6.73)
> MSR: 0002d002 <CE,EE,PR,ME> CR: 88004400 XER: 20000000
>
> GPR00: 00000036 bf9d1c60 98425520 00000007 00008914 bf9d1ca0 00000002 bf9d1c98
> GPR08: 00000007 033b3d68 04e062c0 d0e7bf00 22002800 035012ac 00000000 04e04690
> GPR16: 00000000 00000000 00000000 bf9d2070 00000000 03493d36 04e04660 00000000
> GPR24: 00000000 bf9d1cf0 04e0af40 00000001 bf9d1ca0 00000007 02bc3958 00000000
> NIP [02ad109c] 0x2ad109c
> LR [02bc3958] 0x2bc3958
> --- interrupt: c00
> Code: 4bfeee39 77e91000 40c200fc 77e92000 41c20018 813b0000 28090003 41c207fc 28090002 41c20834 833e001c 835c0140 <81390008> 2c1a0000 80690000 40c2031c
> ---[ end trace 0000000000000000 ]---
>
> Kernel panic - not syncing: Fatal exception
> ---[ end Kernel panic - not syncing: Fatal exception ]---
>
>
> Best regards
> Alexander Wilhelm
Unfortunately, I currently don’t have the device available to test ath12k on
big‑endian now. Nevertheless, I tried to reproduce the same issue on ath11k.
Interestingly, I don’t even reach the null pointer dereference step. Already
when starting the device, I get the following error message:
user@host-A:~# ip link set wlan0 up
RTNETLINK answers: Connection timed out
When I have access to the device again, I can investigate the null-pointer issue
in more detail. Until then, you have the logs above, maybe they already help.
Best regards
Alexander Wilhelm
^ permalink raw reply
* Re: [PATCH] wifi: ath12k: fix MAC address copy on big endian
From: Alexander Wilhelm @ 2026-03-19 7:01 UTC (permalink / raw)
To: Baochen Qiang; +Cc: Jeff Johnson, linux-wireless, ath12k, linux-kernel
In-Reply-To: <44549364-7187-4b1a-b1fe-5bf6e309ec16@oss.qualcomm.com>
On Thu, Mar 19, 2026 at 11:00:22AM +0800, Baochen Qiang wrote:
>
>
> On 3/17/2026 7:22 PM, Alexander Wilhelm wrote:
> > The ath12k_dp_get_mac_addr function performs a simple memcpy from a
> > CPU-native data types into an u8 array. On a big-endian architecture, this
> > later results in a null‑pointer dereference. Convert the data to
>
> curious how could this happen? how matter the endian, it is just six bytes which are not a
> pointer hence can not be dereferenced, no?
You are right, the wrong shuffling of the MAC address on big-endian platform
itself does not immediately cause the null-pointer dereference. But later in the
code execution this address is used, which does lead to a null pointer. The
execution do not handle the error and continues despite the null pointer, so
this may be an additional bug. I need some time to find the exact location
again, but here are the logs that show the triggered null-pointer dereference:
user@host:~# hostapd /mnt/custom/hostapd.conf
Kernel attempted to read user page (8) - exploit attempt? (uid: 0)
BUG: Kernel NULL pointer dereference on read at 0x00000008
Faulting instruction address: 0xe2077f38
Oops: Kernel access of bad area, sig: 11 [#1]
BE PAGE_SIZE=4K SMP NR_CPUS=4 CoreNet Generic
Modules linked in: ath12k(O) mac80211(O) cfg80211(O) compat(O) ...
CPU: 1 PID: 8455 Comm: hostapd Tainted: G O 6.6.73 #0
Hardware name: CyBoxAP-A e5500 0x80241021 CoreNet Generic
NIP: e2077f38 LR: e2077e74 CTR: c00833f0
REGS: d0e7bac0 TRAP: 0300 Tainted: G O (6.6.73)
MSR: 0002b002 <CE,EE,FP,ME> CR: 28004484 XER: 00000000
DEAR: 00000008 ESR: 00000000
GPR00: e2077e74 d0e7bbb0 c1dc4a00 00000000 0002b002 00000058 0934edc0 001c0000
GPR08: d0e7bb58 00000000 c9370948 00000000 c00833f0 035012ac 00000000 04e04690
GPR16: 00000000 00000000 00000000 bf9d2070 00000000 03493d36 04e04660 c9349600
GPR24: 00000000 00000000 00000000 c8b1d1f8 c8b1d248 d09917c0 c8b1d614 0000000e
NIP [e2077f38] ath12k_mac_11d_scan_stop+0x1c98/0x31d0 [ath12k]
LR [e2077e74] ath12k_mac_11d_scan_stop+0x1bd4/0x31d0 [ath12k]
Call Trace:
[d0e7bbb0] [e2077e74] ath12k_mac_11d_scan_stop+0x1bd4/0x31d0 [ath12k] (unreliable)
[d0e7bc10] [e20793b4] ath12k_mac_11d_scan_stop+0x3114/0x31d0 [ath12k]
[d0e7bc40] [e1f5b41c] ieee80211_do_open+0x13c/0x8b0 [mac80211]
[d0e7bc70] [e1f5bb40] ieee80211_do_open+0x860/0x8b0 [mac80211]
[d0e7bc90] [c0675318] __dev_open+0x108/0x1c0
[d0e7bcc0] [c06758ac] __dev_change_flags+0x1dc/0x270
[d0e7bd00] [c067596c] dev_change_flags+0x2c/0x90
[d0e7bd20] [c0774838] devinet_ioctl+0x2c8/0x990
[d0e7bd80] [c0776f60] inet_ioctl+0x1a0/0x270
[d0e7be00] [c0639750] sock_ioctl+0xa0/0x580
[d0e7be60] [c02042c4] sys_ioctl+0x4e4/0xc90
[d0e7bee0] [c000dbac] system_call_exception+0xac/0x1f0
[d0e7bf00] [c00110e8] ret_from_syscall+0x0/0x28
--- interrupt: c00 at 0x2ad109c
NIP: 02ad109c LR: 02bc3958 CTR: c0249eb0
REGS: d0e7bf10 TRAP: 0c00 Tainted: G O (6.6.73)
MSR: 0002d002 <CE,EE,PR,ME> CR: 88004400 XER: 20000000
GPR00: 00000036 bf9d1c60 98425520 00000007 00008914 bf9d1ca0 00000002 bf9d1c98
GPR08: 00000007 033b3d68 04e062c0 d0e7bf00 22002800 035012ac 00000000 04e04690
GPR16: 00000000 00000000 00000000 bf9d2070 00000000 03493d36 04e04660 00000000
GPR24: 00000000 bf9d1cf0 04e0af40 00000001 bf9d1ca0 00000007 02bc3958 00000000
NIP [02ad109c] 0x2ad109c
LR [02bc3958] 0x2bc3958
--- interrupt: c00
Code: 4bfeee39 77e91000 40c200fc 77e92000 41c20018 813b0000 28090003 41c207fc 28090002 41c20834 833e001c 835c0140 <81390008> 2c1a0000 80690000 40c2031c
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Fatal exception
---[ end Kernel panic - not syncing: Fatal exception ]---
Best regards
Alexander Wilhelm
^ permalink raw reply
* Re: [PATCH v2 rtw-next] wifi: rtw89: usb: Rx aggregation for RTL8832CU/RTL8851BU
From: Ping-Ke Shih @ 2026-03-19 7:01 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless; +Cc: rtl8821cerfe2, mh_chen, isaiah
In-Reply-To: <20260312055724.12177-1-pkshih@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> From: Shin-Yi Lin <isaiah@realtek.com>
>
> USB RX Aggregation is a performance optimization technique used
> in USB network devices to increase throughput.
>
> Instead of sending every received network packet to the host computer
> individually, the device hardware groups multiple smaller packets
> into a single, large USB Bulk Transfer.
>
> * toAP/toNB use iperf3 respectively.
>
> With Cisco BE6000 - iperf3 tcp 10 pair (to another NB)
>
> [6G 160Mhz]:
>
> RTL8832CU-USB3.0
> before after
> TX 941 941
> RX 847 919
>
> RTL8832CU-USB2.0
> before after
> TX 293 286
> RX 342 356
>
> [5G 80Mhz]:
>
> RTL8832CU-USB3.0
> before after
> TX 864 877
> RX 864 902
>
> RTL8832CU-USB2.0
> before after
> TX 279 271
> RX 327 349
>
> RTL8851BU
> before after
> TX 115 114
> RX 295 306
>
> Signed-off-by: Shin-Yi Lin <isaiah@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
1 patch(es) applied to rtw-next branch of rtw.git, thanks.
eef6d4449e8a wifi: rtw89: usb: Rx aggregation for RTL8832CU/RTL8851BU
---
https://github.com/pkshih/rtw.git
^ permalink raw reply
* [PATCH ath-next v3] wifi: ath11k: Pass the correct value of each TID during a stop AMPDU session
From: Reshma Immaculate Rajkumar @ 2026-03-19 6:56 UTC (permalink / raw)
To: ath11k; +Cc: linux-wireless, Reshma Immaculate Rajkumar
During ongoing traffic, a request to stop an AMPDU session
for one TID could incorrectly affect other active sessions.
This can happen because an incorrect TID reference would be
passed when updating the BA session state, causing the wrong
session to be stopped. As a result, the affected session would
be reduced to a minimal BA size, leading to a noticeable
throughput degradation.
Fix this issue by passing the correct argument from
ath11k_dp_rx_ampdu_stop() to ath11k_peer_rx_tid_reo_update()
during a stop AMPDU session. Instead of passing peer->tx_tid, which
is the base address of the array, corresponding to TID 0; pass
the value of &peer->rx_tid[params->tid], where the different TID numbers
are accounted for.
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
Fixes: d5c65159f2895 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: Reshma Immaculate Rajkumar <reshma.rajkumar@oss.qualcomm.com>
---
v2:
* Added QTI yearless copyright
v3:
* Removed QUIC copyright information
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 49d959b2e148..85defe11750d 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/ieee80211.h>
@@ -1110,9 +1110,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
struct ath11k_base *ab = ar->ab;
struct ath11k_peer *peer;
struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta);
+ struct dp_rx_tid *rx_tid;
int vdev_id = arsta->arvif->vdev_id;
- dma_addr_t paddr;
- bool active;
int ret;
spin_lock_bh(&ab->base_lock);
@@ -1124,15 +1123,14 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
return -ENOENT;
}
- paddr = peer->rx_tid[params->tid].paddr;
- active = peer->rx_tid[params->tid].active;
+ rx_tid = &peer->rx_tid[params->tid];
- if (!active) {
+ if (!rx_tid->active) {
spin_unlock_bh(&ab->base_lock);
return 0;
}
- ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
+ ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, 1, 0, false);
spin_unlock_bh(&ab->base_lock);
if (ret) {
ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",
@@ -1141,7 +1139,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
}
ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
- params->sta->addr, paddr,
+ params->sta->addr,
+ rx_tid->paddr,
params->tid, 1, 1);
if (ret)
ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n",
base-commit: 7bbb578fc43e7dcb8690cfc98844bd67bc311e8a
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v3 1/1] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
From: Sean Wang @ 2026-03-19 6:00 UTC (permalink / raw)
To: Lucid Duck; +Cc: linux-wireless, nbd, lorenzo, linux-mediatek, morrownr
In-Reply-To: <20260317173016.136975-2-lucid_duck@justthetip.ca>
Hi,
On Tue, Mar 17, 2026 at 12:30 PM Lucid Duck <lucid_duck@justthetip.ca> wrote:
>
> The mt7921 driver never updates phy->txpower_cur
> when TX power rate configuration is sent to firmware. This causes
> mt76_get_txpower() to report bogus values to userspace (typically
> 3 dBm) regardless of actual regulatory or SAR limits. User-set
> txpower limits via iw are also not reflected.
>
> Three root causes are addressed:
>
> 1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
> the correct bounded TX power for each channel but discards the return
> value of mt76_get_rate_power_limits(). Fix: capture the return value
> and store it to phy->txpower_cur when processing the current channel.
>
> 2. mt7921 uses the chanctx model but its add_chanctx callback bypasses
> the common mt76_phy_update_channel(), leaving phy->chandef stale.
> Fix: update phy->chandef from ctx->def in both add_chanctx and
> change_chanctx, and trigger the rate power path to refresh
> txpower_cur. Also trigger on IEEE80211_CONF_CHANGE_CHANNEL in
> config(), matching mt7915.
>
> 3. For chanctx drivers, mac80211 routes user txpower changes through
> BSS_CHANGED_TXPOWER in bss_info_changed() -- not through
> IEEE80211_CONF_CHANGE_POWER in config(). hw->conf.power_level is
> never updated. Fix: handle BSS_CHANGED_TXPOWER in
> mt7921_bss_info_changed(), bridge bss_conf.txpower to
> hw->conf.power_level, and re-trigger the rate power path.
>
> Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.17.1-300.fc43:
>
> Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
> After: correct per-band values, user limits reflected
>
> Test results (regulatory domain: Canada/CA):
> - 2.4GHz ch6: 33 dBm (30 dBm limit + 3 dBm 2x2 path delta)
> - 5GHz ch36: 26 dBm (23 dBm limit + 3 dBm path delta)
> - 6GHz ch5: 15 dBm (12 dBm limit + 3 dBm path delta)
> - Band switch: 100 cycles, 0 failures
> - Module reload: 50 cycles, 0 failures
> - 2-hour soak: 480 samples, zero drift
> - Regdomain switching: 10 countries, all correct
> - User txpower limits: reflected on all bands
> - Monitor mode: correct on all tested channels
>
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
> .../wireless/mediatek/mt76/mt76_connac_mcu.c | 12 +++++++---
> .../net/wireless/mediatek/mt76/mt7921/main.c | 22 ++++++++++++++++++-
> 2 files changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 16db0f208..5856924a9 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2193,14 +2193,20 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
> .hw_value = ch_list[idx],
> .band = band,
> };
> - s8 reg_power, sar_power;
> + s8 reg_power, sar_power, max_power;
>
> reg_power = mt76_connac_get_ch_power(phy, &chan,
> tx_power);
> sar_power = mt76_get_sar_power(phy, &chan, reg_power);
>
> - mt76_get_rate_power_limits(phy, &chan, limits,
> - sar_power);
> + max_power = mt76_get_rate_power_limits(phy, &chan,
> + limits,
> + sar_power);
> +
> + if (phy->chandef.chan &&
> + phy->chandef.chan->hw_value == ch_list[idx] &&
> + phy->chandef.chan->band == band)
> + phy->txpower_cur = max_power;
>
> tx_power_tlv.last_msg = ch_list[idx] == last_ch;
> sku_tlbv.channel = ch_list[idx];
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 5881040ac..38a59c6f2 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -638,7 +638,8 @@ static int mt7921_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
>
> mt792x_mutex_acquire(dev);
>
> - if (changed & IEEE80211_CONF_CHANGE_POWER) {
> + if (changed & (IEEE80211_CONF_CHANGE_POWER |
> + IEEE80211_CONF_CHANGE_CHANNEL)) {
> ret = mt7921_set_tx_sar_pwr(hw, NULL);
> if (ret)
> goto out;
> @@ -719,6 +720,14 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
> if (changed & BSS_CHANGED_CQM)
> mt7921_mcu_set_rssimonitor(dev, vif);
>
> + if (changed & BSS_CHANGED_TXPOWER) {
> + int tx_power = info->txpower;
> +
> + if (tx_power != INT_MIN && tx_power > 0)
> + hw->conf.power_level = tx_power;
> + mt7921_set_tx_sar_pwr(hw, NULL);
> + }
> +
> if (changed & BSS_CHANGED_ASSOC) {
> mt7921_mcu_sta_update(dev, NULL, vif, true,
> MT76_STA_INFO_STATE_ASSOC);
> @@ -1360,8 +1369,15 @@ mt7921_add_chanctx(struct ieee80211_hw *hw,
> struct ieee80211_chanctx_conf *ctx)
> {
> struct mt792x_dev *dev = mt792x_hw_dev(hw);
> + struct mt76_phy *mphy = hw->priv;
>
> dev->new_ctx = ctx;
> + mphy->chandef = ctx->def;
> +
> + mt792x_mutex_acquire(dev);
> + mt7921_set_tx_sar_pwr(hw, NULL);
> + mt792x_mutex_release(dev);
> +
> return 0;
> }
>
> @@ -1396,6 +1412,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
> mt7921_mcu_config_sniffer(mvif, ctx);
> else
> mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
> +
> + phy->mt76->chandef = ctx->def;
> + mt7921_set_tx_sar_pwr(hw, NULL);
> +
I do not think the additional mt7921_set_tx_sar_pwr() calls are justified.
mt7921_set_tx_sar_pwr() is not a lightweight per-channel refresh. It
rebuilds and pushes the rate txpower table for all channels in the band
through the MCU path. This is appropriate when the underlying power
constraints change (e.g. SAR, regulatory limits, or user-configured
txpower), but not for pure channel/chanctx transitions.
add_chanctx(), change_chanctx(), and IEEE80211_CONF_CHANGE_CHANNEL only
reflect channel/context changes and do not imply that the firmware
txpower table needs to be recomputed. Using mt7921_set_tx_sar_pwr() here
effectively turns it into a catch-all sync path.
If the issue is stale txpower reporting after a channel switch, it should
be fixed by updating phy->txpower_cur from the already computed bounded
max power for the current channel, rather than re-triggering full table
programming on every chanctx/channel event.
BSS_CHANGED_TXPOWER is also problematic. It is a per-BSS (per-vif) event,
while hw->conf.power_level is shared per-HW state. Writing
info->txpower into hw->conf.power_level allows one interface to affect
the effective txpower of others sharing the same PHY, which breaks
multi-vif semantics.
> mt792x_mutex_release(phy->dev);
> }
>
> --
> 2.51.0
>
^ permalink raw reply
* [PATCH 2/2] wifi: mt76: mt7921u: escalate broken USB transport to device reset
From: Sean Wang @ 2026-03-19 4:53 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260319045357.13796-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Check the USB control path before running the normal WFSYS reset flow.
If USB access is no longer reliable, stop the WFSYS-only reset path,
mark the device as bus_hung, and queue a USB device reset instead.
Reuse the existing bus_hung state to represent transport-level failure,
keeping the semantics consistent with the SDIO path.
Also initialize bus_hung explicitly during probe for consistency.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7921/mac.c | 4 ++-
.../net/wireless/mediatek/mt76/mt7921/usb.c | 5 ++++
drivers/net/wireless/mediatek/mt76/mt792x.h | 1 +
.../net/wireless/mediatek/mt76/mt792x_usb.c | 26 +++++++++++++++++++
4 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 03b4960db73f..d27dbee8df1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -675,7 +675,9 @@ void mt7921_mac_reset_work(struct work_struct *work)
if (!ret)
break;
}
- if (mt76_is_sdio(&dev->mt76) && atomic_read(&dev->mt76.bus_hung))
+
+ if ((mt76_is_sdio(&dev->mt76) || mt76_is_usb(&dev->mt76)) &&
+ atomic_read(&dev->mt76.bus_hung))
return;
if (i == 10)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
index 6be28f4152ed..8c0f0e4ef87b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
@@ -88,6 +88,10 @@ static int mt7921u_mac_reset(struct mt792x_dev *dev)
{
int err;
+ mt792xu_reset_on_bus_error(dev);
+ if (atomic_read(&dev->mt76.bus_hung))
+ return 0;
+
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
@@ -196,6 +200,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &hif_ops;
+ atomic_set(&dev->mt76.bus_hung, false);
mt792xu_reset_work_init(dev);
udev = usb_get_dev(udev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 5f06074591ca..74222c507b81 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -494,6 +494,7 @@ int mt792xu_init_reset(struct mt792x_dev *dev);
void mt792xu_reset_work_init(struct mt792x_dev *dev);
void mt792xu_reset_work_cleanup(struct mt792x_dev *dev);
int mt792xu_check_bus(struct mt792x_dev *dev);
+int mt792xu_reset_on_bus_error(struct mt792x_dev *dev);
u32 mt792xu_rr(struct mt76_dev *dev, u32 addr);
void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val);
u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
index 2558d87b1e0f..6b10d035bcbc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
@@ -60,6 +60,32 @@ int mt792xu_check_bus(struct mt792x_dev *dev)
}
EXPORT_SYMBOL_GPL(mt792xu_check_bus);
+int mt792xu_reset_on_bus_error(struct mt792x_dev *dev)
+{
+ int err = 0;
+
+ if (!atomic_read(&dev->mt76.bus_hung))
+ err = mt792xu_check_bus(dev);
+
+ if (err) {
+ atomic_set(&dev->mt76.bus_hung, true);
+
+ if (!atomic_xchg(&dev->usb_reset_pending, 1)) {
+ dev_warn(dev->mt76.dev,
+ "USB transport access failed (%d), queueing device reset\n",
+ err);
+
+ schedule_work(&dev->usb_reset_work);
+ }
+
+ return err;
+ }
+
+ atomic_set(&dev->mt76.bus_hung, false);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt792xu_reset_on_bus_error);
+
u32 mt792xu_rr(struct mt76_dev *dev, u32 addr)
{
u32 ret;
--
2.43.0
^ permalink raw reply related
* [PATCH 1/2] wifi: mt76: mt792x: add common USB transport reset helpers
From: Sean Wang @ 2026-03-19 4:53 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
From: Sean Wang <sean.wang@mediatek.com>
Add per-device USB reset work and a control-path access check helper
for mt7921u and mt7925u.
This prepares common infrastructure for transport-level recovery while
keeping the reset state per-device for correct lifetime handling.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7921/usb.c | 2 +
.../net/wireless/mediatek/mt76/mt7925/usb.c | 2 +
drivers/net/wireless/mediatek/mt76/mt792x.h | 5 ++
.../net/wireless/mediatek/mt76/mt792x_usb.c | 50 +++++++++++++++++++
4 files changed, 59 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
index 17057e68bf21..6be28f4152ed 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
@@ -196,6 +196,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &hif_ops;
+ mt792xu_reset_work_init(dev);
udev = usb_get_dev(udev);
usb_reset_device(udev);
@@ -244,6 +245,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
error:
mt76u_queues_deinit(&dev->mt76);
+ mt792xu_reset_work_cleanup(dev);
usb_set_intfdata(usb_intf, NULL);
usb_put_dev(interface_to_usbdev(usb_intf));
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
index d9968f03856d..8b5d58125352 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -184,6 +184,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf,
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &hif_ops;
+ mt792xu_reset_work_init(dev);
udev = usb_get_dev(udev);
usb_reset_device(udev);
@@ -232,6 +233,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf,
error:
mt76u_queues_deinit(&dev->mt76);
+ mt792xu_reset_work_cleanup(dev);
usb_set_intfdata(usb_intf, NULL);
usb_put_dev(interface_to_usbdev(usb_intf));
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 4ff93f2cd624..5f06074591ca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -243,6 +243,8 @@ struct mt792x_dev {
wait_queue_head_t wait;
struct work_struct init_work;
+ struct work_struct usb_reset_work;
+ atomic_t usb_reset_pending;
u8 fw_debug;
u8 fw_features;
@@ -489,6 +491,9 @@ int mt792xu_dma_init(struct mt792x_dev *dev, bool resume);
int mt792xu_mcu_power_on(struct mt792x_dev *dev);
int mt792xu_wfsys_reset(struct mt792x_dev *dev);
int mt792xu_init_reset(struct mt792x_dev *dev);
+void mt792xu_reset_work_init(struct mt792x_dev *dev);
+void mt792xu_reset_work_cleanup(struct mt792x_dev *dev);
+int mt792xu_check_bus(struct mt792x_dev *dev);
u32 mt792xu_rr(struct mt76_dev *dev, u32 addr);
void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val);
u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
index 47827d1c5ccb..2558d87b1e0f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
@@ -11,6 +11,55 @@
#include "mt792x.h"
#include "mt76_connac2_mac.h"
+static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf)
+{
+ return __mt76u_vendor_request(dev, MT_VEND_READ_EXT,
+ USB_DIR_IN | MT_USB_TYPE_VENDOR,
+ (u16)(addr >> 16), (u16)addr,
+ buf, sizeof(__le32));
+}
+
+static void mt792xu_reset_work(struct work_struct *work)
+{
+ struct mt792x_dev *dev =
+ container_of(work, struct mt792x_dev, usb_reset_work);
+ struct usb_interface *usb_intf = to_usb_interface(dev->mt76.dev);
+
+ if (usb_intf && usb_get_intfdata(usb_intf) == dev)
+ usb_queue_reset_device(usb_intf);
+
+ atomic_set(&dev->usb_reset_pending, 0);
+}
+
+void mt792xu_reset_work_init(struct mt792x_dev *dev)
+{
+ INIT_WORK(&dev->usb_reset_work, mt792xu_reset_work);
+ atomic_set(&dev->usb_reset_pending, 0);
+}
+EXPORT_SYMBOL_GPL(mt792xu_reset_work_init);
+
+void mt792xu_reset_work_cleanup(struct mt792x_dev *dev)
+{
+ cancel_work_sync(&dev->usb_reset_work);
+ atomic_set(&dev->usb_reset_pending, 0);
+}
+EXPORT_SYMBOL_GPL(mt792xu_reset_work_cleanup);
+
+int mt792xu_check_bus(struct mt792x_dev *dev)
+{
+ int ret;
+
+ mutex_lock(&dev->mt76.usb.usb_ctrl_mtx);
+ ret = mt792xu_read32(&dev->mt76, MT_HW_CHIPID, dev->mt76.usb.data);
+ mutex_unlock(&dev->mt76.usb.usb_ctrl_mtx);
+
+ if (ret == sizeof(__le32))
+ return 0;
+
+ return ret < 0 ? ret : -EIO;
+}
+EXPORT_SYMBOL_GPL(mt792xu_check_bus);
+
u32 mt792xu_rr(struct mt76_dev *dev, u32 addr)
{
u32 ret;
@@ -333,6 +382,7 @@ void mt792xu_disconnect(struct usb_interface *usb_intf)
{
struct mt792x_dev *dev = usb_get_intfdata(usb_intf);
+ mt792xu_reset_work_cleanup(dev);
cancel_work_sync(&dev->init_work);
if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
return;
--
2.43.0
^ permalink raw reply related
* [wireless:for-next] BUILD SUCCESS d5ad6ab61cbd89afdb60881f6274f74328af3ee9
From: kernel test robot @ 2026-03-19 4:42 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git for-next
branch HEAD: d5ad6ab61cbd89afdb60881f6274f74328af3ee9 wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
elapsed time: 754m
configs tested: 163
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc defconfig gcc-15.2.0
arc randconfig-001-20260319 gcc-11.5.0
arc randconfig-002-20260319 gcc-11.5.0
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm randconfig-001-20260319 gcc-11.5.0
arm randconfig-002-20260319 gcc-11.5.0
arm randconfig-003-20260319 gcc-11.5.0
arm randconfig-004-20260319 gcc-11.5.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260319 gcc-15.2.0
arm64 randconfig-002-20260319 gcc-15.2.0
arm64 randconfig-003-20260319 gcc-15.2.0
arm64 randconfig-004-20260319 gcc-15.2.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260319 gcc-15.2.0
csky randconfig-002-20260319 gcc-15.2.0
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260319 gcc-11.5.0
hexagon randconfig-002-20260319 gcc-11.5.0
i386 allmodconfig clang-20
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 buildonly-randconfig-001-20260319 gcc-14
i386 buildonly-randconfig-002-20260319 gcc-14
i386 buildonly-randconfig-003-20260319 gcc-14
i386 buildonly-randconfig-004-20260319 gcc-14
i386 buildonly-randconfig-005-20260319 gcc-14
i386 buildonly-randconfig-006-20260319 gcc-14
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260319 gcc-14
i386 randconfig-002-20260319 gcc-14
i386 randconfig-003-20260319 gcc-14
i386 randconfig-004-20260319 gcc-14
i386 randconfig-005-20260319 gcc-14
i386 randconfig-006-20260319 gcc-14
i386 randconfig-007-20260319 gcc-14
i386 randconfig-011-20260319 clang-20
i386 randconfig-012-20260319 clang-20
i386 randconfig-013-20260319 clang-20
i386 randconfig-014-20260319 clang-20
i386 randconfig-015-20260319 clang-20
i386 randconfig-016-20260319 clang-20
i386 randconfig-017-20260319 clang-20
loongarch allmodconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260319 gcc-11.5.0
loongarch randconfig-002-20260319 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
m68k mac_defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
nios2 allmodconfig clang-23
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 randconfig-001-20260319 gcc-11.5.0
nios2 randconfig-002-20260319 gcc-11.5.0
openrisc allmodconfig clang-23
openrisc allnoconfig clang-23
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260319 clang-19
parisc randconfig-002-20260319 clang-19
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc chrp32_defconfig clang-19
powerpc randconfig-001-20260319 clang-19
powerpc randconfig-002-20260319 clang-19
powerpc64 randconfig-001-20260319 clang-19
powerpc64 randconfig-002-20260319 clang-19
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh defconfig gcc-14
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260319 gcc-8.5.0
sparc randconfig-002-20260319 gcc-8.5.0
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260319 gcc-8.5.0
sparc64 randconfig-002-20260319 gcc-8.5.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260319 gcc-8.5.0
um randconfig-002-20260319 gcc-8.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260319 clang-20
x86_64 buildonly-randconfig-002-20260319 clang-20
x86_64 buildonly-randconfig-003-20260319 clang-20
x86_64 buildonly-randconfig-004-20260319 clang-20
x86_64 buildonly-randconfig-005-20260319 clang-20
x86_64 buildonly-randconfig-006-20260319 clang-20
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260319 gcc-14
x86_64 randconfig-002-20260319 gcc-14
x86_64 randconfig-003-20260319 gcc-14
x86_64 randconfig-004-20260319 gcc-14
x86_64 randconfig-005-20260319 gcc-14
x86_64 randconfig-006-20260319 gcc-14
x86_64 randconfig-011-20260319 gcc-13
x86_64 randconfig-012-20260319 gcc-13
x86_64 randconfig-013-20260319 gcc-13
x86_64 randconfig-014-20260319 gcc-13
x86_64 randconfig-015-20260319 gcc-13
x86_64 randconfig-016-20260319 gcc-13
x86_64 randconfig-071-20260319 clang-20
x86_64 randconfig-072-20260319 clang-20
x86_64 randconfig-073-20260319 clang-20
x86_64 randconfig-074-20260319 clang-20
x86_64 randconfig-075-20260319 clang-20
x86_64 randconfig-076-20260319 clang-20
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa randconfig-001-20260319 gcc-8.5.0
xtensa randconfig-002-20260319 gcc-8.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox