summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel')
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Kconfig1
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c2
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c120
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c7
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.h4
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/commands.h9
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/bz.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/dr.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/sc.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.c132
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.h28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/commands.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h167
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/power.h37
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h55
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/stats.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dump.c69
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.c32
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.h8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c151
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.c236
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.h141
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/fw.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c123
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c71
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/nan.c5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/nan.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.c5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.c5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.c178
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.c25
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.c254
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.h11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.c50
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.c31
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/time_sync.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.c78
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c160
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c132
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c291
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ptp.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tdls.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c2
80 files changed, 1806 insertions, 1173 deletions
diff --git a/drivers/net/wireless/intel/ipw2x00/Kconfig b/drivers/net/wireless/intel/ipw2x00/Kconfig
index b92df91adb3a..b508f14542d5 100644
--- a/drivers/net/wireless/intel/ipw2x00/Kconfig
+++ b/drivers/net/wireless/intel/ipw2x00/Kconfig
@@ -154,7 +154,6 @@ config LIBIPW
depends on PCI && CFG80211
select WIRELESS_EXT
select CRYPTO
- select CRYPTO_MICHAEL_MIC
select CRYPTO_LIB_ARC4
select CRC32
help
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index 248a051da52d..c11428485dcc 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -4838,7 +4838,7 @@ static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
/* If IPv6 is configured in the kernel then we don't want to filter out all
* of the multicast packets as IPv6 needs some. */
-#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+#if !defined(CONFIG_IPV6)
cmd.host_command = ADD_MULTICAST;
cmd.host_command_sequence = 0;
cmd.host_command_length = 0;
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c
index c6b0de8d91ae..24bb28ab7a49 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c
@@ -25,8 +25,6 @@
#include <linux/ieee80211.h>
#include <net/iw_handler.h>
#include <crypto/arc4.h>
-#include <crypto/hash.h>
-#include <linux/crypto.h>
#include <linux/crc32.h>
#include "libipw.h"
@@ -57,11 +55,6 @@ struct libipw_tkip_data {
struct arc4_ctx rx_ctx_arc4;
struct arc4_ctx tx_ctx_arc4;
- struct crypto_shash *rx_tfm_michael;
- struct crypto_shash *tx_tfm_michael;
-
- /* scratch buffers for virt_to_page() (crypto API) */
- u8 rx_hdr[16], tx_hdr[16];
unsigned long flags;
};
@@ -89,41 +82,14 @@ static void *libipw_tkip_init(int key_idx)
priv = kzalloc_obj(*priv, GFP_ATOMIC);
if (priv == NULL)
- goto fail;
+ return priv;
priv->key_idx = key_idx;
-
- priv->tx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0);
- if (IS_ERR(priv->tx_tfm_michael)) {
- priv->tx_tfm_michael = NULL;
- goto fail;
- }
-
- priv->rx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0);
- if (IS_ERR(priv->rx_tfm_michael)) {
- priv->rx_tfm_michael = NULL;
- goto fail;
- }
-
return priv;
-
- fail:
- if (priv) {
- crypto_free_shash(priv->tx_tfm_michael);
- crypto_free_shash(priv->rx_tfm_michael);
- kfree(priv);
- }
-
- return NULL;
}
static void libipw_tkip_deinit(void *priv)
{
- struct libipw_tkip_data *_priv = priv;
- if (_priv) {
- crypto_free_shash(_priv->tx_tfm_michael);
- crypto_free_shash(_priv->rx_tfm_michael);
- }
kfree_sensitive(priv);
}
@@ -464,73 +430,6 @@ static int libipw_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
return keyidx;
}
-static int michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr,
- u8 *data, size_t data_len, u8 *mic)
-{
- SHASH_DESC_ON_STACK(desc, tfm_michael);
- int err;
-
- if (tfm_michael == NULL) {
- pr_warn("%s(): tfm_michael == NULL\n", __func__);
- return -1;
- }
-
- desc->tfm = tfm_michael;
-
- if (crypto_shash_setkey(tfm_michael, key, 8))
- return -1;
-
- err = crypto_shash_init(desc);
- if (err)
- goto out;
- err = crypto_shash_update(desc, hdr, 16);
- if (err)
- goto out;
- err = crypto_shash_update(desc, data, data_len);
- if (err)
- goto out;
- err = crypto_shash_final(desc, mic);
-
-out:
- shash_desc_zero(desc);
- return err;
-}
-
-static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
-{
- struct ieee80211_hdr *hdr11;
-
- hdr11 = (struct ieee80211_hdr *)skb->data;
-
- switch (le16_to_cpu(hdr11->frame_control) &
- (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
- case IEEE80211_FCTL_TODS:
- memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
- break;
- case IEEE80211_FCTL_FROMDS:
- memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
- break;
- case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
- memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
- break;
- default:
- memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
- break;
- }
-
- if (ieee80211_is_data_qos(hdr11->frame_control)) {
- hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
- & IEEE80211_QOS_CTL_TID_MASK;
- } else
- hdr[12] = 0; /* priority */
-
- hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
-}
-
static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len,
void *priv)
{
@@ -544,12 +443,9 @@ static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len,
return -1;
}
- michael_mic_hdr(skb, tkey->tx_hdr);
pos = skb_put(skb, 8);
- if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
- skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
- return -1;
-
+ michael_mic(&tkey->key[16], (struct ieee80211_hdr *)skb->data,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos);
return 0;
}
@@ -583,10 +479,8 @@ static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx,
if (!tkey->key_set)
return -1;
- michael_mic_hdr(skb, tkey->rx_hdr);
- if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
- skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
- return -1;
+ michael_mic(&tkey->key[24], (struct ieee80211_hdr *)skb->data,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic);
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
@@ -614,17 +508,13 @@ static int libipw_tkip_set_key(void *key, int len, u8 * seq, void *priv)
{
struct libipw_tkip_data *tkey = priv;
int keyidx;
- struct crypto_shash *tfm = tkey->tx_tfm_michael;
struct arc4_ctx *tfm2 = &tkey->tx_ctx_arc4;
- struct crypto_shash *tfm3 = tkey->rx_tfm_michael;
struct arc4_ctx *tfm4 = &tkey->rx_ctx_arc4;
keyidx = tkey->key_idx;
memset(tkey, 0, sizeof(*tkey));
tkey->key_idx = keyidx;
- tkey->tx_tfm_michael = tfm;
tkey->tx_ctx_arc4 = *tfm2;
- tkey->rx_tfm_michael = tfm3;
tkey->rx_ctx_arc4 = *tfm4;
if (len == TKIP_KEY_LEN) {
memcpy(tkey->key, key, TKIP_KEY_LEN);
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index c148654aa953..cbaf250626c5 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -979,9 +979,10 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
struct page *page;
dma_addr_t page_dma;
unsigned long flags;
- gfp_t gfp_mask = priority;
while (1) {
+ gfp_t gfp_mask = priority;
+
spin_lock_irqsave(&rxq->lock, flags);
if (list_empty(&rxq->rx_used)) {
spin_unlock_irqrestore(&rxq->lock, flags);
@@ -1002,9 +1003,9 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
D_INFO("Failed to allocate SKB buffer.\n");
if (rxq->free_count <= RX_LOW_WATERMARK &&
net_ratelimit())
- IL_ERR("Failed to allocate SKB buffer with %0x."
+ IL_ERR("Failed to allocate SKB buffer with %pGg. "
"Only %u free buffers remaining.\n",
- priority, rxq->free_count);
+ &gfp_mask, rxq->free_count);
/* We don't reschedule replenish work here -- we will
* call the restock method and if it still needs
* more buffers it will schedule replenish */
diff --git a/drivers/net/wireless/intel/iwlegacy/3945.h b/drivers/net/wireless/intel/iwlegacy/3945.h
index fb1e33c89d0e..ed63b31fee9a 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945.h
+++ b/drivers/net/wireless/intel/iwlegacy/3945.h
@@ -123,13 +123,15 @@ enum il3945_antenna {
#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
struct il3945_frame {
+ struct list_head list;
+
+ /* Must be last as it ends in a flexible-array member. */
union {
struct ieee80211_hdr frame;
struct il3945_tx_beacon_cmd beacon;
u8 raw[IEEE80211_FRAME_LEN];
u8 cmd[360];
} u;
- struct list_head list;
};
#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
index 7921bc45e21d..18bb55682643 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -4076,7 +4076,7 @@ il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb)
u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n",
- le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK,
+ le32_to_cpu(beacon->beacon_tx_status) & TX_STATUS_MSK,
beacon->beacon_notify_hdr.failure_frame,
le32_to_cpu(beacon->ibss_mgr_status),
le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate);
diff --git a/drivers/net/wireless/intel/iwlegacy/commands.h b/drivers/net/wireless/intel/iwlegacy/commands.h
index b61b8f377702..7aba84ac88a1 100644
--- a/drivers/net/wireless/intel/iwlegacy/commands.h
+++ b/drivers/net/wireless/intel/iwlegacy/commands.h
@@ -1690,7 +1690,7 @@ struct agg_tx_status {
__le16 sequence;
} __packed;
-struct il4965_tx_resp {
+struct il4965_tx_resp_hdr {
u8 frame_count; /* 1 no aggregation, >1 aggregation */
u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */
u8 failure_rts; /* # failures due to unsuccessful RTS */
@@ -1707,6 +1707,10 @@ struct il4965_tx_resp {
__le16 reserved;
__le32 pa_power1; /* RF power amplifier measurement (not used) */
__le32 pa_power2;
+} __packed;
+
+struct il4965_tx_resp {
+ struct il4965_tx_resp_hdr;
/*
* For non-agg: frame status TX_STATUS_*
@@ -2664,7 +2668,8 @@ struct il3945_beacon_notif {
} __packed;
struct il4965_beacon_notif {
- struct il4965_tx_resp beacon_notify_hdr;
+ struct il4965_tx_resp_hdr beacon_notify_hdr;
+ __le32 beacon_tx_status;
__le32 low_tsf;
__le32 high_tsf;
__le32 ibss_mgr_status;
diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h
index 4c9836ab11dd..21f1c7702add 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.h
+++ b/drivers/net/wireless/intel/iwlegacy/common.h
@@ -518,13 +518,15 @@ struct il_channel_info {
#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
struct il_frame {
+ struct list_head list;
+
+ /* Must be last as it ends in a flexible-array member. */
union {
struct ieee80211_hdr frame;
struct il_tx_beacon_cmd beacon;
u8 raw[IEEE80211_FRAME_LEN];
u8 cmd[360];
} u;
- struct list_head list;
};
enum {
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index 77db8c75e6e2..3653ddbf3ce9 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_BZ_UCODE_CORE_MAX 101
+#define IWL_BZ_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_BZ_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index a279dcfd3083..83d893b10f8e 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -9,7 +9,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_DR_UCODE_CORE_MAX 101
+#define IWL_DR_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_DR_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index ee00b2af7a1d..749d46dc0236 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_SC_UCODE_CORE_MAX 101
+#define IWL_SC_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_SC_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index de9aef0d924c..bf0f851a9075 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) {
@@ -534,7 +535,23 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
- /* start by trying to read revision 2 */
+ /* start by trying to read revision 3 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_WRDS_WIFI_DATA_SIZE_REV3,
+ &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev != 3) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ num_chains = ACPI_SAR_NUM_CHAINS_REV2;
+ num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+
+ goto read_table;
+ }
+
+ /* then try revision 2 */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_WRDS_WIFI_DATA_SIZE_REV2,
&tbl_rev);
@@ -591,6 +608,13 @@ read_table:
goto out_free;
}
+ if (WARN_ON(num_chains * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
flags = wifi_pkg->package.elements[1].integer.value;
@@ -624,7 +648,22 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
- /* start by trying to read revision 2 */
+ /* start by trying to read revision 3 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_EWRD_WIFI_DATA_SIZE_REV3,
+ &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev != 3) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+
+ goto read_table;
+ }
+
+ /* then try revision 2 */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_EWRD_WIFI_DATA_SIZE_REV2,
&tbl_rev);
@@ -679,6 +718,13 @@ read_table:
goto out_free;
}
+ if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
enabled = !!(wifi_pkg->package.elements[1].integer.value);
n_profiles = wifi_pkg->package.elements[2].integer.value;
@@ -721,6 +767,13 @@ read_table:
if (tbl_rev < 2)
goto set_enabled;
+ if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * 2 * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
/* parse cdb chains for all profiles */
for (i = 0; i < n_profiles; i++) {
struct iwl_sar_profile_chain *chains;
@@ -760,6 +813,12 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
u8 min_profiles;
} rev_data[] = {
{
+ .revisions = BIT(4),
+ .bands = ACPI_GEO_NUM_BANDS_REV4,
+ .profiles = ACPI_NUM_GEO_PROFILES_REV3,
+ .min_profiles = BIOS_GEO_MIN_PROFILE_NUM,
+ },
+ {
.revisions = BIT(3),
.bands = ACPI_GEO_NUM_BANDS_REV2,
.profiles = ACPI_NUM_GEO_PROFILES_REV3,
@@ -812,6 +871,18 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
num_bands = rev_data[idx].bands;
num_profiles = rev_data[idx].profiles;
+ if (WARN_ON(num_profiles >
+ ARRAY_SIZE(fwrt->geo_profiles))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ if (WARN_ON(num_bands >
+ ARRAY_SIZE(fwrt->geo_profiles[0].bands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
if (rev_data[idx].min_profiles) {
/* read header that says # of profiles */
union acpi_object *entry;
@@ -851,18 +922,20 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
read_table:
fwrt->geo_rev = tbl_rev;
+
for (i = 0; i < num_profiles; i++) {
- for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {
+ struct iwl_geo_profile *prof = &fwrt->geo_profiles[i];
+
+ for (j = 0; j < ARRAY_SIZE(prof->bands); j++) {
union acpi_object *entry;
/*
- * num_bands is either 2 or 3, if it's only 2 then
- * fill the third band (6 GHz) with the values from
- * 5 GHz (second band)
+ * num_bands is either 2 or 3 or 4, if it's lower
+ * than 4, fill the third band (6 GHz) with the values
+ * from 5 GHz (second band)
*/
if (j >= num_bands) {
- fwrt->geo_profiles[i].bands[j].max =
- fwrt->geo_profiles[i].bands[1].max;
+ prof->bands[j].max = prof->bands[1].max;
} else {
entry = &wifi_pkg->package.elements[entry_idx];
entry_idx++;
@@ -872,15 +945,17 @@ read_table:
goto out_free;
}
- fwrt->geo_profiles[i].bands[j].max =
+ prof->bands[j].max =
entry->integer.value;
}
- for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {
+ for (k = 0;
+ k < ARRAY_SIZE(prof->bands[0].chains);
+ k++) {
/* same here as above */
if (j >= num_bands) {
- fwrt->geo_profiles[i].bands[j].chains[k] =
- fwrt->geo_profiles[i].bands[1].chains[k];
+ prof->bands[j].chains[k] =
+ prof->bands[1].chains[k];
} else {
entry = &wifi_pkg->package.elements[entry_idx];
entry_idx++;
@@ -890,7 +965,7 @@ read_table:
goto out_free;
}
- fwrt->geo_profiles[i].bands[j].chains[k] =
+ prof->bands[j].chains[k] =
entry->integer.value;
}
}
@@ -898,6 +973,7 @@ read_table:
}
fwrt->geo_num_profiles = num_profiles;
+ fwrt->geo_bios_source = BIOS_SOURCE_ACPI;
fwrt->geo_enabled = true;
ret = 0;
out_free:
@@ -915,6 +991,22 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
+ /* try to read ppag table rev 5 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_PPAG_WIFI_DATA_SIZE_V3, &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev == 5) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V3;
+ IWL_DEBUG_RADIO(fwrt,
+ "Reading PPAG table (tbl_rev=%d)\n",
+ tbl_rev);
+ goto read_table;
+ } else {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ }
+
/* try to read ppag table rev 1 to 4 (all have the same data size) */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
@@ -950,6 +1042,15 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
goto out_free;
read_table:
+ if (WARN_ON_ONCE(num_sub_bands >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ BUILD_BUG_ON(ACPI_PPAG_NUM_CHAINS >
+ ARRAY_SIZE(fwrt->ppag_chains));
+
fwrt->ppag_bios_rev = tbl_rev;
flags = &wifi_pkg->package.elements[1];
@@ -966,7 +1067,7 @@ read_table:
* first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
* following sub-bands to High-Band (5GHz).
*/
- for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
+ for (i = 0; i < ACPI_PPAG_NUM_CHAINS; i++) {
for (j = 0; j < num_sub_bands; j++) {
union acpi_object *ent;
@@ -980,6 +1081,7 @@ read_table:
}
}
+ iwl_bios_print_ppag(fwrt, num_sub_bands);
fwrt->ppag_bios_source = BIOS_SOURCE_ACPI;
ret = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
index 06cece4ea6d9..51a57e57de7a 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
@@ -8,11 +8,6 @@
#include <linux/acpi.h>
#include "fw/regulatory.h"
-#include "fw/api/commands.h"
-#include "fw/api/power.h"
-#include "fw/api/phy.h"
-#include "fw/api/nvm-reg.h"
-#include "fw/api/config.h"
#include "fw/img.h"
#include "iwl-trans.h"
@@ -44,6 +39,7 @@
#define ACPI_SAR_NUM_SUB_BANDS_REV0 5
#define ACPI_SAR_NUM_SUB_BANDS_REV1 11
#define ACPI_SAR_NUM_SUB_BANDS_REV2 11
+#define ACPI_SAR_NUM_SUB_BANDS_REV3 12
#define ACPI_WRDS_WIFI_DATA_SIZE_REV0 (ACPI_SAR_NUM_CHAINS_REV0 * \
ACPI_SAR_NUM_SUB_BANDS_REV0 + 2)
@@ -51,6 +47,8 @@
ACPI_SAR_NUM_SUB_BANDS_REV1 + 2)
#define ACPI_WRDS_WIFI_DATA_SIZE_REV2 (ACPI_SAR_NUM_CHAINS_REV2 * \
ACPI_SAR_NUM_SUB_BANDS_REV2 + 2)
+#define ACPI_WRDS_WIFI_DATA_SIZE_REV3 (ACPI_SAR_NUM_CHAINS_REV2 * \
+ ACPI_SAR_NUM_SUB_BANDS_REV3 + 2)
#define ACPI_EWRD_WIFI_DATA_SIZE_REV0 ((ACPI_SAR_PROFILE_NUM - 1) * \
ACPI_SAR_NUM_CHAINS_REV0 * \
ACPI_SAR_NUM_SUB_BANDS_REV0 + 3)
@@ -60,11 +58,15 @@
#define ACPI_EWRD_WIFI_DATA_SIZE_REV2 ((ACPI_SAR_PROFILE_NUM - 1) * \
ACPI_SAR_NUM_CHAINS_REV2 * \
ACPI_SAR_NUM_SUB_BANDS_REV2 + 3)
+#define ACPI_EWRD_WIFI_DATA_SIZE_REV3 ((ACPI_SAR_PROFILE_NUM - 1) * \
+ ACPI_SAR_NUM_CHAINS_REV2 * \
+ ACPI_SAR_NUM_SUB_BANDS_REV3 + 3)
#define ACPI_WPFC_WIFI_DATA_SIZE 5 /* domain and 4 filter config words */
/* revision 0 and 1 are identical, except for the semantics in the FW */
#define ACPI_GEO_NUM_BANDS_REV0 2
#define ACPI_GEO_NUM_BANDS_REV2 3
+#define ACPI_GEO_NUM_BANDS_REV4 4
#define ACPI_WRDD_WIFI_DATA_SIZE 2
#define ACPI_SPLC_WIFI_DATA_SIZE 2
@@ -96,10 +98,18 @@
*/
#define ACPI_WTAS_WIFI_DATA_SIZE (3 + IWL_WTAS_BLACK_LIST_MAX)
-#define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((IWL_NUM_CHAIN_LIMITS * \
- IWL_NUM_SUB_BANDS_V1) + 2)
-#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((IWL_NUM_CHAIN_LIMITS * \
- IWL_NUM_SUB_BANDS_V2) + 2)
+#define ACPI_PPAG_NUM_CHAINS 2
+#define ACPI_PPAG_NUM_BANDS_V1 5
+#define ACPI_PPAG_NUM_BANDS_V2 11
+#define ACPI_PPAG_NUM_BANDS_V3 12
+#define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V1) + 2)
+#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V2) + 2)
+
+/* used for ACPI PPAG table rev 5 */
+#define ACPI_PPAG_WIFI_DATA_SIZE_V3 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V3) + 2)
#define IWL_SAR_ENABLE_MSK BIT(0)
#define IWL_REDUCE_POWER_FLAGS_POS 1
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 8d64a271bb94..36159a769916 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -297,6 +297,11 @@ enum iwl_legacy_cmds {
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
/**
+ * @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start
+ */
+ SCAN_START_NOTIFICATION_UMAC = 0xb2,
+
+ /**
* @MATCH_FOUND_NOTIFICATION: scan match found
*/
MATCH_FOUND_NOTIFICATION = 0xd9,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index 6a6e11a57dbf..06370c161fe4 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -56,7 +56,8 @@ enum iwl_data_path_subcmd_ids {
RFH_QUEUE_CONFIG_CMD = 0xD,
/**
- * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4
+ * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or
+ * &struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd.
*/
TLC_MNG_CONFIG_CMD = 0xF,
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..b398c582b867 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -26,7 +26,7 @@ enum iwl_mac_conf_subcmd_ids {
*/
MISSED_VAP_NOTIF = 0xFA,
/**
- * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd
+ * @SESSION_PROTECTION_CMD: &struct iwl_session_prot_cmd
*/
SESSION_PROTECTION_CMD = 0x5,
/**
@@ -34,7 +34,8 @@ enum iwl_mac_conf_subcmd_ids {
*/
CANCEL_CHANNEL_SWITCH_CMD = 0x6,
/**
- * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd
+ * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd_v3 or
+ * &struct iwl_mac_config_cmd
*/
MAC_CONFIG_CMD = 0x8,
/**
@@ -42,7 +43,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,
/**
@@ -356,7 +358,7 @@ struct iwl_mac_wifi_gen_support {
} __packed;
/**
- * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ * struct iwl_mac_config_cmd_v3 - command structure to configure MAC contexts in
* MLD API for versions 2 and 3
* ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
*
@@ -375,7 +377,7 @@ struct iwl_mac_wifi_gen_support {
* @client: client mac data
* @p2p_dev: mac data for p2p device
*/
-struct iwl_mac_config_cmd {
+struct iwl_mac_config_cmd_v3 {
__le32 id_and_color;
__le32 action;
/* MAC_CONTEXT_TYPE_API_E */
@@ -393,7 +395,62 @@ struct iwl_mac_config_cmd {
struct iwl_mac_client_data client;
struct iwl_mac_p2p_dev_data p2p_dev;
};
-} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2, _VER_3 */
+
+/**
+ * struct iwl_mac_nan_data - NAN specific MAC data
+ * @ndi_addrs: extra NDI addresses being used
+ * @ndi_addrs_count: number of extra NDI addresses
+ */
+struct iwl_mac_nan_data {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 reserved;
+ } __packed ndi_addrs[2];
+ __le32 ndi_addrs_count;
+} __packed; /* MAC_CONTEXT_CONFIG_NAN_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ * MLD API for versions 4
+ * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, see &enum iwl_ctxt_action
+ * @mac_type: one of &enum iwl_mac_types
+ * @local_mld_addr: mld address
+ * @reserved_for_local_mld_addr: reserved
+ * @filter_flags: combination of &enum iwl_mac_config_filter_flags
+ * @wifi_gen_v2: he/eht parameters as in cmd version 2
+ * @wifi_gen: he/eht/uhr parameters as in cmd version 3
+ * @nic_not_ack_enabled: mark that the NIC doesn't support receiving
+ * ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG).
+ * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0
+ * len delim to determine if AGG or single.
+ * @client: client mac data
+ * @p2p_dev: mac data for p2p device
+ * @nan: NAN specific data (NAN data interface addresses)
+ */
+struct iwl_mac_config_cmd {
+ __le32 id_and_color;
+ __le32 action;
+ /* MAC_CONTEXT_TYPE_API_E */
+ __le32 mac_type;
+ u8 local_mld_addr[6];
+ __le16 reserved_for_local_mld_addr;
+ __le32 filter_flags;
+ union {
+ struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2;
+ struct iwl_mac_wifi_gen_support wifi_gen;
+ };
+ __le32 nic_not_ack_enabled;
+ /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_3 */
+ union {
+ struct iwl_mac_client_data client;
+ struct iwl_mac_p2p_dev_data p2p_dev;
+ struct iwl_mac_nan_data nan;
+ };
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_4 */
/**
* enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being
@@ -652,6 +709,7 @@ struct iwl_link_config_cmd {
*/
#define IWL_FW_MAX_ACTIVE_LINKS_NUM 2
#define IWL_FW_MAX_LINK_ID 3
+#define IWL_FW_MAX_LINKS IWL_FW_MAX_LINK_ID + 1
/**
* enum iwl_fw_sta_type - FW station types
@@ -662,6 +720,13 @@ struct iwl_link_config_cmd {
* @STATION_TYPE_MCAST: the station used for BCAST / MCAST in GO. Will be
* suspended / resumed at the right timing depending on the clients'
* power save state and the DTIM timing
+ * @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.)
+ * @STATION_TYPE_MAX: maximum number of FW station types
* @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.
*/
@@ -669,8 +734,11 @@ 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_MAX,
+ STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
+}; /* 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 +797,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 +837,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];
@@ -800,6 +868,83 @@ struct iwl_sta_cfg_cmd {
} __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/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
index 2a174c00b712..439a4530ec9f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
@@ -57,8 +57,7 @@ enum iwl_mac_protection_flags {
* @FW_MAC_TYPE_P2P_DEVICE: P2P Device
* @FW_MAC_TYPE_P2P_STA: P2P client
* @FW_MAC_TYPE_GO: P2P GO
- * @FW_MAC_TYPE_TEST: ?
- * @FW_MAC_TYPE_MAX: highest support MAC type
+ * @FW_MAC_TYPE_NAN: NAN (since version 4)
*/
enum iwl_mac_types {
FW_MAC_TYPE_FIRST = 1,
@@ -70,8 +69,7 @@ enum iwl_mac_types {
FW_MAC_TYPE_P2P_DEVICE,
FW_MAC_TYPE_P2P_STA,
FW_MAC_TYPE_GO,
- FW_MAC_TYPE_TEST,
- FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+ FW_MAC_TYPE_NAN,
}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
/**
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..443a9a416325 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -204,7 +204,7 @@ struct iwl_nvm_get_info_phy {
} __packed; /* REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */
#define IWL_NUM_CHANNELS_V1 51
-#define IWL_NUM_CHANNELS 110
+#define IWL_NUM_CHANNELS_V2 110
/**
* struct iwl_nvm_get_info_regulatory_v1 - regulatory information
@@ -227,7 +227,7 @@ struct iwl_nvm_get_info_regulatory_v1 {
struct iwl_nvm_get_info_regulatory {
__le32 lar_enabled;
__le32 n_channels;
- __le32 channel_profile[IWL_NUM_CHANNELS];
+ __le32 channel_profile[IWL_NUM_CHANNELS_V2];
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_2 */
/**
@@ -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/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
index 0cd8a12e0f7c..a3f916630df2 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
@@ -269,6 +269,7 @@ enum iwl_dev_tx_power_cmd_mode {
#define IWL_NUM_CHAIN_LIMITS 2
#define IWL_NUM_SUB_BANDS_V1 5
#define IWL_NUM_SUB_BANDS_V2 11
+#define IWL_NUM_SUB_BANDS_V3 12
/**
* struct iwl_dev_tx_power_common - Common part of the TX power reduction cmd
@@ -425,24 +426,38 @@ struct iwl_dev_tx_power_cmd_v10 {
__le32 flags;
} __packed; /* TX_REDUCED_POWER_API_S_VER_10 */
+struct iwl_dev_tx_power_cmd_v11 {
+ __le16 per_chain[IWL_NUM_CHAIN_TABLES_V2][IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3];
+ u8 per_chain_restriction_changed;
+ u8 reserved;
+ __le32 timer_period;
+ __le32 flags;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_11 */
+
/*
* struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion)
* @common: common part of the command
* @v9: version 9 part of the command
* @v10: version 10 part of the command
+ * @v11: version 11 part of the command
*/
struct iwl_dev_tx_power_cmd {
struct iwl_dev_tx_power_common common;
union {
struct iwl_dev_tx_power_cmd_v9 v9;
struct iwl_dev_tx_power_cmd_v10 v10;
+ struct iwl_dev_tx_power_cmd_v11 v11;
};
-} __packed; /* TX_REDUCED_POWER_API_S_VER_9_VER10 */
+} __packed; /* TX_REDUCED_POWER_API_S_VER_9
+ * TX_REDUCED_POWER_API_S_VER_10
+ * TX_REDUCED_POWER_API_S_VER_11
+ */
#define IWL_NUM_GEO_PROFILES 3
#define IWL_NUM_GEO_PROFILES_V3 8
#define IWL_NUM_BANDS_PER_CHAIN_V1 2
#define IWL_NUM_BANDS_PER_CHAIN_V2 3
+#define IWL_NUM_BANDS_PER_CHAIN_V6 4
/**
* enum iwl_geo_per_chain_offset_operation - type of operation
@@ -524,12 +539,25 @@ struct iwl_geo_tx_power_profiles_cmd_v5 {
__le32 table_revision;
} __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_5 */
+/**
+ * struct iwl_geo_tx_power_profiles_cmd_v6 - struct for PER_CHAIN_LIMIT_OFFSET_CMD cmd.
+ * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation
+ * @table: offset profile per band.
+ * @bios_hdr: describes the revision and the source of the BIOS
+ */
+struct iwl_geo_tx_power_profiles_cmd_v6 {
+ __le32 ops;
+ struct iwl_per_chain_offset table[IWL_NUM_GEO_PROFILES_V3][IWL_NUM_BANDS_PER_CHAIN_V6];
+ struct iwl_bios_config_hdr bios_hdr;
+} __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_6 */
+
union iwl_geo_tx_power_profiles_cmd {
struct iwl_geo_tx_power_profiles_cmd_v1 v1;
struct iwl_geo_tx_power_profiles_cmd_v2 v2;
struct iwl_geo_tx_power_profiles_cmd_v3 v3;
struct iwl_geo_tx_power_profiles_cmd_v4 v4;
struct iwl_geo_tx_power_profiles_cmd_v5 v5;
+ struct iwl_geo_tx_power_profiles_cmd_v6 v6;
};
/**
@@ -573,6 +601,7 @@ enum iwl_ppag_flags {
* @v1: command version 1 structure.
* @v5: command version 5 structure.
* @v7: command version 7 structure.
+ * @v8: command version 8 structure.
* @v1.flags: values from &enum iwl_ppag_flags
* @v1.gain: table of antenna gain values per chain and sub-band
* @v1.reserved: reserved
@@ -581,6 +610,8 @@ enum iwl_ppag_flags {
* @v7.ppag_config_info: see @struct bios_value_u32
* @v7.gain: table of antenna gain values per chain and sub-band
* @v7.reserved: reserved
+ * @v8.ppag_config_info: see @struct bios_value_u32
+ * @v8.gain: table of antenna gain values per chain and sub-band
*/
union iwl_ppag_table_cmd {
struct {
@@ -598,6 +629,10 @@ union iwl_ppag_table_cmd {
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
s8 reserved[2];
} __packed v7; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */
+ struct {
+ struct bios_value_u32 ppag_config_info;
+ s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3];
+ } __packed v8; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_8 */
} __packed;
#define IWL_PPAG_CMD_V1_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 60f0a4924ddf..08f4cc3ea1c3 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
@@ -1028,6 +1029,24 @@ struct iwl_scan_channel_params_v7 {
} __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
* @reserved: reserved for future
@@ -1110,6 +1129,20 @@ struct iwl_scan_req_params_v17 {
} __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
* @ooc_priority: out of channel priority - &enum iwl_scan_priority
@@ -1134,6 +1167,18 @@ struct iwl_scan_req_umac_v17 {
} __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
* @flags: reserved
@@ -1157,6 +1202,16 @@ enum iwl_umac_scan_abort_status {
};
/**
+ * struct iwl_umac_scan_start - scan start notification
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_start {
+ __le32 uid;
+ __le32 reserved;
+} __packed; /* SCAN_START_UMAC_API_S_VER_1 */
+
+/**
* struct iwl_umac_scan_complete - scan complete notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @last_schedule: last scheduling line
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
index 8d9a5058d5a5..68983f6a0026 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
@@ -598,7 +598,6 @@ struct iwl_stats_ntfy_per_sta {
} __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */
#define IWL_STATS_MAX_PHY_OPERATIONAL 3
-#define IWL_STATS_MAX_FW_LINKS (IWL_FW_MAX_LINK_ID + 1)
/**
* struct iwl_system_statistics_notif_oper - statistics notification
@@ -610,7 +609,7 @@ struct iwl_stats_ntfy_per_sta {
*/
struct iwl_system_statistics_notif_oper {
__le32 time_stamp;
- struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+ struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
@@ -624,7 +623,7 @@ struct iwl_system_statistics_notif_oper {
*/
struct iwl_system_statistics_part1_notif_oper {
__le32 time_stamp;
- struct iwl_stats_ntfy_part1_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+ struct iwl_stats_ntfy_part1_per_link per_link[IWL_FW_MAX_LINKS];
__le32 per_phy_crc_error_stats[IWL_STATS_MAX_PHY_OPERATIONAL];
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index 1f26d89fc908..0cffa5493704 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -2933,7 +2933,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n",
le32_to_cpu(desc->trig_desc.type));
- queue_delayed_work(system_unbound_wq, &wk_data->wk,
+ queue_delayed_work(system_dfl_wq, &wk_data->wk,
usecs_to_jiffies(delay));
return 0;
@@ -3236,7 +3236,7 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
if (sync)
iwl_fw_dbg_collect_sync(fwrt, idx);
else
- queue_delayed_work(system_unbound_wq,
+ queue_delayed_work(system_dfl_wq,
&fwrt->dump.wks[idx].wk,
usecs_to_jiffies(delay));
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
index ddd714cff2f4..c2af66899a78 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -128,19 +128,11 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)
IWL_ERR(fwrt, "0x%08X | %s\n", table.error_id,
iwl_fw_lookup_assert_desc(table.error_id));
- IWL_ERR(fwrt, "0x%08X | umac branchlink1\n", table.blink1);
- IWL_ERR(fwrt, "0x%08X | umac branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | umac interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | umac interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | umac data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | umac data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | umac data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | umac major\n", table.umac_major);
- IWL_ERR(fwrt, "0x%08X | umac minor\n", table.umac_minor);
- IWL_ERR(fwrt, "0x%08X | frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | stack pointer\n", table.stack_pointer);
IWL_ERR(fwrt, "0x%08X | last host cmd\n", table.cmd_header);
- IWL_ERR(fwrt, "0x%08X | isr status reg\n", table.nic_isr_pref);
}
static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_num)
@@ -200,39 +192,10 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu
IWL_ERR(fwrt, "0x%08X | %-28s\n", table.error_id,
iwl_fw_lookup_assert_desc(table.error_id));
- IWL_ERR(fwrt, "0x%08X | trm_hw_status0\n", table.trm_hw_status0);
- IWL_ERR(fwrt, "0x%08X | trm_hw_status1\n", table.trm_hw_status1);
- IWL_ERR(fwrt, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(fwrt, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(fwrt, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(fwrt, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(fwrt, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(fwrt, "0x%08X | uCode revision type\n", table.fw_rev_type);
- IWL_ERR(fwrt, "0x%08X | uCode version major\n", table.major);
- IWL_ERR(fwrt, "0x%08X | uCode version minor\n", table.minor);
- IWL_ERR(fwrt, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(fwrt, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(fwrt, "0x%08X | hcmd\n", table.hcmd);
- IWL_ERR(fwrt, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(fwrt, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(fwrt, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(fwrt, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(fwrt, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(fwrt, "0x%08X | last cmd Id\n", table.last_cmd_id);
- IWL_ERR(fwrt, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(fwrt, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(fwrt, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(fwrt, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(fwrt, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(fwrt, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(fwrt, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(fwrt, "0x%08X | flow_handler\n", table.flow_handler);
}
/*
@@ -264,7 +227,6 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
struct iwl_trans *trans = fwrt->trans;
struct iwl_tcm_error_event_table table = {};
u32 base = fwrt->trans->dbg.tcm_error_event_table[idx];
- int i;
u32 flag = idx ? IWL_ERROR_EVENT_TABLE_TCM2 :
IWL_ERROR_EVENT_TABLE_TCM1;
@@ -275,23 +237,10 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
IWL_ERR(fwrt, "TCM%d status:\n", idx + 1);
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
- IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | tcm interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | tcm interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | tcm data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | tcm data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | tcm data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | tcm log PC\n", table.logpc);
- IWL_ERR(fwrt, "0x%08X | tcm frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | tcm stack pointer\n", table.stack_pointer);
- IWL_ERR(fwrt, "0x%08X | tcm msg ID\n", table.msgid);
- IWL_ERR(fwrt, "0x%08X | tcm ISR status\n", table.isr);
- for (i = 0; i < ARRAY_SIZE(table.hw_status); i++)
- IWL_ERR(fwrt, "0x%08X | tcm HW status[%d]\n",
- table.hw_status[i], i);
- for (i = 0; i < ARRAY_SIZE(table.sw_status); i++)
- IWL_ERR(fwrt, "0x%08X | tcm SW status[%d]\n",
- table.sw_status[i], i);
}
/*
@@ -338,26 +287,10 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
IWL_ERR(fwrt, "RCM%d status:\n", idx + 1);
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
- IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | rcm interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | rcm interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | rcm data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | rcm data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | rcm data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | rcm log PC\n", table.logpc);
- IWL_ERR(fwrt, "0x%08X | rcm frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | rcm stack pointer\n", table.stack_pointer);
- IWL_ERR(fwrt, "0x%08X | rcm msg ID\n", table.msgid);
- IWL_ERR(fwrt, "0x%08X | rcm ISR status\n", table.isr);
- IWL_ERR(fwrt, "0x%08X | frame HW status\n", table.frame_hw_status);
- IWL_ERR(fwrt, "0x%08X | LMAC-to-RCM request mbox\n",
- table.mbx_lmac_to_rcm_req);
- IWL_ERR(fwrt, "0x%08X | RCM-to-LMAC request mbox\n",
- table.mbx_rcm_to_lmac_req);
- IWL_ERR(fwrt, "0x%08X | MAC header control\n", table.mh_ctl);
- IWL_ERR(fwrt, "0x%08X | MAC header addr1 low\n", table.mh_addr1_lo);
- IWL_ERR(fwrt, "0x%08X | MAC header info\n", table.mh_info);
- IWL_ERR(fwrt, "0x%08X | MAC header error\n", table.mh_err);
}
static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 378788de1d74..f7a6f21267e9 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -103,6 +103,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67,
IWL_UCODE_TLV_CURRENT_PC = 68,
IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72,
+ IWL_UCODE_TLV_CMD_BIOS_TABLE = 73,
/* contains sub-sections like PNVM file does (did) */
IWL_UCODE_TLV_PNVM_DATA = 74,
@@ -1040,6 +1041,20 @@ struct iwl_fw_cmd_version {
u8 notif_ver;
} __packed;
+/**
+ * struct iwl_fw_cmd_bios_table - firmware command BIOS revision entry
+ * @cmd: command ID
+ * @group: group ID
+ * @max_acpi_revision: max supported ACPI revision of command.
+ * @max_uefi_revision: max supported UEFI revision of command.
+ */
+struct iwl_fw_cmd_bios_table {
+ u8 cmd;
+ u8 group;
+ u8 max_acpi_revision;
+ u8 max_uefi_revision;
+} __packed;
+
struct iwl_fw_tcm_error_addr {
__le32 addr;
}; /* FW_TLV_TCM_ERROR_INFO_ADDRS_S */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.c b/drivers/net/wireless/intel/iwlwifi/fw/img.c
index c2f4fc83a22c..3cc1e3ae0858 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.c
@@ -1,11 +1,41 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2019 - 2021 Intel Corporation
- * Copyright(c) 2024 Intel Corporation
+ * Copyright(c) 2024 - 2025 Intel Corporation
*/
#include <fw/api/commands.h>
#include "img.h"
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def)
+{
+ const struct iwl_fw_cmd_bios_table *entry;
+ /* prior to LONG_GROUP, we never used this CMD version API */
+ u8 grp = iwl_cmd_groupid(cmd_id) ?: LONG_GROUP;
+ u8 cmd = iwl_cmd_opcode(cmd_id);
+
+ if (table_source != BIOS_SOURCE_ACPI &&
+ table_source != BIOS_SOURCE_UEFI)
+ return def;
+
+ if (!fw->ucode_capa.cmd_bios_tables ||
+ !fw->ucode_capa.n_cmd_bios_tables)
+ return def;
+
+ entry = fw->ucode_capa.cmd_bios_tables;
+ for (int i = 0; i < fw->ucode_capa.n_cmd_bios_tables; i++, entry++) {
+ if (entry->group == grp && entry->cmd == cmd) {
+ if (table_source == BIOS_SOURCE_ACPI)
+ return entry->max_acpi_revision;
+ return entry->max_uefi_revision;
+ }
+ }
+
+ return def;
+}
+EXPORT_SYMBOL_GPL(iwl_fw_lookup_cmd_bios_supported_revision);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def)
{
const struct iwl_fw_cmd_version *entry;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h
index 045a3e009429..94113d1db8e1 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include "api/dbg-tlv.h"
+#include "api/nvm-reg.h"
#include "file.h"
#include "error-dump.h"
@@ -57,6 +58,9 @@ struct iwl_ucode_capabilities {
const struct iwl_fw_cmd_version *cmd_versions;
u32 n_cmd_versions;
+
+ const struct iwl_fw_cmd_bios_table *cmd_bios_tables;
+ u32 n_cmd_bios_tables;
};
static inline bool
@@ -274,6 +278,10 @@ iwl_get_ucode_image(const struct iwl_fw *fw, enum iwl_ucode_type ucode_type)
return &fw->img[ucode_type];
}
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def);
u8 iwl_fw_lookup_notif_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 958e71a3c958..55128caac7ed 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;
@@ -300,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)) {
@@ -440,6 +318,27 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt)
}
IWL_EXPORT_SYMBOL(iwl_is_ppag_approved);
+/* Print the PPAG table as read from BIOS */
+void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands)
+{
+ int i, j;
+
+ IWL_DEBUG_RADIO(fwrt, "PPAG table as read from BIOS:\n");
+ IWL_DEBUG_RADIO(fwrt, "PPAG revision = %d\n", fwrt->ppag_bios_rev);
+ IWL_DEBUG_RADIO(fwrt, "PPAG flags = 0x%x\n", fwrt->ppag_flags);
+
+ if (WARN_ON_ONCE(n_subbands >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands)))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(fwrt->ppag_chains); i++)
+ for (j = 0; j < n_subbands; j++)
+ IWL_DEBUG_RADIO(fwrt,
+ "ppag_chains[%d].subbands[%d] = %d\n",
+ i, j,
+ fwrt->ppag_chains[i].subbands[j]);
+}
+
bool iwl_is_tas_approved(void)
{
return dmi_check_system(dmi_tas_approved_list);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
index 1489031687b7..6fffc032efd3 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
@@ -21,10 +21,11 @@
*/
#define BIOS_SAR_MAX_CHAINS_PER_PROFILE 4
#define BIOS_SAR_NUM_CHAINS 2
-#define BIOS_SAR_MAX_SUB_BANDS_NUM 11
+#define BIOS_SAR_MAX_SUB_BANDS_NUM 12
+#define BIOS_PPAG_MAX_SUB_BANDS_NUM 12
#define BIOS_GEO_NUM_CHAINS 2
-#define BIOS_GEO_MAX_NUM_BANDS 3
+#define BIOS_GEO_MAX_NUM_BANDS 4
#define BIOS_GEO_MAX_PROFILE_NUM 8
#define BIOS_GEO_MIN_PROFILE_NUM 3
@@ -100,7 +101,7 @@ struct iwl_geo_profile {
/* Same thing as with SAR, all revisions fit in revision 2 */
struct iwl_ppag_chain {
- s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM];
+ s8 subbands[BIOS_PPAG_MAX_SUB_BANDS_NUM];
};
struct iwl_tas_data {
@@ -180,6 +181,9 @@ enum iwl_dsm_masks_reg {
struct iwl_fw_runtime;
+/* Print the PPAG table as read from BIOS */
+void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands);
+
bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt);
int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt,
@@ -190,10 +194,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/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index ff186fb2e0da..d80ae610e56c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -106,13 +106,14 @@ 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.
* @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables
* @geo_profiles: geographic profiles as read from WGDS BIOS table
+ * @geo_bios_source: see &enum bios_source
* @phy_filters: specific phy filters as read from WPFC BIOS table
* @ppag_bios_rev: PPAG BIOS revision
* @ppag_bios_source: see &enum bios_source
@@ -204,6 +205,7 @@ struct iwl_fw_runtime {
u8 sar_chain_b_profile;
u8 reduced_power_flags;
struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
+ enum bios_source geo_bios_source;
u32 geo_rev;
u32 geo_num_profiles;
bool geo_enabled;
@@ -213,8 +215,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..2ef0a7a920ad 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,12 +429,61 @@ 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)
+ const u8 *vals, u8 prof_index,
+ u8 num_subbands, bool enabled)
{
- memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof,
- sizeof(struct uefi_sar_profile));
+ struct iwl_sar_profile *sar_prof = &fwrt->sar_profiles[prof_index];
+
+ /*
+ * Make sure fwrt has enough room to hold the data
+ * coming from the UEFI table
+ */
+ if (WARN_ON(ARRAY_SIZE(sar_prof->chains) *
+ ARRAY_SIZE(sar_prof->chains[0].subbands) <
+ UEFI_SAR_MAX_CHAINS_PER_PROFILE * num_subbands))
+ return;
+
+ BUILD_BUG_ON(ARRAY_SIZE(sar_prof->chains) !=
+ UEFI_SAR_MAX_CHAINS_PER_PROFILE);
+
+ for (int chain = 0;
+ chain < UEFI_SAR_MAX_CHAINS_PER_PROFILE;
+ chain++) {
+ for (int subband = 0; subband < num_subbands; subband++)
+ sar_prof->chains[chain].subbands[subband] =
+ vals[chain * num_subbands + subband];
+ }
fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
}
@@ -442,24 +491,46 @@ static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_wrds *data;
+ unsigned long size;
+ unsigned long expected_size;
+ int num_subbands;
int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,
- "WRDS", sizeof(*data), NULL);
+ "WRDS",
+ UEFI_SAR_WRDS_TABLE_SIZE_REV2,
+ &size);
+
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_WRDS_REVISION) {
- ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n",
+ switch (data->revision) {
+ case 2:
+ expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV2;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+ break;
+ case 3:
+ expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV3;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+ break;
+ default:
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI WRDS revision:%d\n",
data->revision);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (size != expected_size) {
+ ret = -EINVAL;
goto out;
}
/* The profile from WRDS is officially profile 1, but goes
* into sar_profiles[0] (because we don't have a profile 0).
*/
- iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode);
+ iwl_uefi_set_sar_profile(fwrt, data->vals, 0,
+ num_subbands, data->mode);
out:
kfree(data);
return ret;
@@ -468,21 +539,40 @@ out:
int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_ewrd *data;
+ unsigned long expected_size;
int i, ret = 0;
+ unsigned long size;
+ int num_subbands;
+ int profile_size;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,
- "EWRD", sizeof(*data), NULL);
+ "EWRD",
+ UEFI_SAR_EWRD_TABLE_SIZE_REV2,
+ &size);
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_EWRD_REVISION) {
- ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n",
+ switch (data->revision) {
+ case 2:
+ expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV2;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+ profile_size = UEFI_SAR_PROFILE_SIZE_REV2;
+ break;
+ case 3:
+ expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV3;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+ profile_size = UEFI_SAR_PROFILE_SIZE_REV3;
+ break;
+ default:
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI EWRD revision:%d\n",
data->revision);
+ ret = -EINVAL;
goto out;
}
- if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
+ if (size != expected_size ||
+ data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
ret = -EINVAL;
goto out;
}
@@ -492,8 +582,8 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
* save them in sar_profiles[1-3] (because we don't
* have profile 0). So in the array we start from 1.
*/
- iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1,
- data->mode);
+ iwl_uefi_set_sar_profile(fwrt, &data->vals[i * profile_size],
+ i + 1, num_subbands, data->mode);
out:
kfree(data);
@@ -503,20 +593,39 @@ out:
int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_wgds *data;
- int i, ret = 0;
+ unsigned long expected_size;
+ unsigned long size;
+ int profile_size;
+ int n_subbands;
+ int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,
- "WGDS", sizeof(*data), NULL);
+ "WGDS", UEFI_WGDS_TABLE_SIZE_REV3,
+ &size);
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_WGDS_REVISION) {
+ switch (data->revision) {
+ case 3:
+ expected_size = UEFI_WGDS_TABLE_SIZE_REV3;
+ n_subbands = UEFI_GEO_NUM_BANDS_REV3;
+ break;
+ case 4:
+ expected_size = UEFI_WGDS_TABLE_SIZE_REV4;
+ n_subbands = UEFI_GEO_NUM_BANDS_REV4;
+ break;
+ default:
ret = -EINVAL;
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",
data->revision);
goto out;
}
+ if (size != expected_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||
data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {
ret = -EINVAL;
@@ -525,10 +634,31 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
goto out;
}
+ if (WARN_ON(BIOS_GEO_MAX_PROFILE_NUM >
+ ARRAY_SIZE(fwrt->geo_profiles) ||
+ n_subbands > ARRAY_SIZE(fwrt->geo_profiles[0].bands) ||
+ BIOS_GEO_NUM_CHAINS >
+ ARRAY_SIZE(fwrt->geo_profiles[0].bands[0].chains))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
fwrt->geo_rev = data->revision;
- for (i = 0; i < data->num_profiles; i++)
- memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i],
- sizeof(struct iwl_geo_profile));
+ fwrt->geo_bios_source = BIOS_SOURCE_UEFI;
+ profile_size = 3 * n_subbands;
+ for (int prof = 0; prof < data->num_profiles; prof++) {
+ const u8 *val = &data->vals[profile_size * prof];
+ struct iwl_geo_profile *geo_prof = &fwrt->geo_profiles[prof];
+
+ for (int subband = 0; subband < n_subbands; subband++) {
+ geo_prof->bands[subband].max = *val++;
+
+ for (int chain = 0;
+ chain < BIOS_GEO_NUM_CHAINS;
+ chain++)
+ geo_prof->bands[subband].chains[chain] = *val++;
+ }
+ }
fwrt->geo_num_profiles = data->num_profiles;
fwrt->geo_enabled = true;
@@ -540,28 +670,66 @@ out:
int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_ppag *data;
+ int n_subbands;
+ u32 valid_rev;
int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
- "PPAG", sizeof(*data), NULL);
- if (IS_ERR(data))
- return -EINVAL;
+ "PPAG", UEFI_PPAG_DATA_SIZE_V5,
+ NULL);
+ if (!IS_ERR(data)) {
+ n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV5;
+ valid_rev = BIT(5);
+
+ goto parse_table;
+ }
+
+ data = iwl_uefi_get_verified_variable(fwrt->trans,
+ IWL_UEFI_PPAG_NAME,
+ "PPAG",
+ UEFI_PPAG_DATA_SIZE_V4,
+ NULL);
+ if (!IS_ERR(data)) {
+ n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV4;
+ /* revisions 1-4 have all the same size */
+ valid_rev = BIT(1) | BIT(2) | BIT(3) | BIT(4);
+
+ goto parse_table;
+ }
- if (data->revision < IWL_UEFI_MIN_PPAG_REV ||
- data->revision > IWL_UEFI_MAX_PPAG_REV) {
+ return -EINVAL;
+
+parse_table:
+ if (!(BIT(data->revision) & valid_rev)) {
ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n",
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI PPAG revision:%d\n",
data->revision);
goto out;
}
+ /*
+ * Make sure fwrt has enough room to hold
+ * data coming from the UEFI table
+ */
+ if (WARN_ON(ARRAY_SIZE(fwrt->ppag_chains) *
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands) <
+ UEFI_PPAG_NUM_CHAINS * n_subbands)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
fwrt->ppag_bios_rev = data->revision;
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));
+ for (int chain = 0; chain < UEFI_PPAG_NUM_CHAINS; chain++) {
+ for (int subband = 0; subband < n_subbands; subband++)
+ fwrt->ppag_chains[chain].subbands[subband] =
+ data->vals[chain * n_subbands + subband];
+ }
+
+ iwl_bios_print_ppag(fwrt, n_subbands);
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 349ac1505ad7..474f06db4d43 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -25,16 +25,12 @@
#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
#define IWL_UATS_MAP_SIZE 339
-#define IWL_UEFI_WRDS_REVISION 2
-#define IWL_UEFI_EWRD_REVISION 2
-#define IWL_UEFI_WGDS_REVISION 3
-#define IWL_UEFI_MIN_PPAG_REV 1
-#define IWL_UEFI_MAX_PPAG_REV 4
#define IWL_UEFI_MIN_WTAS_REVISION 1
#define IWL_UEFI_MAX_WTAS_REVISION 2
#define IWL_UEFI_SPLC_REVISION 0
@@ -63,6 +59,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;
@@ -72,68 +71,135 @@ struct uefi_cnv_common_step_data {
u8 radio2;
} __packed;
-/*
- * 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];
-} __packed;
+#define UEFI_PPAG_SUB_BANDS_NUM_REV4 11
+#define UEFI_PPAG_SUB_BANDS_NUM_REV5 12
+#define UEFI_PPAG_NUM_CHAINS 2
-/*
+#define UEFI_SAR_SUB_BANDS_NUM_REV2 11
+#define UEFI_SAR_SUB_BANDS_NUM_REV3 12
+
+#define UEFI_SAR_MAX_CHAINS_PER_PROFILE 4
+
+#define UEFI_GEO_NUM_BANDS_REV3 3
+#define UEFI_GEO_NUM_BANDS_REV4 4
+
+/**
* struct uefi_cnv_var_wrds - WRDS table as defined in UEFI
*
* @revision: the revision of the table
* @mode: is WRDS enbaled/disabled
- * @sar_profile: sar profile #1
+ * @vals: values for sar profile #1 as an array:
+ * vals[chain * num_of_subbands + subband] will return the right value.
+ * num_of_subbands depends on the revision. For revision 3, it is
+ * %UEFI_SAR_SUB_BANDS_NUM_REV3, for earlier revision, it is
+ * %UEFI_SAR_SUB_BANDS_NUM_REV2.
+ * The max number of chains is currently 2
*/
struct uefi_cnv_var_wrds {
u8 revision;
u32 mode;
- struct uefi_sar_profile sar_profile;
+ u8 vals[];
} __packed;
-/*
+#define UEFI_SAR_PROFILE_SIZE_REV2 \
+ (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \
+ UEFI_SAR_SUB_BANDS_NUM_REV2)
+
+#define UEFI_SAR_PROFILE_SIZE_REV3 \
+ (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \
+ UEFI_SAR_SUB_BANDS_NUM_REV3)
+
+#define UEFI_SAR_WRDS_TABLE_SIZE_REV2 \
+ (offsetof(struct uefi_cnv_var_wrds, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV2)
+
+#define UEFI_SAR_WRDS_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_wrds, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV3)
+
+/**
* struct uefi_cnv_var_ewrd - EWRD table as defined in UEFI
* @revision: the revision of the table
* @mode: is WRDS enbaled/disabled
* @num_profiles: how many additional profiles we have in this table (0-3)
- * @sar_profiles: the additional SAR profiles (#2-#4)
+ * @vals: the additional SAR profiles (#2-#4) as an array of SAR profiles.
+ * A SAR profile is defined the &struct uefi_cnv_var_wrds::vals. The size
+ * of each profile depends on the number of subbands which depends on the
+ * revision. This is explained in &struct uefi_cnv_var_wrds.
*/
struct uefi_cnv_var_ewrd {
u8 revision;
u32 mode;
u32 num_profiles;
- struct uefi_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM - 1];
+ u8 vals[];
} __packed;
-/*
+#define UEFI_SAR_EWRD_TABLE_SIZE_REV2 \
+ (offsetof(struct uefi_cnv_var_ewrd, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV2 * (BIOS_SAR_MAX_PROFILE_NUM - 1))
+
+#define UEFI_SAR_EWRD_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_ewrd, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV3 * (BIOS_SAR_MAX_PROFILE_NUM - 1))
+
+/**
* struct uefi_cnv_var_wgds - WGDS table as defined in UEFI
* @revision: the revision of the table
* @num_profiles: the number of geo profiles we have in the table.
* The first 3 are mandatory, and can have up to 8.
- * @geo_profiles: a per-profile table of the offsets to add to SAR values.
+ * @vals: a per-profile table of the offsets to add to SAR values. This is an
+ * array of profiles, each profile is an array of
+ * &struct iwl_geo_profile_band, one for each subband.
+ * There are %UEFI_GEO_NUM_BANDS_REV3 or %UEFI_GEO_NUM_BANDS_REV4 subbands
+ * depending on the revision.
*/
struct uefi_cnv_var_wgds {
u8 revision;
u8 num_profiles;
- struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
+ u8 vals[];
} __packed;
-/*
+/* struct iwl_geo_profile_band is 3 bytes-long, but since it is not packed,
+ * we can't use sizeof()
+ */
+#define UEFI_WGDS_PROFILE_SIZE_REV3 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV3)
+
+#define UEFI_WGDS_PROFILE_SIZE_REV4 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV4)
+
+#define UEFI_WGDS_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_wgds, vals) + \
+ UEFI_WGDS_PROFILE_SIZE_REV3 * BIOS_GEO_MAX_PROFILE_NUM)
+
+#define UEFI_WGDS_TABLE_SIZE_REV4 \
+ (offsetof(struct uefi_cnv_var_wgds, vals) + \
+ UEFI_WGDS_PROFILE_SIZE_REV4 * BIOS_GEO_MAX_PROFILE_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 depends on the revision. For revision 5, it is
+ * %UEFI_PPAG_SUB_BANDS_NUM_REV5, for earlier revision it is
+ * %UEFI_PPAG_SUB_BANDS_NUM_REV4.
+ * the max number of chains is currently 2
*/
struct uefi_cnv_var_ppag {
u8 revision;
u32 ppag_modes;
- struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
+ s8 vals[];
} __packed;
-/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
+#define UEFI_PPAG_DATA_SIZE_V4 \
+ (offsetof(struct uefi_cnv_var_ppag, vals) + \
+ sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV4)
+#define UEFI_PPAG_DATA_SIZE_V5 \
+ (offsetof(struct uefi_cnv_var_ppag, vals) + \
+ sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV5)
+
+/**
+ * struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
* @revision: the revision of the table
* @tas_selection: different options of TAS enablement.
* @black_list_size: the number of defined entried in the black list
@@ -146,7 +212,8 @@ struct uefi_cnv_var_wtas {
u16 black_list[IWL_WTAS_BLACK_LIST_MAX];
} __packed;
-/* struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI
+/**
+ * struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI
* @revision: the revision of the table
* @default_pwr_limit: The default maximum power per device
*/
@@ -155,7 +222,8 @@ struct uefi_cnv_var_splc {
u32 default_pwr_limit;
} __packed;
-/* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI
+/**
+ * struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI
* @revision: the revision of the table
* @mcc: country identifier as defined in ISO/IEC 3166-1 Alpha 2 code
*/
@@ -164,7 +232,8 @@ struct uefi_cnv_var_wrdd {
u32 mcc;
} __packed;
-/* struct uefi_cnv_var_eckv - ECKV table as defined in UEFI
+/**
+ * struct uefi_cnv_var_eckv - ECKV table as defined in UEFI
* @revision: the revision of the table
* @ext_clock_valid: indicates if external 32KHz clock is valid
*/
@@ -175,7 +244,8 @@ struct uefi_cnv_var_eckv {
#define UEFI_MAX_DSM_FUNCS 32
-/* struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI
+/**
+ * struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI
* @revision: the revision of the table
* @functions: payload of the different DSM functions
*/
@@ -185,7 +255,9 @@ struct uefi_cnv_var_general_cfg {
} __packed;
#define IWL_UEFI_WBEM_REV0_MASK (BIT(0) | BIT(1))
-/* struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined
+
+/**
+ * struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined
* in UEFI
* @revision: the revision of the table
* @wbem_320mhz_per_mcc: enablement of 320MHz bandwidth per MCC
@@ -274,6 +346,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 +447,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/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 45cf2bc68e41..5f40cd15e27f 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -85,7 +85,6 @@ enum iwl_nvm_type {
#define IWL_WATCHDOG_DISABLED 0
#define IWL_DEF_WD_TIMEOUT 2500
#define IWL_LONG_WD_TIMEOUT 10000
-#define IWL_MAX_WD_TIMEOUT 120000
#define IWL_DEFAULT_MAX_TX_POWER 22
#define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 475b3e417efa..d5ded4d3a30b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -133,6 +133,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
kfree(drv->fw.dbg.mem_tlv);
kfree(drv->fw.iml);
kfree(drv->fw.ucode_capa.cmd_versions);
+ kfree(drv->fw.ucode_capa.cmd_bios_tables);
kfree(drv->fw.phy_integration_ver);
kfree(drv->trans->dbg.pc_data);
drv->trans->dbg.pc_data = NULL;
@@ -1314,7 +1315,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
if (le32_to_cpup((const __le32 *)tlv_data) >
- IWL_FW_MAX_LINK_ID + 1) {
+ IWL_FW_MAX_LINKS) {
IWL_ERR(drv,
"%d is an invalid number of links\n",
le32_to_cpup((const __le32 *)tlv_data));
@@ -1426,6 +1427,26 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
return -ENOMEM;
drv->fw.pnvm_size = tlv_len;
break;
+ case IWL_UCODE_TLV_CMD_BIOS_TABLE:
+ if (tlv_len % sizeof(struct iwl_fw_cmd_bios_table)) {
+ IWL_ERR(drv,
+ "Invalid length for command bios table: %u\n",
+ tlv_len);
+ return -EINVAL;
+ }
+
+ if (capa->cmd_bios_tables) {
+ IWL_ERR(drv, "Duplicate TLV type 0x%02X detected\n",
+ tlv_type);
+ return -EINVAL;
+ }
+ capa->cmd_bios_tables = kmemdup(tlv_data, tlv_len,
+ GFP_KERNEL);
+ if (!capa->cmd_bios_tables)
+ return -ENOMEM;
+ capa->n_cmd_bios_tables =
+ tlv_len / sizeof(struct iwl_fw_cmd_bios_table);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 6d235c417fdd..8f3f651451bb 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -23,6 +23,8 @@
#include "fw/api/commands.h"
#include "fw/api/cmdhdr.h"
#include "fw/img.h"
+#include "fw/dbg.h"
+
#include "mei/iwl-mei.h"
/* NVM offsets (in words) definitions */
@@ -1702,6 +1704,11 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
band);
new_rule = false;
+ if (IWL_FW_CHECK(trans, !center_freq,
+ "Invalid channel %d (idx %d) in NVM\n",
+ nvm_chan[ch_idx], ch_idx))
+ continue;
+
if (!(ch_flags & NVM_CHANNEL_VALID)) {
iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
nvm_chan[ch_idx], ch_flags);
@@ -2031,7 +2038,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
if (empty_otp)
IWL_INFO(trans, "OTP is empty\n");
- nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS);
+ nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2);
if (!nvm) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index 89901786fd68..16b2c313e72b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -138,7 +138,7 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
IWL_RESET_MODE_FUNC_RESET,
IWL_RESET_MODE_PROD_RESET,
};
- static const enum iwl_reset_mode escalation_list_sc[] = {
+ static const enum iwl_reset_mode escalation_list_top[] = {
IWL_RESET_MODE_SW_RESET,
IWL_RESET_MODE_REPROBE,
IWL_RESET_MODE_REPROBE,
@@ -159,14 +159,14 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
if (trans->request_top_reset) {
trans->request_top_reset = 0;
- if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC)
+ if (iwl_trans_is_top_reset_supported(trans))
return IWL_RESET_MODE_TOP_RESET;
return IWL_RESET_MODE_PROD_RESET;
}
- if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
- escalation_list = escalation_list_sc;
- escalation_list_size = ARRAY_SIZE(escalation_list_sc);
+ if (iwl_trans_is_top_reset_supported(trans)) {
+ escalation_list = escalation_list_top;
+ escalation_list_size = ARRAY_SIZE(escalation_list_top);
} else {
escalation_list = escalation_list_old;
escalation_list_size = ARRAY_SIZE(escalation_list_old);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 688f9fee2821..61e4f4776dcb 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1088,7 +1088,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans,
*/
trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET,
&trans->status);
- queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0);
+ queue_delayed_work(system_dfl_wq, &trans->restart.wk, 0);
}
static inline void iwl_trans_fw_error(struct iwl_trans *trans,
@@ -1258,4 +1258,22 @@ bool iwl_trans_is_pm_supported(struct iwl_trans *trans);
bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans);
+static inline bool iwl_trans_is_top_reset_supported(struct iwl_trans *trans)
+{
+ /* not supported before Sc family */
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ return false;
+
+ /* for Sc family only supported for Sc2/Sc2f */
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_SC &&
+ CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_SC)
+ return false;
+
+ /* so far these numbers are increasing - not before Pe */
+ if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) < IWL_CFG_RF_TYPE_PE)
+ return false;
+
+ return true;
+}
+
#endif /* __iwl_trans_h__ */
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/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/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 29df747c8938..52b2ae496811 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -61,20 +61,27 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
struct iwl_mac_config_cmd *cmd)
{
+ u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD);
+ int len = sizeof(*cmd);
int ret;
lockdep_assert_wiphy(mld->wiphy);
- ret = iwl_mld_send_cmd_pdu(mld,
- WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
- cmd);
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) < 4) {
+ if (WARN_ON(cmd->mac_type == cpu_to_le32(FW_MAC_TYPE_NAN)))
+ return -EINVAL;
+
+ len = sizeof(struct iwl_mac_config_cmd_v3);
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len);
if (ret)
IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);
return ret;
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
+static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
{
switch (vif->type) {
case NL80211_IFTYPE_STATION:
@@ -111,14 +118,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 +196,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 +213,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,
@@ -386,7 +430,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
}
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
/* Constructor function for struct iwl_mld_vif */
static void
@@ -397,6 +441,7 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
lockdep_assert_wiphy(mld->wiphy);
mld_vif->mld = mld;
+ mld_vif->fw_id = IWL_MLD_INVALID_FW_ID;
mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
if (!mld->fw_status.in_hw_restart) {
@@ -444,6 +489,10 @@ void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
lockdep_assert_wiphy(mld->wiphy);
+ /* NAN interface type is not known to FW */
+ if (vif->type == NL80211_IFTYPE_NAN)
+ return;
+
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 62fca166afd1..8dfc79fed253 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#ifndef __iwl_mld_iface_h__
#define __iwl_mld_iface_h__
@@ -33,6 +33,7 @@ enum iwl_mld_cca_40mhz_wa_status {
* there is an indication that a non-BSS interface is to be added.
* @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile
* @IWL_MLD_EMLSR_BLOCKED_NAN: NAN is preventing EMLSR.
+ * @IWL_MLD_EMLSR_BLOCKED_TDLS: TDLS connection is preventing EMLSR.
*/
enum iwl_mld_emlsr_blocked {
IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1,
@@ -42,6 +43,7 @@ enum iwl_mld_emlsr_blocked {
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10,
IWL_MLD_EMLSR_BLOCKED_TPT = 0x20,
IWL_MLD_EMLSR_BLOCKED_NAN = 0x40,
+ IWL_MLD_EMLSR_BLOCKED_TDLS = 0x80,
};
/**
@@ -201,6 +203,15 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
}
+/* Call only for interfaces that were added to the driver! */
+static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
+{
+ if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif)))
+ return false;
+
+ return true;
+}
+
#define iwl_mld_link_dereference_check(mld_vif, link_id) \
rcu_dereference_check((mld_vif)->link[link_id], \
lockdep_is_held(&mld_vif->mld->wiphy->mtx))
@@ -219,8 +230,6 @@ iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id);
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif);
-
/* Cleanup function for struct iwl_mld_vif, will be called in restart */
void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index b5430e8a73d6..b66e84d2365f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -437,7 +437,7 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
}
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
/* Constructor function for struct iwl_mld_link */
static int
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 9e4da8e4de93..ca691259fc5e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -40,6 +40,7 @@ struct iwl_probe_resp_data {
* @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
* @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
* @mon_sta: station used for TX injection in monitor interface.
+ * @last_cqm_rssi_event: rssi of the last cqm rssi event
* @average_beacon_energy: average beacon energy for beacons received during
* client connections
* @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs,
@@ -66,6 +67,7 @@ struct iwl_mld_link {
struct iwl_mld_int_sta bcast_sta;
struct iwl_mld_int_sta mcast_sta;
struct iwl_mld_int_sta mon_sta;
+ int last_cqm_rssi_event;
/* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */
struct ieee80211_key_conf *ap_early_keys[6];
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
index d39dd36b08e3..a4ddc32e2860 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include "mld.h"
#include "iface.h"
@@ -77,9 +77,12 @@ static void iwl_mld_low_latency_iter(void *_data, u8 *mac,
bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC;
bool low_latency;
- if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result)))
+ if (!iwl_mld_vif_fw_id_valid(mld_vif))
return;
+ BUILD_BUG_ON(ARRAY_SIZE(mld->fw_id_to_vif) !=
+ ARRAY_SIZE(mld->low_latency.result));
+
low_latency = mld->low_latency.result[mld_vif->fw_id];
if (prev != low_latency)
@@ -272,8 +275,10 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,
if (WARN_ON_ONCE(!mld->low_latency.pkts_counters))
return;
- if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) ||
- queue >= mld->trans->info.num_rxqs))
+ if (!iwl_mld_vif_fw_id_valid(mld_vif))
+ return;
+
+ if (WARN_ON_ONCE(queue >= mld->trans->info.num_rxqs))
return;
if (mld->low_latency.stopped)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 0c53d6bd9651..da6fd7471568 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -754,6 +754,30 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
mld->monitor.phy.valid = false;
}
+static
+int iwl_mld_mac80211_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ enum nl80211_iftype old_type = vif->type;
+ bool old_p2p = vif->p2p;
+ int ret;
+
+ iwl_mld_mac80211_remove_interface(hw, vif);
+
+ /* set the new type for adding it cleanly */
+ vif->type = new_type;
+ vif->p2p = p2p;
+
+ ret = iwl_mld_mac80211_add_interface(hw, vif);
+
+ /* restore for mac80211, it will change it again */
+ vif->type = old_type;
+ vif->p2p = old_p2p;
+
+ return ret;
+}
+
struct iwl_mld_mc_iter_data {
struct iwl_mld *mld;
int port_id;
@@ -1124,6 +1148,8 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
/* Now activate the link */
if (iwl_mld_can_activate_link(mld, vif, link)) {
+ iwl_mld_tlc_update_phy(mld, vif, link);
+
ret = iwl_mld_activate_link(mld, link);
if (ret)
goto err;
@@ -1185,6 +1211,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
+ iwl_mld_tlc_update_phy(mld, vif, link);
+
/* in the non-MLO case, remove/re-add the link to clean up FW state.
* In MLO, it'll be done in drv_change_vif_link
*/
@@ -1727,14 +1755,23 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
return -EBUSY;
}
- ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER);
+ ret = iwl_mld_add_sta(mld, sta, vif);
if (ret)
return ret;
- /* just added first TDLS STA, so disable PM */
- if (sta->tdls && tdls_count == 0)
+ /* just added first TDLS STA, so disable PM and block EMLSR */
+ if (sta->tdls && tdls_count == 0) {
iwl_mld_update_mac_power(mld, vif, false);
+ /* TDLS requires single-link operation with
+ * direct peer communication.
+ * Block and exit EMLSR when TDLS is established.
+ */
+ iwl_mld_block_emlsr(mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TDLS,
+ iwl_mld_get_primary_link(vif));
+ }
+
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
mld_vif->ap_sta = sta;
@@ -1761,6 +1798,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);
@@ -1870,8 +1917,23 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
iwl_mld_remove_sta(mld, sta);
if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) {
- /* just removed last TDLS STA, so enable PM */
+ /* just removed last TDLS STA, so enable PM
+ * and unblock EMLSR
+ */
iwl_mld_update_mac_power(mld, vif, false);
+
+ /* Unblock EMLSR when TDLS connection is torn down */
+ iwl_mld_unblock_emlsr(mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TDLS);
+ }
+
+ 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;
@@ -2716,6 +2778,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
.get_antenna = iwl_mld_get_antenna,
.set_antenna = iwl_mld_set_antenna,
.add_interface = iwl_mld_mac80211_add_interface,
+ .change_interface = iwl_mld_mac80211_change_interface,
.remove_interface = iwl_mld_mac80211_remove_interface,
.conf_tx = iwl_mld_mac80211_conf_tx,
.prepare_multicast = iwl_mld_mac80211_prepare_multicast,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 495e9d8f3af6..9af79297c3b6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
HCMD_NAME(MAC_PM_POWER_TABLE),
HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(SCAN_START_NOTIFICATION_UMAC),
HCMD_NAME(RSS_CONFIG_CMD),
HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
HCMD_NAME(REPLY_RX_MPDU_CMD),
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index 66c7a7d31409..606cb64f8ea4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -205,7 +205,7 @@
struct iwl_mld {
/* Add here fields that need clean up on restart */
struct_group(zeroed_on_hw_restart,
- struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];
+ struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINKS];
struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
u8 used_phy_ids: NUM_PHY_CTX;
@@ -530,9 +530,9 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
#define IWL_MLD_INVALID_FW_ID 0xff
#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \
-static int \
+int \
iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
- u8 *fw_id, \
+ u8 *fw_id, \
struct ieee80211_##_mac80211_type *mac80211_ptr) \
{ \
u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index f842f5183223..8227ccb31d60 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -13,7 +13,8 @@
HOW(NON_BSS) \
HOW(TMP_NON_BSS) \
HOW(TPT) \
- HOW(NAN)
+ HOW(NAN) \
+ HOW(TDLS)
static const char *
iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked)
@@ -110,7 +111,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 | \
@@ -739,7 +739,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
/* Ignore any BSS that was not seen in the last MLO scan */
if (ktime_before(link_conf->bss->ts_boottime,
- mld->scan.last_mlo_scan_time))
+ mld->scan.last_mlo_scan_start_time))
continue;
data[n_data].link_id = link_id;
@@ -945,7 +945,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld,
if (!mld_vif->authorized || hweight16(usable_links) <= 1)
return;
- if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
+ if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time,
ktime_sub_ns(ktime_get_boottime_ns(),
5ULL * NSEC_PER_SEC)),
"Last MLO scan was too long ago, can't select links\n"))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index 2dbd3d58b0c6..4d8e85f2bd7c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -54,9 +54,8 @@ static int iwl_mld_nan_config(struct iwl_mld *mld,
ether_addr_copy(cmd.nmi_addr, vif->addr);
cmd.master_pref = conf->master_pref;
- if (conf->cluster_id)
- memcpy(cmd.cluster_id, conf->cluster_id + 4,
- sizeof(cmd.cluster_id));
+ memcpy(cmd.cluster_id, conf->cluster_id + 4,
+ sizeof(cmd.cluster_id));
cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255;
cmd.dwell_time =
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
index c9c83d1012f0..c04d77208971 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
@@ -2,7 +2,8 @@
/*
* Copyright (C) 2025 Intel Corporation
*/
-
+#ifndef __iwl_mld_nan_h__
+#define __iwl_mld_nan_h__
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
@@ -26,3 +27,5 @@ bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt,
u32 obj_id);
+
+#endif /* __iwl_mld_nan_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 240526d8b632..9c88a8579a75 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -287,6 +287,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
* at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
*/
+CMD_VERSIONS(scan_start_notif,
+ CMD_VER_ENTRY(1, iwl_umac_scan_start))
CMD_VERSIONS(scan_complete_notif,
CMD_VER_ENTRY(1, iwl_umac_scan_complete))
CMD_VERSIONS(scan_iter_complete_notif,
@@ -360,6 +362,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
link_id)
DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
+DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
mac_id)
DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
@@ -402,6 +405,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
RX_HANDLER_SYNC)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
RX_HANDLER_SYNC)
+ RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC,
+ scan_start_notif)
RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
scan_complete_notif)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
index 0deaf179f07c..6887f9feaa5c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
@@ -32,9 +32,9 @@ struct iwl_mld_phy {
};
static inline struct iwl_mld_phy *
-iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel)
+iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *chanctx)
{
- return (void *)channel->drv_priv;
+ return (void *)chanctx->drv_priv;
}
/* Cleanup function for struct iwl_mld_phy, will be called in restart */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c
index c3318e84f4a2..49b0d9f8f865 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c
@@ -405,7 +405,10 @@ int iwl_mld_set_tx_power(struct iwl_mld *mld,
.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
.common.pwr_restriction = cpu_to_le16(u_tx_power),
};
- int len = sizeof(cmd.common) + sizeof(cmd.v10);
+ int len = sizeof(cmd.common) + sizeof(cmd.v11);
+
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 10) == 10)
+ len = sizeof(cmd.common) + sizeof(cmd.v10);
if (WARN_ON(!mld_link))
return -ENODEV;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
index 231920425c06..c65f4b56a327 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
@@ -301,10 +301,12 @@ void iwl_mld_ptp_init(struct iwl_mld *mld)
mld->ptp_data.ptp_clock =
ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
- if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
+ if (IS_ERR(mld->ptp_data.ptp_clock)) {
IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
PTR_ERR(mld->ptp_data.ptp_clock));
mld->ptp_data.ptp_clock = NULL;
+ } else if (!mld->ptp_data.ptp_clock) {
+ IWL_DEBUG_INFO(mld, "PTP module unavailable on this kernel\n");
} else {
IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
mld->ptp_data.ptp_clock_info.name,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index 6ab5a3410353..659243ada86c 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);
}
@@ -72,16 +73,36 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
{
u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
/* Only set to South Korea if the table revision is 1 */
- __le32 sk = cpu_to_le32(mld->fwrt.geo_rev == 1 ? 1 : 0);
+ u8 sk = mld->fwrt.geo_rev == 1 ? 1 : 0;
union iwl_geo_tx_power_profiles_cmd cmd = {
.v5.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES),
- .v5.table_revision = sk,
};
+ u32 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
+ int n_subbands;
+ int cmd_size;
int ret;
- ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v5.table[0][0],
- ARRAY_SIZE(cmd.v5.table[0]),
- BIOS_GEO_MAX_PROFILE_NUM);
+ switch (cmd_ver) {
+ case 5:
+ n_subbands = ARRAY_SIZE(cmd.v5.table[0]);
+ cmd.v5.table_revision = cpu_to_le32(sk);
+ cmd_size = sizeof(cmd.v5);
+ break;
+ case 6:
+ n_subbands = ARRAY_SIZE(cmd.v6.table[0]);
+ cmd.v6.bios_hdr.table_revision = mld->fwrt.geo_rev;
+ cmd.v6.bios_hdr.table_source = mld->fwrt.geo_bios_source;
+ cmd_size = sizeof(cmd.v6);
+ break;
+ default:
+ WARN(false, "unsupported version: %d", cmd_ver);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(offsetof(typeof(cmd), v6.table) !=
+ offsetof(typeof(cmd), v5.table));
+ ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v6.table[0][0],
+ n_subbands, BIOS_GEO_MAX_PROFILE_NUM);
/* It is a valid scenario to not support SAR, or miss wgds table,
* but in that case there is no need to send the command.
@@ -89,28 +110,48 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
if (ret)
return 0;
- return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, sizeof(cmd.v5));
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size);
}
int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b)
{
- u32 cmd_id = REDUCE_TX_POWER_CMD;
struct iwl_dev_tx_power_cmd cmd = {
.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
- .v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags),
};
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, REDUCE_TX_POWER_CMD, 10);
+ int num_subbands;
+ int cmd_size;
int ret;
+ switch (cmd_ver) {
+ case 10:
+ cmd.v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags);
+ cmd_size = sizeof(cmd.common) + sizeof(cmd.v10);
+ num_subbands = IWL_NUM_SUB_BANDS_V2;
+ break;
+ case 11:
+ cmd.v11.flags = cpu_to_le32(mld->fwrt.reduced_power_flags);
+ cmd_size = sizeof(cmd.common) + sizeof(cmd.v11);
+ num_subbands = IWL_NUM_SUB_BANDS_V3;
+ break;
+ default:
+ WARN_ONCE(1, "Bad version for REDUCE_TX_POWER_CMD: %d\n",
+ cmd_ver);
+ return -EOPNOTSUPP;
+ }
+
/* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */
- ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v10.per_chain[0][0][0],
- IWL_NUM_CHAIN_TABLES, IWL_NUM_SUB_BANDS_V2,
+ /* v10 and v11 have the same position for per_chain */
+ BUILD_BUG_ON(offsetof(typeof(cmd), v11.per_chain) !=
+ offsetof(typeof(cmd), v10.per_chain));
+ ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v11.per_chain[0][0][0],
+ IWL_NUM_CHAIN_TABLES, num_subbands,
prof_a, prof_b);
/* return on error or if the profile is disabled (positive number) */
if (ret)
return ret;
- return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd,
- sizeof(cmd.common) + sizeof(cmd.v10));
+ return iwl_mld_send_cmd_pdu(mld, REDUCE_TX_POWER_CMD, &cmd, cmd_size);
}
int iwl_mld_init_sar(struct iwl_mld *mld)
@@ -165,30 +206,86 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
{
struct iwl_fw_runtime *fwrt = &mld->fwrt;
union iwl_ppag_table_cmd cmd = {
- .v7.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source,
- .v7.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev,
- .v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
+ /* v7 and v8 have the same layout for the ppag_config_info */
+ .v8.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source,
+ .v8.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev,
+ .v8.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
};
+ u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD);
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1);
+ int cmd_len = sizeof(cmd.v8);
+ u8 cmd_bios_rev;
int ret;
+ BUILD_BUG_ON(offsetof(typeof(cmd), v8.ppag_config_info.hdr) !=
+ offsetof(typeof(cmd), v7.ppag_config_info.hdr));
+ BUILD_BUG_ON(offsetof(typeof(cmd), v8.gain) !=
+ offsetof(typeof(cmd), v7.gain));
+
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain) > ARRAY_SIZE(fwrt->ppag_chains));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain[0]) >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain) > ARRAY_SIZE(fwrt->ppag_chains));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain[0]) >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands));
+
IWL_DEBUG_RADIO(fwrt,
"PPAG MODE bits going to be sent: %d\n",
fwrt->ppag_flags);
- for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) {
- for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) {
- cmd.v7.gain[chain][subband] =
- fwrt->ppag_chains[chain].subbands[subband];
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table: chain[%d] band[%d]: gain = %d\n",
- chain, subband, cmd.v7.gain[chain][subband]);
+ /* Since ver 7 will be deprecated at some point, don't bother making
+ * this code generic for both ver 7 and ver 8: duplicate the code.
+ */
+ if (cmd_ver == 7) {
+ for (int chain = 0; chain < ARRAY_SIZE(cmd.v7.gain); chain++) {
+ for (int subband = 0;
+ subband < ARRAY_SIZE(cmd.v7.gain[0]);
+ subband++) {
+ cmd.v7.gain[chain][subband] =
+ fwrt->ppag_chains[chain].subbands[subband];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ chain, subband,
+ cmd.v7.gain[chain][subband]);
+ }
+ }
+ cmd_len = sizeof(cmd.v7);
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 4);
+ } else if (cmd_ver == 8) {
+ for (int chain = 0; chain < ARRAY_SIZE(cmd.v8.gain); chain++) {
+ for (int subband = 0;
+ subband < ARRAY_SIZE(cmd.v8.gain[0]);
+ subband++) {
+ cmd.v8.gain[chain][subband] =
+ fwrt->ppag_chains[chain].subbands[subband];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ chain, subband,
+ cmd.v8.gain[chain][subband]);
+ }
}
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 5);
+ } else {
+ WARN(1, "Bad version for PER_PLATFORM_ANT_GAIN_CMD %d\n",
+ cmd_ver);
+ return -EINVAL;
+ }
+
+ if (cmd_bios_rev < fwrt->ppag_bios_rev) {
+ IWL_ERR(mld,
+ "BIOS revision compatibility check failed - Supported: %d, Current: %d\n",
+ cmd_bios_rev, fwrt->ppag_bios_rev);
+ return 0;
}
IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
- ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD),
- &cmd, sizeof(cmd.v7));
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len);
if (ret < 0)
IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
ret);
@@ -352,21 +449,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/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 214dcfde2fb4..a2e586c6ea67 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include <net/mac80211.h>
@@ -791,6 +791,9 @@ static void *
iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
{
struct ieee80211_radiotap_tlv *tlv;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
tlv = skb_put(skb, sizeof(*tlv));
tlv->type = cpu_to_le16(type);
@@ -1234,8 +1237,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
-
switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
case 0:
if (he_type == RATE_MCS_HE_TYPE_TRIG) {
@@ -1329,7 +1330,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
struct sk_buff *skb)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(mld->monitor.cur_aid);
@@ -1353,8 +1353,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, &mld->monitor.cur_aid,
sizeof(mld->monitor.cur_aid));
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
#endif
@@ -1362,7 +1360,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
struct sk_buff *skb,
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(*ntfy);
@@ -1382,8 +1379,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, ntfy, vendor_data_len);
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
static void
@@ -1407,6 +1402,7 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
u32 rate_n_flags = phy_data->rate_n_flags;
u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ u32 he_type = u32_get_bits(rate_n_flags, RATE_MCS_HE_TYPE_MSK);
bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
/* bandwidth may be overridden to RU by PHY ntfy */
@@ -1481,6 +1477,12 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
rx_status->encoding = RX_ENC_EHT;
iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
break;
+ case RATE_MCS_MOD_TYPE_UHR:
+ rx_status->encoding = RX_ENC_UHR;
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ if (he_type == RATE_MCS_HE_TYPE_UHR_ELR)
+ rx_status->uhr.elr = 1;
+ break;
default:
WARN_ON_ONCE(1);
}
@@ -2204,8 +2206,9 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
ret = wait_event_timeout(mld->rxq_sync.waitq,
READ_ONCE(mld->rxq_sync.state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n",
- mld->rxq_sync.state, mld->rxq_sync.cookie);
+ IWL_FW_CHECK(mld, !ret,
+ "RXQ sync failed: state=0x%lx, cookie=%d\n",
+ mld->rxq_sync.state, mld->rxq_sync.cookie);
out:
mld->rxq_sync.state = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index a1a4cf3ab3d3..b3836423e53e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -47,8 +47,6 @@
/* adaptive dwell number of APs override mask for social channels */
#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21)
-#define SCAN_TIMEOUT_MSEC (30000 * HZ)
-
/* minimal number of 2GHz and 5GHz channels in the regular scan request */
#define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4
@@ -116,6 +114,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_v8 *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;
@@ -473,6 +478,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
+ if (scan_status == IWL_MLD_SCAN_INT_MLO)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START;
+
if (params->enable_6ghz_passive)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
@@ -512,9 +520,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 +560,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 +576,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 +587,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 +670,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 +739,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 +837,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_v8 *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 +880,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_v8 *cp = scan_ptrs->channel_params;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
u32 i;
@@ -1063,25 +1082,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_v8 *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 +1107,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_v8 *cp = scan_ptrs->channel_params;
struct ieee80211_supported_band *sband =
&mld->nvm_data->bands[NL80211_BAND_6GHZ];
@@ -1107,14 +1124,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,49 +1161,148 @@ 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_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)
{
- struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
- struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params;
u32 bitmap_ssid = 0;
- int uid, ret;
+ int ret;
- memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+ iwl_mld_scan_cmd_set_gen_params(mld, params, vif, scan_ptrs,
+ scan_status);
- /* find a free UID entry */
- uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
- if (uid < 0)
- return uid;
+ ret = iwl_mld_scan_cmd_set_sched_params(params, scan_ptrs);
+ if (ret)
+ return ret;
- cmd->uid = cpu_to_le32(uid);
- cmd->ooc_priority =
- cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status));
+ 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_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
+ };
+ int ret;
+
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V4))
+ return -EINVAL;
- iwl_mld_scan_cmd_set_gen_params(mld, params, vif,
- &scan_p->general_params, scan_status);
+ cmd->uid = cpu_to_le32(uid);
+ cmd->ooc_priority = cpu_to_le32(ooc_priority);
- ret = iwl_mld_scan_cmd_set_sched_params(params,
- scan_p->periodic_params.schedule,
- &scan_p->periodic_params.delay);
+ 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_p->probe_params,
- &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_p,
- 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;
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(18),
+ 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)
@@ -1817,9 +1933,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
IWL_MLD_SCAN_INT_MLO);
- if (!ret)
- mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
-
IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
}
@@ -1904,6 +2017,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
ieee80211_sched_scan_results(mld->hw);
}
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = le32_to_cpu(notif->uid);
+
+ if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
+ "FW reports out-of-range scan UID %d\n", uid))
+ return;
+
+ if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
+ "FW reports scan UID %d we didn't trigger\n", uid))
+ return;
+
+ IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid,
+ mld->scan.uid_status[uid]);
+ if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO,
+ "FW reports scan start notification %d we didn't trigger\n",
+ mld->scan.uid_status[uid]))
+ return;
+
+ mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns();
+}
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
@@ -1942,9 +2079,7 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf = NULL;
if (fw_link_id != IWL_MLD_INVALID_FW_ID)
- link_conf =
- wiphy_dereference(mld->wiphy,
- mld->fw_id_to_bss_conf[fw_link_id]);
+ link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
/* It is possible that by the time the scan is complete the
* link was already removed and is not valid.
@@ -2031,6 +2166,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;
@@ -2041,6 +2178,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..dc299fe2f999 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld,
void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
@@ -109,13 +112,14 @@ 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.
* @last_start_time_jiffies: stores the last start time in jiffies
* (interface up/reset/resume).
- * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since
- * boot.
+ * @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds
+ * since boot.
*/
struct iwl_mld_scan {
/* Add here fields that need clean up on restart */
@@ -134,9 +138,10 @@ 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;
+ u64 last_mlo_scan_start_time;
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index 6b7a89e050e6..4c97d12ce2d0 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);
@@ -498,7 +528,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
return iwl_mld_send_sta_cmd(mld, &cmd);
}
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
static int
iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
@@ -725,14 +755,14 @@ iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+ struct ieee80211_vif *vif)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
struct ieee80211_link_sta *link_sta;
int link_id;
int ret;
- ret = iwl_mld_init_sta(mld, sta, vif, type);
+ ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
if (ret)
return ret;
@@ -908,7 +938,7 @@ static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
goto unlock;
- for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
+ for (int i = 0; i < IWL_FW_MAX_LINKS; i++)
total_mpdus += tx ? queue_counter->per_link[i].tx :
queue_counter->per_link[i].rx;
@@ -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/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index 1897b121aae2..36288c2fb38c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -89,7 +89,7 @@ struct iwl_mld_per_link_mpdu_counter {
*/
struct iwl_mld_per_q_mpdu_counter {
spinlock_t lock;
- struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1];
+ struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINKS];
unsigned long window_start_time;
} ____cacheline_aligned_in_smp;
@@ -190,7 +190,7 @@ iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta)
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type);
+ struct ieee80211_vif *vif);
void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
struct ieee80211_link_sta *link_sta);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 7b8709716324..54eb0ead78ee 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -369,15 +369,39 @@ out:
static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
struct ieee80211_bss_conf *bss_conf)
{
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
int exit_emlsr_thresh;
+ int last_event;
if (sig == 0) {
IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n");
return;
}
- /* TODO: task=statistics handle CQM notifications */
+ if (WARN_ON(!link))
+ return;
+
+ /* CQM Notification */
+ if (vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) {
+ int thold = bss_conf->cqm_rssi_thold;
+ int hyst = bss_conf->cqm_rssi_hyst;
+
+ last_event = link->last_cqm_rssi_event;
+ if (thold && sig < thold &&
+ (last_event == 0 || sig < last_event - hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig, GFP_KERNEL);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig, GFP_KERNEL);
+ }
+ }
if (!iwl_mld_emlsr_active(vif)) {
/* We're not in EMLSR and our signal is bad,
@@ -407,14 +431,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
u32 total_airtime_usec = 0;
for (u32 fw_id = 0;
- fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf);
+ fw_id < mld->fw->ucode_capa.num_links;
fw_id++) {
const struct iwl_stats_ntfy_per_link *link_stats;
struct ieee80211_bss_conf *bss_conf;
int sig;
- bss_conf = wiphy_dereference(mld->wiphy,
- mld->fw_id_to_bss_conf[fw_id]);
+ bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id);
if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION)
continue;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
index 26cf27be762d..dce747270167 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
@@ -42,7 +42,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
iwl_construct_mld(mld, trans, cfg, fw, hw, NULL);
fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
- fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1;
+ fw->ucode_capa.num_links = IWL_FW_MAX_LINKS;
mld->fwrt.trans = trans;
mld->fwrt.fw = fw;
@@ -68,7 +68,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
return 0;
}
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link,
@@ -94,7 +94,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
rcu_assign_pointer(vif->link_conf[link_id], link);
}
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
/* Helper function to add and initialize a VIF for KUnit tests */
struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
@@ -199,7 +199,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
vif->active_links |= BIT(link->link_id);
}
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
struct ieee80211_link_sta *link_sta,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c
index 897ab65b71aa..474dd555e70b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2025 Intel Corporation
+ * Copyright (C) 2025-2026 Intel Corporation
*/
#include "mld.h"
@@ -116,9 +116,9 @@ static bool iwl_mld_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)
u8 skb_dialog_token;
if (ieee80211_is_timing_measurement(skb))
- skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;
+ skb_dialog_token = mgmt->u.action.wnm_timing_msr.dialog_token;
else
- skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;
+ skb_dialog_token = mgmt->u.action.ftm.dialog_token;
if ((ether_addr_equal(mgmt->sa, addr) ||
ether_addr_equal(mgmt->da, addr)) &&
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
index 62a54c37a98c..78d6162d9297 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -9,6 +9,7 @@
#include "hcmd.h"
#include "sta.h"
#include "phy.h"
+#include "iface.h"
#include "fw/api/rs.h"
#include "fw/api/context.h"
@@ -36,7 +37,8 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
const struct ieee80211_sta_he_cap *own_he_cap,
- const struct ieee80211_sta_eht_cap *own_eht_cap)
+ const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap)
{
struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
@@ -90,6 +92,12 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
}
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap &&
+ link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX &&
+ own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK |
+ IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK;
+
return cpu_to_le16(flags);
}
@@ -406,6 +414,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_supported_band *sband,
const struct ieee80211_sta_he_cap *own_he_cap,
const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap,
struct iwl_tlc_config_cmd *cmd)
{
int i;
@@ -423,7 +432,16 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
- if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap) {
+ cmd->mode = IWL_TLC_MNG_MODE_UHR;
+ /*
+ * FIXME: spec currently inherits from EHT but has no
+ * finer MCS bits. Once that's there, need to add them
+ * to the bitmaps (and maybe copy this to UHR, or so.)
+ */
+ iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
+ own_eht_cap, cmd);
+ } else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
cmd->mode = IWL_TLC_MNG_MODE_EHT;
iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
own_eht_cap, cmd);
@@ -513,19 +531,23 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
struct ieee80211_bss_conf *link)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
enum nl80211_band band = link->chanreq.oper.chan->band;
struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
const struct ieee80211_sta_he_cap *own_he_cap =
ieee80211_get_he_iftype_cap_vif(sband, vif);
const struct ieee80211_sta_eht_cap *own_eht_cap =
ieee80211_get_eht_iftype_cap_vif(sband, vif);
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap =
+ ieee80211_get_uhr_iftype_cap_vif(sband, vif);
struct iwl_tlc_config_cmd cmd = {
/* For AP mode, use 20 MHz until the STA is authorized */
.max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
iwl_mld_fw_bw_from_sta_bw(link_sta) :
IWL_TLC_MNG_CH_WIDTH_20MHZ,
.flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
- own_he_cap, own_eht_cap),
+ own_he_cap, own_eht_cap,
+ own_uhr_cap),
.chains = iwl_mld_get_fw_chains(mld),
.sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
.max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
@@ -546,7 +568,10 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
- chan_ctx = rcu_dereference_wiphy(mld->wiphy, link->chanctx_conf);
+ if (WARN_ON_ONCE(!mld_link))
+ return;
+
+ chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
if (WARN_ON(!chan_ctx))
return;
@@ -555,7 +580,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
own_he_cap, own_eht_cap,
- &cmd);
+ own_uhr_cap, &cmd);
if (cmd_ver == 6) {
cmd_ptr = &cmd;
@@ -638,6 +663,49 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf);
}
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+ struct ieee80211_chanctx_conf *chan_ctx;
+ int link_id = link_conf->link_id;
+ struct ieee80211_sta *sta;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
+
+ for_each_station(sta, mld->hw) {
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ struct ieee80211_link_sta *link_sta;
+
+ if (mld_sta->vif != vif)
+ continue;
+
+ link_sta = link_sta_dereference_protected(sta, link_id);
+ if (!link_sta)
+ continue;
+
+ mld_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
+ link_id);
+
+ /* In recovery flow, the station may not be (yet) in the
+ * firmware, don't send a TLC command for a station the
+ * firmware does not know.
+ */
+ if (!mld_link_sta || !mld_link_sta->in_fw)
+ continue;
+
+ if (chan_ctx)
+ iwl_mld_config_tlc_link(mld, vif, link_conf, link_sta);
+ /* TODO: else, remove the TLC object in the firmware */
+ }
+}
+
void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
index c32f42e8840b..c7ff209c9ab6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
@@ -20,4 +20,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data);
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf);
+
#endif /* __iwl_mld_tlc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index a19f9d2e9346..9a74f60c9185 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2807,7 +2807,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm,
if (IS_ERR_OR_NULL(vif))
return;
- if (len < sizeof(struct iwl_scan_offload_match_info)) {
+ if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) {
IWL_ERR(mvm, "Invalid scan match info notification\n");
return;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
index ebc569e94f55..1b67836b1fac 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/etherdevice.h>
#include <linux/math64.h>
@@ -1409,8 +1409,7 @@ void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
struct iwl_mvm_loc_entry *entry;
const u8 *ies, *lci, *civic, *msr_ie;
size_t ies_len, lci_len = 0, civic_len = 0;
- size_t baselen = IEEE80211_MIN_ACTION_SIZE +
- sizeof(mgmt->u.action.u.ftm);
+ size_t baselen = IEEE80211_MIN_ACTION_SIZE(ftm);
static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;
static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;
@@ -1419,7 +1418,7 @@ void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
lockdep_assert_held(&mvm->mutex);
- ies = mgmt->u.action.u.ftm.variable;
+ ies = mgmt->u.action.ftm.variable;
ies_len = len - baselen;
msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 43cf94c9a36b..f05df3a3300e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -459,23 +459,19 @@ 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) {
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 ||
+ !mvm->trans->cfg->uhb_supported) {
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 +482,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);
@@ -906,7 +909,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
{
- union iwl_geo_tx_power_profiles_cmd geo_tx_cmd;
+ union iwl_geo_tx_power_profiles_cmd geo_tx_cmd = {};
struct iwl_geo_tx_power_profiles_resp *resp;
u16 len;
int ret;
@@ -958,7 +961,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
{
u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
- union iwl_geo_tx_power_profiles_cmd cmd;
+ union iwl_geo_tx_power_profiles_cmd cmd = {};
u16 len;
u32 n_bands;
u32 n_profiles;
@@ -1032,12 +1035,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;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 090791fe0638..1ec9807e4827 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -6229,9 +6229,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
ret = wait_event_timeout(mvm->rx_sync_waitq,
READ_ONCE(mvm->queue_sync_state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
- mvm->queue_sync_state,
- mvm->queue_sync_cookie);
+ IWL_FW_CHECK(mvm, !ret,
+ "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
+ mvm->queue_sync_state,
+ mvm->queue_sync_cookie);
}
out:
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-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
index bf54b90a7c51..b65825747b9d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
@@ -6,7 +6,7 @@
static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd,
+ struct iwl_mac_config_cmd_v3 *cmd,
int cmd_ver)
{
if (vif->type == NL80211_IFTYPE_AP) {
@@ -24,7 +24,7 @@ static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd,
+ struct iwl_mac_config_cmd_v3 *cmd,
u32 action)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -83,7 +83,7 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
}
static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
- struct iwl_mac_config_cmd *cmd)
+ struct iwl_mac_config_cmd_v3 *cmd)
{
int ret = iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
@@ -98,7 +98,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action, bool force_assoc_off)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_STATION);
@@ -151,7 +151,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
@@ -170,7 +170,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
@@ -187,7 +187,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
@@ -210,7 +210,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
u32 action)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_AP);
@@ -286,7 +286,7 @@ int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_config_cmd cmd = {
+ struct iwl_mac_config_cmd_v3 cmd = {
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
.id_and_color = cpu_to_le32(mvmvif->id),
};
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..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),
};
@@ -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/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
index ad156b82eaa9..f7b620136c85 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
@@ -304,7 +304,9 @@ void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
PTR_ERR(mvm->ptp_data.ptp_clock));
mvm->ptp_data.ptp_clock = NULL;
- } else if (mvm->ptp_data.ptp_clock) {
+ } else if (!mvm->ptp_data.ptp_clock) {
+ IWL_DEBUG_INFO(mvm, "PTP module unavailable on this kernel\n");
+ } else {
IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
mvm->ptp_data.ptp_clock_info.name,
ptp_clock_index(mvm->ptp_data.ptp_clock));
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,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
index 4945ebf19f6b..a7cd2e4ba1ae 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
@@ -234,7 +234,7 @@ void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
* Also convert TU to msec.
*/
delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
@@ -548,7 +548,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
*/
delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
return 0;
}
@@ -659,6 +659,6 @@ retry:
/* register a timeout in case we don't succeed in switching */
delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
1024 / 1000;
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c
index edae3e24192b..039b4daac73f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2022 Intel Corporation
+ * Copyright (C) 2022, 2026 Intel Corporation
*/
#include "mvm.h"
@@ -18,9 +18,9 @@ static bool iwl_mvm_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)
u8 skb_dialog_token;
if (ieee80211_is_timing_measurement(skb))
- skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;
+ skb_dialog_token = mgmt->u.action.wnm_timing_msr.dialog_token;
else
- skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;
+ skb_dialog_token = mgmt->u.action.ftm.dialog_token;
if ((ether_addr_equal(mgmt->sa, addr) ||
ether_addr_equal(mgmt->da, addr)) &&
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
index b15c5d486527..a50e845cea42 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
@@ -95,7 +95,9 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
-void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+static void
+_iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans,
+ bool dump_on_timeout)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
@@ -133,7 +135,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
"timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
inta_hw, reset_done);
- if (!reset_done) {
+ if (!reset_done && dump_on_timeout) {
struct iwl_fw_error_dump_mode mode = {
.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
.context = IWL_ERR_CONTEXT_FROM_OPMODE,
@@ -147,6 +149,11 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
trans_pcie->fw_reset_state = FW_RESET_IDLE;
}
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+{
+ _iwl_trans_pcie_fw_reset_handshake(trans, false);
+}
+
static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -163,7 +170,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
* should assume that the firmware is already dead.
*/
trans->state = IWL_TRANS_NO_FW;
- iwl_trans_pcie_fw_reset_handshake(trans);
+ _iwl_trans_pcie_fw_reset_handshake(trans, true);
}
trans_pcie->is_down = true;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
index 4560d92d76fe..a05f60f9224b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
@@ -3197,7 +3197,7 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file,
if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
return -EINVAL;
if (mode == IWL_RESET_MODE_TOP_RESET) {
- if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ if (!iwl_trans_is_top_reset_supported(trans))
return -EINVAL;
trans->request_top_reset = 1;
}