diff options
Diffstat (limited to 'drivers/net/wireless/intel')
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; } |
