diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index f1f33af0170a0..6c84cedb3f2b2 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -1105,7 +1105,7 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size) spin_lock_init(&qmi->event_lock); INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work); - ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&qmi->qmi_hdl, QMI_SERVICE_ID_WLFW, WLFW_SERVICE_VERS_V01, 0); if (ret) goto err_qmi_lookup; diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h index 9f311f3bc9e7f..88d58f78989dd 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h @@ -7,7 +7,6 @@ #ifndef WCN3990_QMI_SVC_V01_H #define WCN3990_QMI_SVC_V01_H -#define WLFW_SERVICE_ID_V01 0x45 #define WLFW_SERVICE_VERS_V01 0x01 #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 861d83b976e08..9d4f52b6f1eb7 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3305,7 +3305,7 @@ static int ath11k_mac_fils_discovery(struct ath11k_vif *arvif, if (info->fils_discovery.max_interval) { interval = info->fils_discovery.max_interval; - tmpl = ieee80211_get_fils_discovery_tmpl(ar->hw, arvif->vif); + tmpl = ieee80211_get_fils_discovery_tmpl(ar->hw, arvif->vif, 0); if (tmpl) ret = ath11k_wmi_fils_discovery_tmpl(ar, arvif->vdev_id, tmpl); @@ -3314,7 +3314,7 @@ static int ath11k_mac_fils_discovery(struct ath11k_vif *arvif, interval = info->unsol_bcast_probe_resp_interval; tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(ar->hw, - arvif->vif); + arvif->vif, 0); if (tmpl) ret = ath11k_wmi_probe_resp_tmpl(ar, arvif->vdev_id, tmpl); @@ -10480,7 +10480,7 @@ static int __ath11k_mac_register(struct ath11k *ar) if (!ab->hw_params.supports_monitor) /* There's a race between calling ieee80211_register_hw() * and here where the monitor mode is enabled for a little - * while. But that time is so short and in practise it make + * while. But that time is so short and in practice it doesn't make * a difference in real life. */ ar->hw->wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR); diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index f23d75c8ad679..4a627dc519a5c 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3342,7 +3342,7 @@ int ath11k_qmi_init_service(struct ath11k_base *ab) spin_lock_init(&ab->qmi.event_lock); INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work); - ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW, ATH11K_QMI_WLFW_SERVICE_VERS_V01, ab->qmi.service_ins_id); if (ret < 0) { diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 7968ab122b65d..eae416db8b525 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -15,7 +15,6 @@ #define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 #define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000 #define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 -#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 #define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01 diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig index 1ea1af1b8f6c5..0d5d1c55bfc13 100644 --- a/drivers/net/wireless/ath/ath12k/Kconfig +++ b/drivers/net/wireless/ath/ath12k/Kconfig @@ -1,8 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause-Clear config ATH12K - tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)" + tristate "Qualcomm Wi-Fi 7 support (ath12k)" depends on MAC80211 && HAS_DMA && PCI - select CRYPTO_MICHAEL_MIC select QCOM_QMI_HELPERS select MHI_BUS select QRTR @@ -16,10 +15,10 @@ config ATH12K If you choose to build a module, it'll be called ath12k. config ATH12K_AHB - bool "QTI ath12k AHB support" + bool "Qualcomm ath12k AHB support" depends on ATH12K && REMOTEPROC select QCOM_MDT_LOADER - select QCOM_SCM + select QCOM_PAS help Enable support for Ath12k AHB bus chipsets, example IPQ5332. @@ -34,7 +33,7 @@ config ATH12K_DEBUG you want optimal performance choose N. config ATH12K_DEBUGFS - bool "QTI ath12k debugfs support" + bool "Qualcomm ath12k debugfs support" depends on ATH12K && MAC80211_DEBUGFS help Enable ath12k debugfs support diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index 3ba1236956cc8..3b39b2c333076 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -32,6 +32,7 @@ ath12k-$(CONFIG_ATH12K_TRACING) += trace.o ath12k-$(CONFIG_PM) += wow.o ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath12k-$(CONFIG_THERMAL) += thermal.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index d81367ce69294..c1b9be3e03ac8 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 3a26fea6af1a9..dd80eebff96a5 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_ACPI_H #define ATH12K_ACPI_H diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 9a4d34e491044..07bb83710b1f1 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -5,19 +5,19 @@ */ #include -#include +#include #include #include #include #include #include #include +#include #include "ahb.h" #include "debug.h" #include "hif.h" #define ATH12K_IRQ_CE0_OFFSET 4 -#define ATH12K_MAX_UPDS 1 #define ATH12K_UPD_IRQ_WRD_LEN 18 static struct ath12k_ahb_driver *ath12k_ahb_family_drivers[ATH12K_DEVICE_FAMILY_MAX]; @@ -338,24 +338,25 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) char fw2_name[ATH12K_USERPD_FW_NAME_LEN]; struct device *dev = ab->dev; const struct firmware *fw, *fw2; - struct reserved_mem *rmem = NULL; unsigned long time_left; phys_addr_t mem_phys; + struct resource res; void *mem_region; size_t mem_size; u32 pasid; int ret; - rmem = ath12k_core_get_reserved_mem(ab, 0); - if (!rmem) - return -ENODEV; + ret = of_reserved_mem_region_to_resource_byname(dev->of_node, "q6-region", + &res); + if (ret) + return ret; - mem_phys = rmem->base; - mem_size = rmem->size; + mem_phys = res.start; + mem_size = resource_size(&res); mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC); if (IS_ERR(mem_region)) { - ath12k_err(ab, "unable to map memory region: %pa+%pa\n", - &rmem->base, &rmem->size); + ath12k_err(ab, "unable to map memory region: %pa+%zx\n", + &res.start, mem_size); return PTR_ERR(mem_region); } @@ -382,8 +383,12 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) ATH12K_AHB_UPD_SWID; /* Load FW image to a reserved memory location */ - ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size, - &mem_phys); + if (ab_ahb->scm_auth_enabled) + ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, + mem_phys, mem_size, &mem_phys); + else + ret = qcom_mdt_load_no_init(dev, fw, fw_name, mem_region, + mem_phys, mem_size, &mem_phys); if (ret) { ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); goto err_fw; @@ -414,11 +419,13 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) goto err_fw2; } - /* Authenticate FW image using peripheral ID */ - ret = qcom_scm_pas_auth_and_reset(pasid); - if (ret) { - ath12k_err(ab, "failed to boot the remote processor %d\n", ret); - goto err_fw2; + if (ab_ahb->scm_auth_enabled) { + /* Authenticate FW image using peripheral ID */ + ret = qcom_pas_auth_and_reset(pasid); + if (ret) { + ath12k_err(ab, "failed to boot the remote processor %d\n", ret); + goto err_fw2; + } } /* Instruct Q6 to spawn userPD thread */ @@ -475,13 +482,15 @@ static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend) qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0); - pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | - ATH12K_AHB_UPD_SWID; - /* Release the firmware */ - ret = qcom_scm_pas_shutdown(pasid); - if (ret) - ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n", - ab_ahb->userpd_id, ret); + if (ab_ahb->scm_auth_enabled) { + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + /* Release the firmware */ + ret = qcom_pas_shutdown(pasid); + if (ret) + ath12k_err(ab, "PAS shutdown failed for userPD%d: %d\n", + ab_ahb->userpd_id, ret); + } } static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab) @@ -575,31 +584,36 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath12k_ahb_ext_grp_napi_poll); - for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { - /* For TX ring, ensure that the ring mask and the - * tcl_to_wbm_rbm_map point to the same ring number. - */ + for (j = 0; j < DP_TCL_NUM_RING_MAX; j++) { if (ring_mask->tx[i] & - BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num)) { + BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = wbm2host_tx_completions_ring1 - j; } + } - if (ring_mask->rx[i] & BIT(j)) { + for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { + if (ring_mask->rx[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = reo2host_destination_ring1 - j; } - if (ring_mask->rx_err[i] & BIT(j)) + if (ring_mask->rx_err[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_exception; - if (ring_mask->rx_wbm_rel[i] & BIT(j)) + if (ring_mask->rx_wbm_rel[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = wbm2host_rx_release; - if (ring_mask->reo_status[i] & BIT(j)) + if (ring_mask->reo_status[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_status; - if (ring_mask->rx_mon_dest[i] & BIT(j)) + if (ring_mask->rx_mon_dest[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = rxdma2host_monitor_destination_mac1; } diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index 8a040d03d27a3..a153db6cf1d3d 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -21,13 +21,13 @@ #define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ) #define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP #define ATH12K_AHB_FW_PREFIX "q6_fw" -#define ATH12K_AHB_FW_SUFFIX ".mdt" -#define ATH12K_AHB_FW2 "iu_fw.mdt" +#define ATH12K_AHB_FW_SUFFIX ".mbn" +#define ATH12K_AHB_FW2 "iu_fw.mbn" #define ATH12K_AHB_UPD_SWID 0x12 #define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ) #define ATH12K_USERPD_READY_TIMEOUT (10 * HZ) #define ATH12K_USERPD_STOP_TIMEOUT (5 * HZ) -#define ATH12K_USERPD_ID_MASK GENMASK(9, 8) +#define ATH12K_USERPD_ID_MASK GENMASK(10, 8) #define ATH12K_USERPD_FW_NAME_LEN 35 enum ath12k_ahb_smp2p_msg_id { @@ -68,6 +68,7 @@ struct ath12k_ahb { int userpd_irq_num[ATH12K_USERPD_MAX_IRQ]; const struct ath12k_ahb_ops *ahb_ops; const struct ath12k_ahb_device_family_ops *device_family_ops; + bool scm_auth_enabled; }; struct ath12k_ahb_driver { diff --git a/drivers/net/wireless/ath/ath12k/ce.h b/drivers/net/wireless/ath/ath12k/ce.h index df4f2a4f84809..009cddf2d68d6 100644 --- a/drivers/net/wireless/ath/ath12k/ce.h +++ b/drivers/net/wireless/ath/ath12k/ce.h @@ -38,10 +38,15 @@ #define PIPEDIR_INOUT 3 /* bidirectional */ #define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ -/* CE address/mask */ -#define CE_HOST_IE_ADDRESS 0x75804C -#define CE_HOST_IE_2_ADDRESS 0x758050 -#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS +/* IPQ5332 CE address/mask */ +#define CE_HOST_IPQ5332_IE_ADDRESS 0x75804C +#define CE_HOST_IPQ5332_IE_2_ADDRESS 0x758050 +#define CE_HOST_IPQ5332_IE_3_ADDRESS CE_HOST_IPQ5332_IE_ADDRESS + +/* IPQ5424 CE address/mask */ +#define CE_HOST_IPQ5424_IE_ADDRESS 0x21804C +#define CE_HOST_IPQ5424_IE_2_ADDRESS 0x218050 +#define CE_HOST_IPQ5424_IE_3_ADDRESS CE_HOST_IPQ5424_IE_ADDRESS #define CE_HOST_IE_3_SHIFT 0xC diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 9d6c50a94e642..195fc905b30f9 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -49,7 +49,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .dp_params = { .tx_comp_ring_size = 32768, .rxdma_monitor_buf_ring_size = 4096, - .rxdma_monitor_dst_ring_size = 8092, + .rxdma_monitor_dst_ring_size = 8192, .num_pool_tx_desc = 32768, .rx_desc_count = 12288, }, @@ -637,31 +637,6 @@ u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab) } EXPORT_SYMBOL(ath12k_core_get_max_peers_per_radio); -struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, - int index) -{ - struct device *dev = ab->dev; - struct reserved_mem *rmem; - struct device_node *node; - - node = of_parse_phandle(dev->of_node, "memory-region", index); - if (!node) { - ath12k_dbg(ab, ATH12K_DBG_BOOT, - "failed to parse memory-region for index %d\n", index); - return NULL; - } - - rmem = of_reserved_mem_lookup(node); - of_node_put(node); - if (!rmem) { - ath12k_dbg(ab, ATH12K_DBG_BOOT, - "unable to get memory-region for index %d\n", index); - return NULL; - } - - return rmem; -} - static inline void ath12k_core_to_group_ref_get(struct ath12k_base *ab) { @@ -708,8 +683,10 @@ static void ath12k_core_stop(struct ath12k_base *ab) ath12k_core_to_group_ref_put(ab); - if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) { + ath12k_dp_reoq_lut_addr_reset(ath12k_ab_to_dp(ab)); ath12k_qmi_firmware_stop(ab); + } ath12k_acpi_stop(ab); @@ -835,8 +812,6 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) goto err_qmi_deinit; } - ath12k_debugfs_pdev_create(ab); - return 0; err_qmi_deinit: @@ -863,11 +838,24 @@ static int ath12k_core_pdev_create(struct ath12k_base *ab) return ret; } + ret = ath12k_thermal_register(ab); + if (ret) { + ath12k_err(ab, "could not register thermal device: %d\n", ret); + goto err_dp_pdev_free; + } + + ath12k_debugfs_pdev_create(ab); + return 0; + +err_dp_pdev_free: + ath12k_dp_pdev_free(ab); + return ret; } static void ath12k_core_pdev_destroy(struct ath12k_base *ab) { + ath12k_thermal_unregister(ab); ath12k_dp_pdev_free(ab); } @@ -995,6 +983,27 @@ static void ath12k_core_device_cleanup(struct ath12k_base *ab) mutex_unlock(&ab->core_lock); } +static int ath12k_core_device_setup(struct ath12k_base *ab) +{ + int ret; + + guard(mutex)(&ab->core_lock); + + ret = ath12k_core_pdev_create(ab); + if (ret) { + ath12k_err(ab, "failed to create pdev core %d\n", ret); + return ret; + } + + ath12k_hif_irq_enable(ab); + + ret = ath12k_core_rfkill_config(ab); + if (ret && ret != -EOPNOTSUPP) + return ret; + + return 0; +} + static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) { struct ath12k_base *ab; @@ -1004,8 +1013,6 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags); - ath12k_mac_unregister(ag); - for (i = ag->num_devices - 1; i >= 0; i--) { ab = ag->ab[i]; if (!ab) @@ -1016,6 +1023,12 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) ath12k_core_device_cleanup(ab); } + /* Unregister MAC (drops wiphys) only after per-device cleanup */ + ath12k_mac_unregister(ag); + + /* Teardown MLO state after MAC unregister for symmetry */ + ath12k_mac_mlo_teardown(ag); + ath12k_mac_destroy(ag); } @@ -1123,8 +1136,14 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag) lockdep_assert_held(&ag->mutex); - if (test_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags)) + if (test_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags)) { + ret = ath12k_core_mlo_setup(ag); + if (WARN_ON(ret)) { + ath12k_mac_unregister(ag); + goto err_mac_destroy; + } goto core_pdev_create; + } ret = ath12k_mac_allocate(ag); if (WARN_ON(ret)) @@ -1146,26 +1165,11 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag) if (!ab) continue; - mutex_lock(&ab->core_lock); - set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); - ret = ath12k_core_pdev_create(ab); - if (ret) { - ath12k_err(ab, "failed to create pdev core %d\n", ret); - mutex_unlock(&ab->core_lock); - goto err; - } - - ath12k_hif_irq_enable(ab); - - ret = ath12k_core_rfkill_config(ab); - if (ret && ret != -EOPNOTSUPP) { - mutex_unlock(&ab->core_lock); + ret = ath12k_core_device_setup(ab); + if (ret) goto err; - } - - mutex_unlock(&ab->core_lock); } return 0; @@ -1344,6 +1348,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) goto exit; err_deinit: + ath12k_dp_reoq_lut_addr_reset(ath12k_ab_to_dp(ab)); ath12k_dp_cmn_device_deinit(ath12k_ab_to_dp(ab)); mutex_unlock(&ab->core_lock); mutex_unlock(&ag->mutex); @@ -1361,6 +1366,7 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab) mutex_lock(&ab->core_lock); ath12k_link_sta_rhash_tbl_destroy(ab); + ath12k_thermal_unregister(ab); ath12k_dp_pdev_free(ab); ath12k_ce_cleanup_pipes(ab); ath12k_wmi_detach(ab); @@ -1496,12 +1502,13 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) complete_all(&ar->scan.completed); complete(&ar->scan.on_channel); complete(&ar->peer_assoc_done); - complete(&ar->peer_delete_done); + ath12k_peer_delete_wait_flush(ar); complete(&ar->install_key_done); complete(&ar->vdev_setup_done); complete(&ar->vdev_delete_done); complete(&ar->bss_survey_done); complete_all(&ar->regd_update_completed); + complete_all(&ar->thermal.wmi_sync); wake_up(&ar->dp.tx_empty_waitq); idr_for_each(&ar->txmgmt_idr, @@ -1817,10 +1824,22 @@ static struct ath12k_hw_group *ath12k_core_hw_group_alloc(struct ath12k_base *ab return ag; } +static void ath12k_core_free_wsi_info(struct ath12k_hw_group *ag) +{ + int i; + + for (i = 0; i < ag->num_devices; i++) { + of_node_put(ag->wsi_node[i]); + ag->wsi_node[i] = NULL; + } + ag->num_devices = 0; +} + static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag) { mutex_lock(&ath12k_hw_group_mutex); + ath12k_core_free_wsi_info(ag); list_del(&ag->list); kfree(ag); @@ -1846,52 +1865,59 @@ static struct ath12k_hw_group *ath12k_core_hw_group_find_by_dt(struct ath12k_bas static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag, struct ath12k_base *ab) { - struct device_node *wsi_dev = ab->dev->of_node, *next_wsi_dev; - struct device_node *tx_endpoint, *next_rx_endpoint; - int device_count = 0; - - next_wsi_dev = wsi_dev; + struct device_node *next_wsi_dev; + int device_count = 0, ret = 0; + struct device_node *wsi_dev; - if (!next_wsi_dev) + wsi_dev = of_node_get(ab->dev->of_node); + if (!wsi_dev) return -ENODEV; do { - ag->wsi_node[device_count] = next_wsi_dev; + if (device_count >= ATH12K_MAX_DEVICES) { + ath12k_warn(ab, "device count in DT %d is more than limit %d\n", + device_count, ATH12K_MAX_DEVICES); + ret = -EINVAL; + break; + } + + ag->wsi_node[device_count++] = of_node_get(wsi_dev); - tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1); + struct device_node *tx_endpoint __free(device_node) = + of_graph_get_endpoint_by_regs(wsi_dev, 0, -1); if (!tx_endpoint) { - of_node_put(next_wsi_dev); - return -ENODEV; + ret = -ENODEV; + break; } - next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint); + struct device_node *next_rx_endpoint __free(device_node) = + of_graph_get_remote_endpoint(tx_endpoint); if (!next_rx_endpoint) { - of_node_put(next_wsi_dev); - of_node_put(tx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(tx_endpoint); - of_node_put(next_wsi_dev); - next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint); if (!next_wsi_dev) { - of_node_put(next_rx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(next_rx_endpoint); + of_node_put(wsi_dev); + wsi_dev = next_wsi_dev; + } while (ab->dev->of_node != wsi_dev); - device_count++; - if (device_count > ATH12K_MAX_DEVICES) { - ath12k_warn(ab, "device count in DT %d is more than limit %d\n", - device_count, ATH12K_MAX_DEVICES); - of_node_put(next_wsi_dev); - return -EINVAL; + if (ret) { + while (--device_count >= 0) { + of_node_put(ag->wsi_node[device_count]); + ag->wsi_node[device_count] = NULL; } - } while (wsi_dev != next_wsi_dev); - of_node_put(next_wsi_dev); + of_node_put(wsi_dev); + return ret; + } + + of_node_put(wsi_dev); ag->num_devices = device_count; return 0; @@ -1962,9 +1988,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a ath12k_core_get_wsi_index(ag, ab)) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "unable to get wsi info from dt, grouping single device"); + ath12k_core_free_wsi_info(ag); ag->id = ATH12K_INVALID_GROUP_ID; ag->num_devices = 1; - memset(ag->wsi_node, 0, sizeof(ag->wsi_node)); wsi->index = 0; } @@ -2235,6 +2261,7 @@ void ath12k_core_deinit(struct ath12k_base *ab) void ath12k_core_free(struct ath12k_base *ab) { timer_delete_sync(&ab->rx_replenish_retry); + ath12k_wmi_free(); destroy_workqueue(ab->workqueue_aux); destroy_workqueue(ab->workqueue); kfree(ab); @@ -2259,6 +2286,9 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, if (!ab->workqueue_aux) goto err_free_wq; + if (ath12k_wmi_alloc() < 0) + goto err_free_wq_aux; + mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); @@ -2293,6 +2323,8 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, return ab; +err_free_wq_aux: + destroy_workqueue(ab->workqueue_aux); err_free_wq: destroy_workqueue(ab->workqueue); err_sc_free: diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index d15eb7c8a45a7..37a194e00248c 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -36,6 +36,7 @@ #include "coredump.h" #include "cmn_defs.h" #include "dp_cmn.h" +#include "thermal.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -156,6 +157,7 @@ enum ath12k_hw_rev { ATH12K_HW_WCN7850_HW20, ATH12K_HW_IPQ5332_HW10, ATH12K_HW_QCC2072_HW10, + ATH12K_HW_IPQ5424_HW10, }; enum ath12k_firmware_mode { @@ -351,6 +353,9 @@ struct ath12k_link_vif { u16 num_stations; bool is_csa_in_progress; struct wiphy_work bcn_tx_work; + + bool set_wds_vdev_param; + bool nawds_enabled; }; struct ath12k_vif { @@ -490,6 +495,10 @@ struct ath12k_link_sta { /* link address similar to ieee80211_link_sta */ u8 addr[ETH_ALEN]; + u16 tcl_metadata; + u16 ast_hash; + u16 ast_idx; + /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ u32 bw; @@ -522,9 +531,11 @@ struct ath12k_sta { u16 links_map; u8 assoc_link_id; u16 ml_peer_id; - u8 num_peer; + u16 free_logical_link_idx_map; enum ieee80211_sta_state state; + + bool enable_4addr; }; #define ATH12K_HALF_20MHZ_BW 10 @@ -587,6 +598,7 @@ struct ath12k_dbg_htt_stats { struct ath12k_debug { struct dentry *debugfs_pdev; struct dentry *debugfs_pdev_symlink; + struct dentry *debugfs_pdev_symlink_default; struct ath12k_dbg_htt_stats htt_stats; enum wmi_halphy_ctrl_path_stats_id tpc_stats_type; bool tpc_request; @@ -653,7 +665,8 @@ struct ath12k { /* protects the radio specific data like debug stats, ppdu_stats_info stats, * vdev_stop_status info, scan data, ath12k_sta info, ath12k_link_vif info, - * channel context data, survey info, test mode data, regd_channel_update_queue. + * channel context data, test mode data, regd_channel_update_queue, + * peer_delete_waits. */ spinlock_t data_lock; @@ -672,9 +685,10 @@ struct ath12k { u8 pdev_idx; u8 lmac_id; u8 hw_link_id; + u8 radio_idx; struct completion peer_assoc_done; - struct completion peer_delete_done; + struct list_head peer_delete_waits; int install_key_status; struct completion install_key_done; @@ -708,7 +722,6 @@ struct ath12k { * avoid reporting garbage data. */ bool ch_info_can_report_survey; - struct survey_info survey[ATH12K_NUM_CHANS]; struct completion bss_survey_done; struct work_struct regd_update_work; @@ -757,6 +770,16 @@ struct ath12k { s8 max_allowed_tx_power; struct ath12k_pdev_rssi_offsets rssi_info; + + struct ath12k_thermal thermal; + + /* Protected by ar->data_lock */ + struct ath12k_incumbent_signal_interference { + u32 center_freq; + enum nl80211_chan_width width; + u32 chan_bw_interference_bitmap; + bool handling_in_progress; + } incumbent_signal_interference; }; struct ath12k_hw { @@ -768,6 +791,11 @@ struct ath12k_hw { */ struct mutex hw_mutex; enum ath12k_hw_state state; + + /* protects survey[] shared across radios of this hw. */ + spinlock_t survey_lock; + struct survey_info survey[ATH12K_NUM_CHANS]; + bool regd_updated; bool use_6ghz_regd; @@ -1271,8 +1299,6 @@ void ath12k_fw_stats_init(struct ath12k *ar); void ath12k_fw_stats_bcn_free(struct list_head *head); void ath12k_fw_stats_free(struct ath12k_fw_stats *stats); void ath12k_fw_stats_reset(struct ath12k *ar); -struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, - int index); enum ath12k_qmi_mem_mode ath12k_core_get_memory_mode(struct ath12k_base *ab); static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state) @@ -1363,13 +1389,13 @@ static inline struct ath12k_hw *ath12k_hw_to_ah(struct ieee80211_hw *hw) return hw->priv; } -static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 hw_link_id) +static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 radio_idx) { - if (WARN(hw_link_id >= ah->num_radio, - "bad hw link id %d, so switch to default link\n", hw_link_id)) - hw_link_id = 0; + if (WARN(radio_idx >= ah->num_radio, + "bad radio index %d, use default radio\n", radio_idx)) + radio_idx = 0; - return &ah->radio[hw_link_id]; + return &ah->radio[radio_idx]; } static inline struct ath12k_hw *ath12k_ar_to_ah(struct ath12k *ar) diff --git a/drivers/net/wireless/ath/ath12k/coredump.c b/drivers/net/wireless/ath/ath12k/coredump.c index ce1beeb548366..070512338f138 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.c +++ b/drivers/net/wireless/ath/ath12k/coredump.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "hif.h" diff --git a/drivers/net/wireless/ath/ath12k/coredump.h b/drivers/net/wireless/ath/ath12k/coredump.h index 13f46a6051134..584ce0a085a7f 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.h +++ b/drivers/net/wireless/ath/ath12k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_COREDUMP_H_ #define _ATH12K_COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath12k/dbring.h b/drivers/net/wireless/ath/ath12k/dbring.h index e1c0eba774ec9..780c27c570e55 100644 --- a/drivers/net/wireless/ath/ath12k/dbring.h +++ b/drivers/net/wireless/ath/ath12k/dbring.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_DBRING_H diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index bf254e43a68d0..658b8083c9c3f 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUG_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 358031fa14ebb..d54995b7adb27 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1031,6 +1031,7 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, struct ath12k_device_dp_stats *device_stats = &dp->device_stats; int len = 0, i, j, ret; struct ath12k *ar; + u32 center_freq; const int size = 4096; static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { [HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR] = "Overflow", @@ -1082,6 +1083,9 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, if (!buf) return -ENOMEM; + len += scnprintf(buf + len, size - len, + "DEVICE DP STATS (timestamp: %lldms):\n\n", + ktime_to_ms(ktime_get())); len += scnprintf(buf + len, size - len, "DEVICE RX STATS:\n\n"); len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", device_stats->err_ring_pkts); @@ -1161,6 +1165,12 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, for (i = 0; i < ab->num_radios; i++) { ar = ath12k_mac_get_ar_by_pdev_id(ab, DP_SW2HW_MACID(i)); if (ar) { + spin_lock_bh(&ar->data_lock); + center_freq = ar->rx_channel ? ar->rx_channel->center_freq : 0; + spin_unlock_bh(&ar->data_lock); + len += scnprintf(buf + len, size - len, + "\nradio%d center_freq: %u\n", + i, center_freq); len += scnprintf(buf + len, size - len, "\nradio%d tx_pending: %u\n", i, atomic_read(&ar->dp.num_tx_pending)); @@ -1173,7 +1183,7 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, for (i = 0; i < DP_REO_DST_RING_MAX; i++) { len += scnprintf(buf + len, size - len, "Ring%d:", i + 1); - for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + for (j = 0; j < ab->ag->num_devices; j++) { len += scnprintf(buf + len, size - len, "\t%d:%u", j, device_stats->reo_rx[i][j]); @@ -1190,7 +1200,7 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) { len += scnprintf(buf + len, size - len, "%s:", wbm_rel_src[i]); - for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + for (j = 0; j < ab->ag->num_devices; j++) { len += scnprintf(buf + len, size - len, "\t%d:%u", j, @@ -1450,6 +1460,44 @@ static const struct file_operations fops_pdev_stats = { .llseek = default_llseek, }; +static ssize_t +ath12k_write_simulate_incumbent_signal_interference(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct wiphy *wiphy = ath12k_ar_to_hw(ar)->wiphy; + u32 chan_bw_interference_bitmap; + int ret; + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + /* + * Bitmap uses the firmware primary-based ordering documented in + * ath12k_wmi_transform_interference_bitmap() & intf_map_80. + */ + if (kstrtou32_from_user(user_buf, count, 0, &chan_bw_interference_bitmap)) + return -EINVAL; + + wiphy_lock(wiphy); + ret = ath12k_wmi_simulate_incumbent_signal_interference(ar, chan_bw_interference_bitmap); + if (ret) + goto exit; + + ret = count; + +exit: + wiphy_unlock(wiphy); + return ret; +} + +static const struct file_operations fops_simulate_incumbent_signal_interference = { + .write = ath12k_write_simulate_incumbent_signal_interference, + .open = simple_open, +}; + static void ath12k_debugfs_fw_stats_register(struct ath12k *ar) { @@ -1473,18 +1521,35 @@ void ath12k_debugfs_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; struct ieee80211_hw *hw = ar->ah->hw; - char pdev_name[5]; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct dentry *ath12k_fs; char buf[100] = {}; + char pdev_name[5]; scnprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); /* Create a symlink under ieee80211/phy* */ - scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", ar->debug.debugfs_pdev); - ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", - hw->wiphy->debugfsdir, - buf); + if (ar->radio_idx == 0) { + scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", + ar->debug.debugfs_pdev); + ath12k_fs = hw->wiphy->debugfsdir; + + /* symbolic link for compatibility */ + ar->debug.debugfs_pdev_symlink_default = debugfs_create_symlink("ath12k", + ath12k_fs, + buf); + } + + if (ah->num_radio > 1) { + scnprintf(buf, sizeof(buf), "../../../ath12k/%pd2", + ar->debug.debugfs_pdev); + ath12k_fs = hw->wiphy->radio_cfg[ar->radio_idx].radio_debugfsdir; + ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", + ath12k_fs, + buf); + } if (ar->mac.sbands[NL80211_BAND_5GHZ].channels) { debugfs_create_file("dfs_simulate_radar", 0200, @@ -1498,6 +1563,14 @@ void ath12k_debugfs_register(struct ath12k *ar) ar, &fops_tpc_stats_type); init_completion(&ar->debug.tpc_complete); + if (ar->mac.sbands[NL80211_BAND_6GHZ].channels && + test_bit(WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT, + ar->ab->wmi_ab.svc_map)) { + debugfs_create_file("simulate_incumbent_signal_interference", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_incumbent_signal_interference); + } + ath12k_debugfs_htt_stats_register(ar); ath12k_debugfs_fw_stats_register(ar); @@ -1513,7 +1586,9 @@ void ath12k_debugfs_unregister(struct ath12k *ar) /* Remove symlink under ieee80211/phy* */ debugfs_remove(ar->debug.debugfs_pdev_symlink); + debugfs_remove(ar->debug.debugfs_pdev_symlink_default); debugfs_remove_recursive(ar->debug.debugfs_pdev); ar->debug.debugfs_pdev_symlink = NULL; + ar->debug.debugfs_pdev_symlink_default = NULL; ar->debug.debugfs_pdev = NULL; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 21641a8a03460..f5dbca1b8d751 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 7f6ca07fb3359..b772181a496ec 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -5722,6 +5722,75 @@ ath12k_htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_chan_switch_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_chan_switch_stats_tlv *sbuf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 switch_freq, switch_profile; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u8 i; + + if (tag_len < sizeof(*sbuf)) + return; + + i = min(le32_to_cpu(sbuf->switch_count), ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN); + if (!i) + return; + + len += scnprintf(buf + len, buf_len - len, "Channel Change Timings:\n"); + len += scnprintf(buf + len, buf_len - len, + "|%-20s|%-21s|%-7s|%-12s|%-12s|%-15s|", + "PRIMARY CHANNEL FREQ", "BANDWIDTH CENTER FREQ", "PHYMODE", + "TX_CHAINMASK", "RX_CHAINMASK", "SWITCH TIME(us)"); + len += scnprintf(buf + len, buf_len - len, + "%-7s|%-11s|%-7s|%-8s|%-7s|%-10s|\n", + "INI(us)", "TPC+CTL(us)", "CAL(us)", "MISC(us)", "CTL(us)", + "SW PROFILE"); + + /* + * sbuf->switch_count has the number of successful channel changes. The firmware + * sends the record of channel change in such a way that sbuf->chan_stats[0] will + * point to the channel change that occurred first and the recent channel change + * records will be stored in sbuf->chan_stats[9]. As and when new channel change + * occurs, sbuf->chan_stats[0] will be replaced by records from the next index, + * sbuf->chan_stats[1]. While printing the records, reverse chronological order + * is followed, i.e., the most recent channel change records are printed first + * and the oldest one, last. + */ + while (i--) { + switch_freq = le32_to_cpu(sbuf->chan_stats[i].chan_switch_freq); + switch_profile = le32_to_cpu(sbuf->chan_stats[i].chan_switch_profile); + + len += scnprintf(buf + len, buf_len - len, + "|%20u|%21u|%7u|%12u|%12u|%15u|", + u32_get_bits(switch_freq, + ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ), + u32_get_bits(switch_freq, + ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK), + le32_to_cpu(sbuf->chan_stats[i].chan_switch_time)); + len += scnprintf(buf + len, buf_len - len, + "%7u|%11u|%7u|%8u|%7u|%10u|\n", + le32_to_cpu(sbuf->chan_stats[i].ini_module_time), + le32_to_cpu(sbuf->chan_stats[i].tpc_module_time), + le32_to_cpu(sbuf->chan_stats[i].cal_module_time), + le32_to_cpu(sbuf->chan_stats[i].misc_module_time), + le32_to_cpu(sbuf->chan_stats[i].ctl_module_time), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE)); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -6024,6 +6093,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_TX_HWQ_CMN_TAG: ath12k_htt_print_tx_hwq_stats_cmn_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_CHAN_SWITCH_STATS_TAG: + ath12k_htt_print_chan_switch_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index bfabe6500d44d..82ab7b9e4db90 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -164,6 +164,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_PDEV_MLO_IPC_STATS = 64, ATH12K_DBG_HTT_EXT_PDEV_RTT_RESP_STATS = 65, ATH12K_DBG_HTT_EXT_PDEV_RTT_INITIATOR_STATS = 66, + ATH12K_DBG_HTT_EXT_CHAN_SWITCH_STATS = 76, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -267,6 +268,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_PDEV_RTT_HW_STATS_TAG = 196, HTT_STATS_PDEV_RTT_TBR_SELFGEN_QUEUED_STATS_TAG = 197, HTT_STATS_PDEV_RTT_TBR_CMD_RESULT_STATS_TAG = 198, + HTT_STATS_CHAN_SWITCH_STATS_TAG = 213, HTT_STATS_MAX_TAG, }; @@ -2156,4 +2158,28 @@ struct htt_tx_hwq_stats_cmn_tlv { __le32 txq_timeout; } __packed; +#define ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN 10 + +#define ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ GENMASK(15, 0) +#define ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ GENMASK(31, 16) +#define ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE GENMASK(7, 0) +#define ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK GENMASK(15, 8) +#define ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK GENMASK(23, 16) +#define ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE GENMASK(31, 24) + +struct ath12k_htt_chan_switch_stats_tlv { + struct { + __le32 chan_switch_freq; + __le32 chan_switch_profile; + __le32 chan_switch_time; + __le32 cal_module_time; + __le32 ini_module_time; + __le32 tpc_module_time; + __le32 misc_module_time; + __le32 ctl_module_time; + __le32 reserved; + } chan_stats[ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN]; + __le32 switch_count; /* shows how many channel changes have occurred */ +} __packed; + #endif diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h index 8de924f4d7d52..44c78f3ab9044 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_STA_H_ diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 53ddb0d188012..107feeb07a636 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -4,7 +4,6 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#include #include "core.h" #include "dp_tx.h" #include "hif.h" @@ -41,7 +40,6 @@ void ath12k_dp_peer_cleanup(struct ath12k *ar, int vdev_id, const u8 *addr) } ath12k_dp_rx_peer_tid_cleanup(ar, peer); - crypto_free_shash(peer->dp_peer->tfm_mmic); peer->dp_peer->dp_setup_done = false; spin_unlock_bh(&dp->dp_lock); } @@ -1100,7 +1098,6 @@ static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab) return; if (dp->reoq_lut.vaddr_unaligned) { - ath12k_hal_write_reoq_lut_addr(ab, 0); dma_free_coherent(ab->dev, dp->reoq_lut.size, dp->reoq_lut.vaddr_unaligned, dp->reoq_lut.paddr_unaligned); @@ -1108,7 +1105,6 @@ static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab) } if (dp->ml_reoq_lut.vaddr_unaligned) { - ath12k_hal_write_ml_reoq_lut_addr(ab, 0); dma_free_coherent(ab->dev, dp->ml_reoq_lut.size, dp->ml_reoq_lut.vaddr_unaligned, dp->ml_reoq_lut.paddr_unaligned); @@ -1571,6 +1567,7 @@ static int ath12k_dp_setup(struct ath12k_base *ab) ath12k_dp_rx_free(ab); fail_cmn_reoq_cleanup: + ath12k_dp_reoq_lut_addr_reset(dp); ath12k_dp_reoq_lut_cleanup(ab); fail_cmn_srng_cleanup: @@ -1630,3 +1627,14 @@ void ath12k_dp_cmn_hw_group_assign(struct ath12k_dp *dp, dp->device_id = ab->device_id; dp_hw_grp->dp[dp->device_id] = dp; } + +void ath12k_dp_reoq_lut_addr_reset(struct ath12k_dp *dp) +{ + struct ath12k_base *ab = dp->ab; + + if (dp->reoq_lut.vaddr_unaligned) + ath12k_hal_write_reoq_lut_addr(ab, 0); + + if (dp->ml_reoq_lut.vaddr_unaligned) + ath12k_hal_write_ml_reoq_lut_addr(ab, 0); +} diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 6596089d41688..a94bbc337df42 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -538,7 +538,7 @@ struct ath12k_dp { /* Lock for protection of peers and rhead_peer_addr */ spinlock_t dp_lock; - struct ath12k_dp_arch_ops *ops; + const struct ath12k_dp_arch_ops *ops; /* Linked list of struct ath12k_dp_link_peer */ struct list_head peers; @@ -701,4 +701,5 @@ struct ath12k_rx_desc_info *ath12k_dp_get_rx_desc(struct ath12k_dp *dp, u32 cookie); struct ath12k_tx_desc_info *ath12k_dp_get_tx_desc(struct ath12k_dp *dp, u32 desc_id); +void ath12k_dp_reoq_lut_addr_reset(struct ath12k_dp *dp); #endif diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index cc71c5c5de5a1..8e701c8116ec0 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -146,7 +146,7 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab, } int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, - int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data), void *data) { @@ -205,16 +205,9 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev, if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) return; - if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) { + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) is_ampdu = HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); - tx_retry_failed = - __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) - - __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success); - tx_retry_count = - HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + - HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); - } if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) { @@ -223,10 +216,19 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev, HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M); tid = le32_get_bits(usr_stats->ack_ba.info, HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM); - } - if (common->fes_duration_us) - tx_duration = le32_to_cpu(common->fes_duration_us); + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) { + tx_retry_failed = + __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) - + __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success); + tx_retry_count = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + } + + if (common->fes_duration_us) + tx_duration = le32_to_cpu(common->fes_duration_us); + } user_rate = &usr_stats->rate; flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h index 6020e632f74ec..987689f11cda4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.h +++ b/drivers/net/wireless/ath/ath12k/dp_htt.h @@ -1523,7 +1523,7 @@ int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id, void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, struct sk_buff *skb); int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, - int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data), void *data); int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa462..7d5be77b081f8 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { struct ath12k *ar; @@ -495,12 +493,8 @@ EXPORT_SYMBOL(ath12k_dp_mon_update_radiotap); void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, - const struct hal_rx_mon_ppdu_info *ppduinfo, - struct ieee80211_rx_status *status, - u8 decap) + struct ieee80211_rx_status *status) { - struct ath12k_dp *dp = dp_pdev->dp; - struct ath12k_base *ab = dp->ab; static const struct ieee80211_radiotap_he known = { .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), @@ -508,14 +502,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, }; struct ieee80211_rx_status *rx_status; struct ieee80211_radiotap_he *he = NULL; - struct ieee80211_sta *pubsta = NULL; - struct ath12k_dp_link_peer *peer; - struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct hal_rx_desc_data rx_info; - bool is_mcbc = rxcb->is_mcbc; - bool is_eapol_tkip = rxcb->is_eapol; - struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data; - u8 addr[ETH_ALEN] = {}; status->link_valid = 0; @@ -526,64 +512,10 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, status->flag |= RX_FLAG_RADIOTAP_HE; } - ath12k_dp_extract_rx_desc_data(dp->hal, &rx_info, rx_desc, rx_desc); - - rcu_read_lock(); - spin_lock_bh(&dp->dp_lock); - peer = ath12k_dp_rx_h_find_link_peer(dp_pdev, msdu, &rx_info); - if (peer && peer->sta) { - pubsta = peer->sta; - memcpy(addr, peer->addr, ETH_ALEN); - if (pubsta->valid_links) { - status->link_valid = 1; - status->link_id = peer->link_id; - } - } - - spin_unlock_bh(&dp->dp_lock); - rcu_read_unlock(); - - ath12k_dbg(ab, ATH12K_DBG_DATA, - "rx skb %p len %u peer %pM %u %s %s%s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", - msdu, - msdu->len, - addr, - rxcb->tid, - (is_mcbc) ? "mcast" : "ucast", - (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", - (status->encoding == RX_ENC_HT) ? "ht" : "", - (status->encoding == RX_ENC_VHT) ? "vht" : "", - (status->encoding == RX_ENC_HE) ? "he" : "", - (status->bw == RATE_INFO_BW_40) ? "40" : "", - (status->bw == RATE_INFO_BW_80) ? "80" : "", - (status->bw == RATE_INFO_BW_160) ? "160" : "", - (status->bw == RATE_INFO_BW_320) ? "320" : "", - status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", - status->rate_idx, - status->nss, - status->freq, - status->band, status->flag, - !!(status->flag & RX_FLAG_FAILED_FCS_CRC), - !!(status->flag & RX_FLAG_MMIC_ERROR), - !!(status->flag & RX_FLAG_AMSDU_MORE)); - - ath12k_dbg_dump(ab, ATH12K_DBG_DP_RX, NULL, "dp rx msdu: ", - msdu->data, msdu->len); rx_status = IEEE80211_SKB_RXCB(msdu); *rx_status = *status; - /* TODO: trace rx packet */ - - /* PN for multicast packets are not validate in HW, - * so skip 802.3 rx path - * Also, fast_rx expects the STA to be authorized, hence - * eapol packets are sent in slow path. - */ - if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol_tkip && - !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) - rx_status->flag |= RX_FLAG_8023; - - ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi); + ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), NULL, msdu, napi); } EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu); diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index 167028d27513b..162cdcaa57a77 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -112,9 +112,7 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, - const struct hal_rx_mon_ppdu_info *ppduinfo, - struct ieee80211_rx_status *status, - u8 decap); + struct ieee80211_rx_status *status); struct sk_buff * ath12k_dp_mon_rx_merg_msdus(struct ath12k_pdev_dp *dp_pdev, struct dp_mon_mpdu *mon_mpdu, diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index bec712cc1682e..e901768498dc4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp) void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp) { - mutex_lock(&dp->link_peer_rhash_tbl_lock); + guard(mutex)(&dp->link_peer_rhash_tbl_lock); + + if (!dp->rhead_peer_addr) + return; + rhashtable_destroy(dp->rhead_peer_addr); kfree(dp->rhead_peer_addr); dp->rhead_peer_addr = NULL; - mutex_unlock(&dp->link_peer_rhash_tbl_lock); } static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp, diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 3011cf71840fb..7c9709bf717b4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -139,12 +139,13 @@ struct ath12k_dp_peer { u16 sec_type; /* Info used in MMIC verification of * RX fragments */ - struct crypto_shash *tfm_mmic; struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; unsigned long link_peers_map; struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + + bool use_4addr; }; struct ath12k_dp_link_peer * diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index f7c808995dc60..117e87d1d3e17 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4,10 +4,10 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include #include #include -#include #include "core.h" #include "debug.h" #include "hw.h" @@ -743,6 +743,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct ath12k_dp_link_peer *peer; struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta); + struct ath12k_dp_rx_tid *rx_tid; struct ath12k_link_sta *arsta; int vdev_id; bool active; @@ -778,7 +779,8 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar, return 0; } - ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, peer->dp_peer->rx_tid, + rx_tid = &peer->dp_peer->rx_tid[params->tid]; + ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid, 1, 0, false); spin_unlock_bh(&dp->dp_lock); if (ret) { @@ -1098,7 +1100,8 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev, static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + enum ath12k_dp_rx_decap_type decap_type) { struct ieee80211_hdr *hdr; struct ethhdr *eth; @@ -1106,12 +1109,24 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, u8 sa[ETH_ALEN]; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}}; + struct ath12k_dp_rx_rfc1042_hdr *llc; eth = (struct ethhdr *)msdu->data; ether_addr_copy(da, eth->h_dest); ether_addr_copy(sa, eth->h_source); - rfc.snap_type = eth->h_proto; - skb_pull(msdu, sizeof(*eth)); + if (decap_type == DP_RX_DECAP_TYPE_8023) { + /* + * For 802.3 frames, eth->h_proto carries a length field, not + * an EtherType. The actual EtherType is in the LLC/SNAP header + * that follows the Ethernet header. + */ + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + sizeof(*eth)); + rfc.snap_type = llc->snap_type; + skb_pull(msdu, sizeof(*eth) + sizeof(*llc)); + } else { + rfc.snap_type = eth->h_proto; + skb_pull(msdu, sizeof(*eth)); + } memcpy(skb_push(msdu, sizeof(rfc)), &rfc, sizeof(rfc)); ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info); @@ -1125,14 +1140,15 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, } void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, - struct hal_rx_desc *rx_desc, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer) { + enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type; struct ethhdr *ehdr; - switch (rx_info->decap_type) { + switch (decap_type) { case DP_RX_DECAP_TYPE_NATIVE_WIFI: ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info); break; @@ -1146,19 +1162,40 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu /* mac80211 allows fast path only for authorized STA */ if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) { ATH12K_SKB_RXCB(msdu)->is_eapol = true; - ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info); + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + + if (peer && !peer->use_4addr && + rx_info->is_from_ds && rx_info->is_to_ds) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); break; } /* PN for mcast packets will be validated in mac80211; * remove eth header and add 802.11 header. */ - if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) - ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info); + if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + + rx_info->rx_status->flag |= RX_FLAG_8023; break; case DP_RX_DECAP_TYPE_8023: - /* TODO: Handle undecap for these formats */ - break; + /* + * Note that ethernet decap format indicates that the decapped + * packet is either Ethernet 2 (DIX) or 802.3 (uses SNAP/LLC). + */ + if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + rx_info->rx_status->flag |= RX_FLAG_8023; } } EXPORT_SYMBOL(ath12k_dp_rx_h_undecap); @@ -1341,9 +1378,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc struct ath12k_dp_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ieee80211_rx_status *status = rx_info->rx_status; - u8 decap = rx_info->decap_type; bool is_mcbc = rxcb->is_mcbc; - bool is_eapol = rxcb->is_eapol; peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); @@ -1387,21 +1422,11 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc /* TODO: trace rx packet */ - /* PN for multicast packets are not validate in HW, - * so skip 802.3 rx path - * Also, fast_rx expects the STA to be authorized, hence - * eapol packets are sent in slow path. - */ - if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol && - !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) - rx_status->flag |= RX_FLAG_8023; - ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi); } EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu); bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_dp *dp, - struct hal_rx_desc *rx_desc, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info) { @@ -1441,29 +1466,27 @@ static void ath12k_dp_rx_frag_timer(struct timer_list *timer) int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id) { struct ath12k_base *ab = ar->ab; - struct crypto_shash *tfm; struct ath12k_dp_link_peer *peer; struct ath12k_dp_rx_tid *rx_tid; int i; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); - tfm = crypto_alloc_shash("michael_mic", 0, 0); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); + if (fips_enabled) { + ath12k_warn(ab, "This driver is disabled due to FIPS\n"); + return -ENOENT; + } spin_lock_bh(&dp->dp_lock); peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, peer_mac); if (!peer || !peer->dp_peer) { spin_unlock_bh(&dp->dp_lock); - crypto_free_shash(tfm); ath12k_warn(ab, "failed to find the peer to set up fragment info\n"); return -ENOENT; } if (!peer->primary_link) { spin_unlock_bh(&dp->dp_lock); - crypto_free_shash(tfm); return 0; } @@ -1474,55 +1497,12 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev skb_queue_head_init(&rx_tid->rx_frags); } - peer->dp_peer->tfm_mmic = tfm; peer->dp_peer->dp_setup_done = true; spin_unlock_bh(&dp->dp_lock); return 0; } -int ath12k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, - struct ieee80211_hdr *hdr, u8 *data, - size_t data_len, u8 *mic) -{ - SHASH_DESC_ON_STACK(desc, tfm); - u8 mic_hdr[16] = {}; - u8 tid = 0; - int ret; - - if (!tfm) - return -EINVAL; - - desc->tfm = tfm; - - ret = crypto_shash_setkey(tfm, key, 8); - if (ret) - goto out; - - ret = crypto_shash_init(desc); - if (ret) - goto out; - - /* TKIP MIC header */ - memcpy(mic_hdr, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(mic_hdr + ETH_ALEN, ieee80211_get_SA(hdr), ETH_ALEN); - if (ieee80211_is_data_qos(hdr->frame_control)) - tid = ieee80211_get_tid(hdr); - mic_hdr[12] = tid; - - ret = crypto_shash_update(desc, mic_hdr, 16); - if (ret) - goto out; - ret = crypto_shash_update(desc, data, data_len); - if (ret) - goto out; - ret = crypto_shash_final(desc, mic); -out: - shash_desc_zero(desc); - return ret; -} -EXPORT_SYMBOL(ath12k_dp_rx_h_michael_mic); - void ath12k_dp_rx_h_undecap_frag(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, u32 flags) { diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 1ec5382f59955..0660db070e921 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -6,7 +6,6 @@ #ifndef ATH12K_DP_RX_H #define ATH12K_DP_RX_H -#include #include "core.h" #include "debug.h" @@ -189,15 +188,14 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal, } void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, - struct hal_rx_desc *rx_desc, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info); + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer); void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info); bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_dp *dp, - struct hal_rx_desc *rx_desc, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info); u64 ath12k_dp_rx_h_get_pn(struct ath12k_dp *dp, struct sk_buff *skb); @@ -206,9 +204,6 @@ void ath12k_dp_rx_h_sort_frags(struct ath12k_hal *hal, struct sk_buff *cur_frag); void ath12k_dp_rx_h_undecap_frag(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, u32 flags); -int ath12k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, - struct ieee80211_hdr *hdr, u8 *data, - size_t data_len, u8 *mic); int ath12k_dp_rx_ampdu_start(struct ath12k *ar, struct ieee80211_ampdu_params *params, u8 link_id); diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index c104ab3a60567..3a874db7968e7 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -268,21 +268,28 @@ enum hal_rx_reception_type { }; enum hal_rx_legacy_rate { - HAL_RX_LEGACY_RATE_1_MBPS, - HAL_RX_LEGACY_RATE_2_MBPS, - HAL_RX_LEGACY_RATE_5_5_MBPS, - HAL_RX_LEGACY_RATE_6_MBPS, - HAL_RX_LEGACY_RATE_9_MBPS, - HAL_RX_LEGACY_RATE_11_MBPS, - HAL_RX_LEGACY_RATE_12_MBPS, - HAL_RX_LEGACY_RATE_18_MBPS, - HAL_RX_LEGACY_RATE_24_MBPS, - HAL_RX_LEGACY_RATE_36_MBPS, - HAL_RX_LEGACY_RATE_48_MBPS, - HAL_RX_LEGACY_RATE_54_MBPS, + HAL_RX_LEGACY_RATE_LP_1_MBPS, + HAL_RX_LEGACY_RATE_LP_2_MBPS, + HAL_RX_LEGACY_RATE_LP_5_5_MBPS, + HAL_RX_LEGACY_RATE_LP_11_MBPS, + HAL_RX_LEGACY_RATE_SP_2_MBPS, + HAL_RX_LEGACY_RATE_SP_5_5_MBPS, + HAL_RX_LEGACY_RATE_SP_11_MBPS, HAL_RX_LEGACY_RATE_INVALID, }; +enum hal_rx_legacy_rates_ofdm { + HAL_RX_LEGACY_RATE_OFDM_48_MBPS, + HAL_RX_LEGACY_RATE_OFDM_24_MBPS, + HAL_RX_LEGACY_RATE_OFDM_12_MBPS, + HAL_RX_LEGACY_RATE_OFDM_6_MBPS, + HAL_RX_LEGACY_RATE_OFDM_54_MBPS, + HAL_RX_LEGACY_RATE_OFDM_36_MBPS, + HAL_RX_LEGACY_RATE_OFDM_18_MBPS, + HAL_RX_LEGACY_RATE_OFDM_9_MBPS, + HAL_RX_LEGACY_RATE_OFDM_INVALID, +}; + enum hal_ring_type { HAL_REO_DST, HAL_REO_EXCEPTION, @@ -731,7 +738,9 @@ struct hal_rx_desc_data { addr2_present:1, is_mcbc:1, seq_ctl_valid:1, - fc_valid:1; + fc_valid:1, + is_to_ds:1, + is_from_ds:1; u16 msdu_len; u16 peer_id; u16 seq_no; @@ -1015,7 +1024,7 @@ enum hal_wbm_rel_desc_type { /* Interrupt mitigation - timer threshold in us */ #define HAL_SRNG_INT_TIMER_THRESHOLD_TX 1000 -#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 500 +#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 200 #define HAL_SRNG_INT_TIMER_THRESHOLD_OTHER 256 enum hal_srng_mac_type { diff --git a/drivers/net/wireless/ath/ath12k/hif.h b/drivers/net/wireless/ath/ath12k/hif.h index e8840fab6061c..4de8667690e91 100644 --- a/drivers/net/wireless/ath/ath12k/hif.h +++ b/drivers/net/wireless/ath/ath12k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_HIF_H diff --git a/drivers/net/wireless/ath/ath12k/htc.h b/drivers/net/wireless/ath/ath12k/htc.h index 7e3dccc7cc148..05b3e593542d3 100644 --- a/drivers/net/wireless/ath/ath12k/htc.h +++ b/drivers/net/wireless/ath/ath12k/htc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_HTC_H @@ -301,7 +301,7 @@ struct ath12k_htc { u8 wmi_ep_count; }; -int ath12k_htc_init(struct ath12k_base *ar); +int ath12k_htc_init(struct ath12k_base *ab); int ath12k_htc_wait_target(struct ath12k_htc *htc); int ath12k_htc_start(struct ath12k_htc *htc); int ath12k_htc_connect_service(struct ath12k_htc *htc, @@ -309,8 +309,8 @@ int ath12k_htc_connect_service(struct ath12k_htc *htc, struct ath12k_htc_svc_conn_resp *conn_resp); int ath12k_htc_send(struct ath12k_htc *htc, enum ath12k_htc_ep_id eid, struct sk_buff *packet); -struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ar, int size); -void ath12k_htc_rx_completion_handler(struct ath12k_base *ar, +struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size); +void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index da75d19ae1a00..49cfd5dfc70a6 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -23,14 +23,28 @@ struct ieee80211_rx_status; #define TARGET_NUM_VDEVS(ab) ((ab)->profile_param->num_vdevs) /* Max num of stations for Single Radio mode */ -#define TARGET_NUM_STATIONS_SINGLE(ab) ((ab)->profile_param->max_client_single) +#define TARGET_NUM_STATIONS_SINGLE(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_single, \ + _ab->profile_param->max_client_single); \ +}) /* Max num of stations for DBS */ -#define TARGET_NUM_STATIONS_DBS(ab) ((ab)->profile_param->max_client_dbs) +#define TARGET_NUM_STATIONS_DBS(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs, \ + _ab->profile_param->max_client_dbs); \ +}) /* Max num of stations for DBS_SBS */ #define TARGET_NUM_STATIONS_DBS_SBS(ab) \ - ((ab)->profile_param->max_client_dbs_sbs) +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs_sbs, \ + _ab->profile_param->max_client_dbs_sbs); \ +}) #define TARGET_NUM_STATIONS(ab, x) TARGET_NUM_STATIONS_##x(ab) @@ -182,6 +196,7 @@ struct ath12k_hw_params { bool supports_shadow_regs:1; bool supports_aspm:1; bool current_cc_support:1; + bool supports_cong_ctrl_max_msdus:1; u32 num_tcl_banks; u32 max_tx_ring; @@ -217,6 +232,11 @@ struct ath12k_hw_params { /* setup REO queue, frag etc only for primary link peer */ bool dp_primary_link_only:1; + struct { + u32 max_client_single; + u32 max_client_dbs; + u32 max_client_dbs_sbs; + } client; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index c467cf8dfbec2..f22de384d3584 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -168,30 +168,31 @@ static const struct ieee80211_channel ath12k_6ghz_channels[] = { CHAN6G(233, 7115, 0), }; +#define ATH12K_MAC_RATE_A_M(bps, code) \ + { .bitrate = (bps), .hw_value = (code),\ + .flags = IEEE80211_RATE_MANDATORY_A } + +#define ATH12K_MAC_RATE_B(bps, code, code_short) \ + { .bitrate = (bps), .hw_value = (code), .hw_value_short = (code_short),\ + .flags = IEEE80211_RATE_SHORT_PREAMBLE } + static struct ieee80211_rate ath12k_legacy_rates[] = { { .bitrate = 10, .hw_value = ATH12K_HW_RATE_CCK_LP_1M }, - { .bitrate = 20, - .hw_value = ATH12K_HW_RATE_CCK_LP_2M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_2M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, - .hw_value = ATH12K_HW_RATE_CCK_LP_5_5M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_5_5M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, - .hw_value = ATH12K_HW_RATE_CCK_LP_11M, - .hw_value_short = ATH12K_HW_RATE_CCK_SP_11M, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - - { .bitrate = 60, .hw_value = ATH12K_HW_RATE_OFDM_6M }, - { .bitrate = 90, .hw_value = ATH12K_HW_RATE_OFDM_9M }, - { .bitrate = 120, .hw_value = ATH12K_HW_RATE_OFDM_12M }, - { .bitrate = 180, .hw_value = ATH12K_HW_RATE_OFDM_18M }, - { .bitrate = 240, .hw_value = ATH12K_HW_RATE_OFDM_24M }, - { .bitrate = 360, .hw_value = ATH12K_HW_RATE_OFDM_36M }, - { .bitrate = 480, .hw_value = ATH12K_HW_RATE_OFDM_48M }, - { .bitrate = 540, .hw_value = ATH12K_HW_RATE_OFDM_54M }, + ATH12K_MAC_RATE_B(20, ATH12K_HW_RATE_CCK_LP_2M, + ATH12K_HW_RATE_CCK_SP_2M), + ATH12K_MAC_RATE_B(55, ATH12K_HW_RATE_CCK_LP_5_5M, + ATH12K_HW_RATE_CCK_SP_5_5M), + ATH12K_MAC_RATE_B(110, ATH12K_HW_RATE_CCK_LP_11M, + ATH12K_HW_RATE_CCK_SP_11M), + ATH12K_MAC_RATE_A_M(60, ATH12K_HW_RATE_OFDM_6M), + ATH12K_MAC_RATE_A_M(90, ATH12K_HW_RATE_OFDM_9M), + ATH12K_MAC_RATE_A_M(120, ATH12K_HW_RATE_OFDM_12M), + ATH12K_MAC_RATE_A_M(180, ATH12K_HW_RATE_OFDM_18M), + ATH12K_MAC_RATE_A_M(240, ATH12K_HW_RATE_OFDM_24M), + ATH12K_MAC_RATE_A_M(360, ATH12K_HW_RATE_OFDM_36M), + ATH12K_MAC_RATE_A_M(480, ATH12K_HW_RATE_OFDM_48M), + ATH12K_MAC_RATE_A_M(540, ATH12K_HW_RATE_OFDM_54M), }; static const int @@ -736,11 +737,17 @@ u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, if (ath12k_mac_bitrate_is_cck(rate->bitrate) != cck) continue; - if (rate->hw_value == hw_rate) + /* To handle 802.11a PPDU type */ + if ((!cck) && (rate->hw_value == hw_rate) && + (rate->flags & IEEE80211_RATE_MANDATORY_A)) return i; + /* To handle 802.11b short PPDU type */ else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE && rate->hw_value_short == hw_rate) return i; + /* To handle 802.11b long PPDU type */ + else if (rate->hw_value == hw_rate) + return i; } return 0; @@ -785,7 +792,7 @@ struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) /* To use the arvif returned, caller must have held rcu read lock. */ - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif_iter.vdev_id = vdev_id; arvif_iter.ar = ar; @@ -3982,6 +3989,17 @@ static void ath12k_bss_assoc(struct ath12k *ar, ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", arvif->vdev_id, ret); + if (ar->ab->hw_params->supports_sta_ps && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_DTIM_POLICY, + WMI_DTIM_POLICY_STICK); + if (ret) + ath12k_warn(ar->ab, "failed to set vdev %d stick DTIM policy: %d\n", + arvif->vdev_id, ret); + } + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) && ahvif->vdev_type == WMI_VDEV_TYPE_STA && ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) @@ -4323,7 +4341,8 @@ static int ath12k_mac_fils_discovery(struct ath12k_link_vif *arvif, if (info->fils_discovery.max_interval) { interval = info->fils_discovery.max_interval; - tmpl = ieee80211_get_fils_discovery_tmpl(hw, vif); + tmpl = ieee80211_get_fils_discovery_tmpl(hw, vif, + info->link_id); if (tmpl) ret = ath12k_wmi_fils_discovery_tmpl(ar, arvif->vdev_id, tmpl); @@ -4331,7 +4350,8 @@ static int ath12k_mac_fils_discovery(struct ath12k_link_vif *arvif, unsol_bcast_probe_resp_enabled = 1; interval = info->unsol_bcast_probe_resp_interval; - tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); + tmpl = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif, + info->link_id); if (tmpl) ret = ath12k_wmi_probe_resp_tmpl(ar, arvif->vdev_id, tmpl); @@ -5442,7 +5462,7 @@ int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw, ar->last_tx_power_update)) goto send_tx_power; - params.pdev_id = ar->pdev->pdev_id; + params.pdev_id = ath12k_mac_get_target_pdev_id(ar); params.vdev_id = arvif->vdev_id; params.stats_id = WMI_REQUEST_PDEV_STAT; ret = ath12k_mac_get_fw_stats(ar, ¶ms); @@ -5614,12 +5634,14 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - arg = kzalloc(sizeof(*arg), GFP_KERNEL); + arg = kzalloc(struct_size(arg, chan_list, n_channels), GFP_KERNEL); if (!arg) { ret = -ENOMEM; goto exit; } + arg->num_chan = n_channels; + ath12k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH12K_SCAN_ID; @@ -5641,18 +5663,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, arg->scan_f_passive = 1; } - if (n_channels) { - arg->num_chan = n_channels; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); - if (!arg->chan_list) { - ret = -ENOMEM; - goto exit; - } - - for (i = 0; i < arg->num_chan; i++) - arg->chan_list[i] = chan_list[i]->center_freq; - } + for (i = 0; i < arg->num_chan; i++) + arg->chan_list[i] = chan_list[i]->center_freq; ret = ath12k_start_scan(ar, arg); if (ret) { @@ -5677,7 +5689,6 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, exit: if (arg) { - kfree(arg->chan_list); kfree(arg->extraie.ptr); kfree(arg); } @@ -6575,6 +6586,79 @@ static int ath12k_mac_station_disassoc(struct ath12k *ar, return 0; } +static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta) +{ + struct ath12k_dp_link_peer *peer; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + struct ath12k_vif *ahvif; + struct ath12k_dp *dp; + unsigned long links; + struct ath12k *ar; + u8 link_id; + int ret; + + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arsta = wiphy_dereference(wiphy, ahsta->link[link_id]); + if (!arsta) + continue; + + arvif = arsta->arvif; + ahvif = arvif->ahvif; + ar = arvif->ar; + + if (arvif->set_wds_vdev_param) + goto skip_nawds; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "setting USE_4ADDR for peer %pM\n", arsta->addr); + + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME); + if (ret) { + ath12k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", + arsta->addr, ret); + return ret; + } + + if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET) + goto skip_nawds; + + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WDS_EXT_ENABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n", + arvif->vdev_id, ret); + return ret; + } + + arvif->nawds_enabled = true; + +skip_nawds: + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, + arsta->addr); + if (peer && peer->dp_peer) { + peer->dp_peer->ucast_ra_only = true; + peer->dp_peer->use_4addr = true; + } else { + spin_unlock_bh(&dp->dp_lock); + ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", + arsta->addr); + return -ENOENT; + } + + spin_unlock_bh(&dp->dp_lock); + } + + return 0; +} + static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) { struct ieee80211_link_sta *link_sta; @@ -6796,6 +6880,8 @@ static void ath12k_mac_free_unassign_link_sta(struct ath12k_hw *ah, return; ahsta->links_map &= ~BIT(link_id); + ahsta->free_logical_link_idx_map |= BIT(arsta->link_idx); + rcu_assign_pointer(ahsta->link[link_id], NULL); synchronize_rcu(); @@ -7114,6 +7200,7 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); struct ieee80211_link_sta *link_sta; struct ath12k_link_vif *arvif; + int link_idx; lockdep_assert_wiphy(ah->hw->wiphy); @@ -7132,8 +7219,16 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, ether_addr_copy(arsta->addr, link_sta->addr); - /* logical index of the link sta in order of creation */ - arsta->link_idx = ahsta->num_peer++; + if (!ahsta->free_logical_link_idx_map) + return -ENOSPC; + + /* + * Allocate a logical link index by selecting the first available bit + * from the free logical index map + */ + link_idx = __ffs(ahsta->free_logical_link_idx_map); + ahsta->free_logical_link_idx_map &= ~BIT(link_idx); + arsta->link_idx = link_idx; arsta->link_id = link_id; ahsta->links_map |= BIT(arsta->link_id); @@ -7642,6 +7737,7 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) { memset(ahsta, 0, sizeof(*ahsta)); + ahsta->free_logical_link_idx_map = U16_MAX; arsta = &ahsta->deflink; @@ -7857,6 +7953,28 @@ int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ath12k_mac_op_sta_set_txpwr); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + + lockdep_assert_wiphy(hw->wiphy); + + /* + * 4-address mode disabled option is available only for station + * interface from mac80211, and we have wds_vdev_param for station + * interface and target will not allow to disable the wds_vdev_param + * during run time. So, add support only for enable case, for + * disable case station interface needs to be reconnect. + */ + if (enabled && !ahsta->enable_4addr) { + if (!ath12k_mac_sta_set_4addr(hw->wiphy, ahsta)) + ahsta->enable_4addr = true; + } +} +EXPORT_SYMBOL(ath12k_mac_op_sta_set_4addr); + void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, @@ -8386,6 +8504,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */ @@ -9615,6 +9737,19 @@ static int ath12k_mac_start(struct ath12k *ar) goto err; } + if (ab->hw_params->supports_cong_ctrl_max_msdus) { + ret = ath12k_wmi_pdev_set_param(ar, + WMI_PDEV_PARAM_SET_CONG_CTRL_MAX_MSDUS, + ATH12K_NUM_POOL_TX_DESC(ab), + pdev->pdev_id); + if (ret) { + ath12k_err(ab, + "failed to set congestion control MAX MSDUS: %d\n", + ret); + goto err; + } + } + __ath12k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); /* TODO: Do we need to enable ANI? */ @@ -9637,6 +9772,10 @@ static int ath12k_mac_start(struct ath12k *ar) ar->allocated_vdev_map = 0; ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; + spin_lock_bh(&ar->data_lock); + ar->incumbent_signal_interference.handling_in_progress = false; + spin_unlock_bh(&ar->data_lock); + /* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration. */ @@ -9664,6 +9803,12 @@ static int ath12k_mac_start(struct ath12k *ar) } } + ret = ath12k_thermal_throttling_config_default(ar); + if (ret) { + ath12k_err(ab, "failed to set thermal throttle: %d\n", ret); + goto err; + } + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], &ab->pdevs[ar->pdev_idx]); @@ -9844,6 +9989,10 @@ static void ath12k_mac_stop(struct ath12k *ar) synchronize_rcu(); atomic_set(&ar->num_pending_mgmt_tx, 0); + + spin_lock_bh(&ar->data_lock); + ar->incumbent_signal_interference.handling_in_progress = false; + spin_unlock_bh(&ar->data_lock); } void ath12k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) @@ -9996,7 +10145,9 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_AP) vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED | - IEEE80211_OFFLOAD_DECAP_ENABLED); + IEEE80211_OFFLOAD_DECAP_ENABLED | + IEEE80211_OFFLOAD_ENCAP_MCAST | + IEEE80211_OFFLOAD_ENCAP_4ADDR); if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET; @@ -10013,6 +10164,10 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; } + if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) + vif->offload_flags |= (IEEE80211_OFFLOAD_ENCAP_MCAST | + IEEE80211_OFFLOAD_ENCAP_4ADDR); + param_id = WMI_VDEV_PARAM_RX_DECAP_TYPE; if (vif->offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED) param_value = ATH12K_HW_TXRX_ETHERNET; @@ -10213,6 +10368,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) struct ieee80211_hw *hw = ah->hw; struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); struct ath12k_wmi_vdev_create_arg vdev_arg = {}; struct ath12k_wmi_peer_create_arg peer_param = {}; struct ieee80211_bss_conf *link_conf = NULL; @@ -10381,6 +10537,26 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) goto err_peer_del; } + /* + * There could be race condition in firmware for the station + * interface between enabling 4-address peer WMI param and + * sending 4-address frame (NULL or EAPOL via TCL). + * Make the station as WDS while bringup itself + * to avoid the race condition + */ + if (vif->type == NL80211_IFTYPE_STATION && + (wdev && wdev->use_4addr)) { + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_WDS, + 1); + if (ret) { + ath12k_warn(ar->ab, "failed to set WDS vdev param: %d\n", + ret); + goto err_peer_del; + } + arvif->set_wds_vdev_param = true; + } + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && ahvif->vdev_type == WMI_VDEV_TYPE_STA && ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { @@ -10420,22 +10596,8 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) err_peer_del: if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { - reinit_completion(&ar->peer_delete_done); - - ret = ath12k_wmi_send_peer_delete_cmd(ar, arvif->bssid, - arvif->vdev_id); - if (ret) { - ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", - arvif->vdev_id, arvif->bssid); - goto err_dp_peer_del; - } - - ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, - arvif->bssid); - if (ret) - goto err_dp_peer_del; - - ar->num_peers--; + /* ignore return value: propagate the original error */ + ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); } err_dp_peer_del: @@ -11109,6 +11271,8 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, ml_arg->assoc_link = arvif->is_sta_assoc_link; + ml_arg->ieee_link_id = arvif->link_id; + partner_info = ml_arg->partner_info; links = ahvif->links_map; @@ -11121,7 +11285,7 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, if (arvif == arvif_p) continue; - if (!arvif_p->is_created) + if (!arvif_p->is_started) continue; link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, @@ -11132,6 +11296,7 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, partner_info->vdev_id = arvif_p->vdev_id; partner_info->hw_link_id = arvif_p->ar->pdev->hw_link_id; + partner_info->ieee_link_id = arvif_p->link_id; ether_addr_copy(partner_info->addr, link_conf->addr); ml_arg->num_partner_links++; partner_info++; @@ -11429,8 +11594,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, struct ieee80211_vif_chanctx_switch *vifs, int n_vifs) { + struct ath12k_incumbent_signal_interference *incumbent; struct ath12k_wmi_vdev_up_params params = {}; struct ieee80211_bss_conf *link_conf; + struct cfg80211_chan_def *chandef; struct ath12k_base *ab = ar->ab; struct ath12k_link_vif *arvif; struct ieee80211_vif *vif; @@ -11455,6 +11622,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, continue; } + if (WARN_ON(!arvif)) + continue; + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", arvif->vdev_id, @@ -11542,6 +11712,42 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, if (!ath12k_mac_monitor_stop(ar)) ath12k_mac_monitor_start(ar); } + + incumbent = &ar->incumbent_signal_interference; + spin_lock_bh(&ar->data_lock); + if (incumbent->handling_in_progress) { + chandef = &vifs[0].new_ctx->def; + if (incumbent->chan_bw_interference_bitmap & + ATH12K_WMI_DCS_SEG_PRI20) { + if (incumbent->center_freq != + chandef->chan->center_freq) { + incumbent->chan_bw_interference_bitmap = 0; + incumbent->handling_in_progress = false; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "incumbent signal interference chan switch completed\n"); + } else { + ath12k_warn(ab, + "incumbent signal interference chan switch not done, freq %u\n", + incumbent->center_freq); + } + } else { + if (incumbent->center_freq != + chandef->chan->center_freq || + incumbent->width != chandef->width) { + incumbent->chan_bw_interference_bitmap = 0; + incumbent->handling_in_progress = false; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "Bandwidth/channel change due to incumbent signal interference completed\n"); + } else { + ath12k_warn(ab, "Bandwidth/channel change due to incumbent sig intf not done intf_freq %u chan_freq %u intf_width %u chan_width %u\n", + incumbent->center_freq, + chandef->chan->center_freq, + incumbent->width, + chandef->width); + } + } + } + spin_unlock_bh(&ar->data_lock); } static void @@ -12210,23 +12416,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k *ar; + struct ath12k *curr_ar, *new_ar, *group_ar; + struct ieee80211_vif_chanctx_switch *v; + int i, j, count = 0; lockdep_assert_wiphy(hw->wiphy); - ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx); - if (!ar) - return -EINVAL; + if (n_vifs == 0) + return 0; - /* Switching channels across radio is not allowed */ - if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) - return -EINVAL; + struct ath12k **ar_map __free(kfree) = kcalloc(n_vifs, sizeof(*ar_map), GFP_KERNEL); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "mac chanctx switch n_vifs %d mode %d\n", - n_vifs, mode); - ath12k_mac_update_vif_chan(ar, vifs, n_vifs); + if (!ar_map) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + + if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac chanctx switch band change not supported\n"); + return -EOPNOTSUPP; + } + + curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx); + new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx); + + if (!curr_ar || !new_ar) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "unable to determine device for the passed channel ctx\n"); + ath12k_generic_dbg(ATH12K_DBG_MAC, + "Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n", + v->old_ctx->def.chan->center_freq, + curr_ar ? "valid" : "invalid", + v->new_ctx->def.chan->center_freq, + new_ar ? "valid" : "invalid"); + return -EINVAL; + } + + /* Switching a vif between two radios is not allowed */ + if (curr_ar != new_ar) { + ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch to another radio not supported\n"); + return -EOPNOTSUPP; + } + + ar_map[i] = curr_ar; + } + + /* Group vifs by radio (ar) and process each group independently. */ + bool *processed __free(kfree) = kcalloc(n_vifs, sizeof(*processed), GFP_KERNEL); + + if (!processed) + return -ENOMEM; + + struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) = + kcalloc(n_vifs, sizeof(*group_vifs), GFP_KERNEL); + + if (!group_vifs) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + if (processed[i]) + continue; + + group_ar = ar_map[i]; + count = 0; + for (j = 0; j < n_vifs; j++) { + if (!processed[j] && ar_map[j] == group_ar) { + group_vifs[count++] = vifs[j]; + processed[j] = true; + } + } + + ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + count, mode); + ath12k_mac_update_vif_chan(group_ar, group_vifs, count); + } return 0; } EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx); @@ -13334,53 +13602,54 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar, int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { + struct ath12k_hw *ah = hw->priv; struct ath12k *ar; struct ieee80211_supported_band *sband; - struct survey_info *ar_survey; - int orig_idx = idx; + struct survey_info *ah_survey; + int sband_idx = idx; lockdep_assert_wiphy(hw->wiphy); - if (idx >= ATH12K_NUM_CHANS) + if (sband_idx >= ATH12K_NUM_CHANS) return -ENOENT; sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; - if (sband && idx >= sband->n_channels) { - idx -= sband->n_channels; + if (sband && sband_idx >= sband->n_channels) { + sband_idx -= sband->n_channels; sband = NULL; } if (!sband) sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; - if (sband && idx >= sband->n_channels) { - idx -= sband->n_channels; + if (sband && sband_idx >= sband->n_channels) { + sband_idx -= sband->n_channels; sband = NULL; } if (!sband) sband = hw->wiphy->bands[NL80211_BAND_6GHZ]; - if (!sband || idx >= sband->n_channels) + if (!sband || sband_idx >= sband->n_channels) return -ENOENT; - ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[idx]); + ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[sband_idx]); if (!ar) { - if (sband->channels[idx].flags & IEEE80211_CHAN_DISABLED) { + if (sband->channels[sband_idx].flags & IEEE80211_CHAN_DISABLED) { memset(survey, 0, sizeof(*survey)); return 0; } return -ENOENT; } - ar_survey = &ar->survey[orig_idx]; + ah_survey = &ah->survey[idx]; - ath12k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); + ath12k_mac_update_bss_chan_survey(ar, &sband->channels[sband_idx]); - spin_lock_bh(&ar->data_lock); - memcpy(survey, ar_survey, sizeof(*survey)); - spin_unlock_bh(&ar->data_lock); + scoped_guard(spinlock_bh, &ah->survey_lock) { + memcpy(survey, ah_survey, sizeof(*survey)); + } - survey->channel = &sband->channels[idx]; + survey->channel = &sband->channels[sband_idx]; if (ar->rx_channel == survey->channel) survey->filled |= SURVEY_INFO_IN_USE; @@ -13464,7 +13733,7 @@ void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, /* TODO: Use real NF instead of default one. */ signal = rate_info.rssi_comb; - params.pdev_id = ar->pdev->pdev_id; + params.pdev_id = ath12k_mac_get_target_pdev_id(ar); params.vdev_id = 0; params.stats_id = WMI_REQUEST_VDEV_STAT; @@ -13592,7 +13861,7 @@ void ath12k_mac_op_link_sta_statistics(struct ieee80211_hw *hw, spin_unlock_bh(&ar->ab->dp->dp_lock); if (!signal && ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA) { - params.pdev_id = ar->pdev->pdev_id; + params.pdev_id = ath12k_mac_get_target_pdev_id(ar); params.vdev_id = 0; params.stats_id = WMI_REQUEST_VDEV_STAT; @@ -13726,19 +13995,13 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2; struct ath12k_wmi_scan_req_arg *arg __free(kfree) = - kzalloc(sizeof(*arg), GFP_KERNEL); + kzalloc(struct_size(arg, chan_list, 1), GFP_KERNEL); if (!arg) return -ENOMEM; - ath12k_wmi_start_scan_init(ar, arg); arg->num_chan = 1; + ath12k_wmi_start_scan_init(ar, arg); - u32 *chan_list __free(kfree) = kcalloc(arg->num_chan, sizeof(*chan_list), - GFP_KERNEL); - if (!chan_list) - return -ENOMEM; - - arg->chan_list = chan_list; arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH12K_SCAN_ID; arg->chan_list[0] = chan->center_freq; @@ -14475,6 +14738,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar, ar->rssi_info.temp_offset = 0; ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset; + ath12k_thermal_init_configs(ar); + return 0; } @@ -14630,12 +14895,6 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; - /* MLO is not yet supported so disable Wireless Extensions for now - * to make sure ath12k users don't use it. This flag can be removed - * once WIPHY_FLAG_SUPPORTS_MLO is enabled. - */ - wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - /* Copy over MLO related capabilities received from * WMI_SERVICE_READY_EXT2_EVENT if single_chip_mlo_supp is set. */ @@ -14731,7 +14990,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) if (is_monitor_disable) /* There's a race between calling ieee80211_register_hw() * and here where the monitor mode is enabled for a little - * while. But that time is so short and in practise it make + * while. But that time is so short and in practice it doesn't make * a difference in real life. */ wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR); @@ -14813,11 +15072,11 @@ static void ath12k_mac_setup(struct ath12k *ar) spin_lock_init(&ar->dp.ppdu_list_lock); INIT_LIST_HEAD(&ar->arvifs); INIT_LIST_HEAD(&ar->dp.ppdu_stats_info); + INIT_LIST_HEAD(&ar->peer_delete_waits); init_completion(&ar->vdev_setup_done); init_completion(&ar->vdev_delete_done); init_completion(&ar->peer_assoc_done); - init_completion(&ar->peer_delete_done); init_completion(&ar->install_key_done); init_completion(&ar->bss_survey_done); init_completion(&ar->scan.started); @@ -14826,6 +15085,11 @@ static void ath12k_mac_setup(struct ath12k *ar) init_completion(&ar->mlo_setup_done); init_completion(&ar->completed_11d_scan); init_completion(&ar->regd_update_completed); + init_completion(&ar->thermal.wmi_sync); + mutex_init(&ar->thermal.lock); + + ar->thermal.temperature = 0; + ar->thermal.hwmon_dev = NULL; INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work); wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work); @@ -15061,6 +15325,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag, mutex_init(&ah->hw_mutex); + spin_lock_init(&ah->survey_lock); spin_lock_init(&ah->dp_hw.peer_lock); INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list); @@ -15075,6 +15340,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag, ar->hw_link_id = pdev->hw_link_id; ar->pdev = pdev; ar->pdev_idx = pdev_idx; + ar->radio_idx = i; pdev->ar = ar; ag->hw_links[ar->hw_link_id].device_id = ab->device_id; @@ -15142,7 +15408,6 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) if (!ab) continue; - ath12k_debugfs_pdev_create(ab); ath12k_mac_set_device_defaults(ab); total_radio += ab->num_radios; } diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 7b50c59763840..aba98afd4365c 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -255,6 +255,9 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index 59589748f1a8c..ae9a8be17be46 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -123,7 +122,7 @@ static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac, struct ath12k_p2p_noa_arg *arg = data; struct ath12k_link_vif *arvif; - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif = &ahvif->deflink; if (!arvif->is_created || arvif->ar != arg->ar || arvif->vdev_id != arg->vdev_id) return; diff --git a/drivers/net/wireless/ath/ath12k/p2p.h b/drivers/net/wireless/ath/ath12k/p2p.h index 03ee877e6d6b7..3041f88508db5 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.h +++ b/drivers/net/wireless/ath/ath12k/p2p.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_P2P_H diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 065449806ea9b..b4ac30149a510 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -189,6 +189,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -198,6 +200,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); @@ -1648,7 +1652,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ret = ab_pci->device_family_ops->arch_init(ab); if (ret) { ath12k_err(ab, "PCI arch_init failed %d\n", ret); - goto err_pci_msi_free; + goto err_free_irq; } ret = ath12k_core_init(ab); diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 5f3bd3b9a3e97..8ef3892269b46 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -9,6 +9,55 @@ #include "debug.h" #include "debugfs.h" +static void ath12k_peer_delete_wait_register(struct ath12k *ar, + struct ath12k_peer_delete_wait *wait, + u32 vdev_id, const u8 *addr) +{ + wait->vdev_id = vdev_id; + ether_addr_copy(wait->addr, addr); + init_completion(&wait->done); + + spin_lock_bh(&ar->data_lock); + list_add(&wait->list, &ar->peer_delete_waits); + spin_unlock_bh(&ar->data_lock); +} + +static void ath12k_peer_delete_wait_unregister(struct ath12k *ar, + struct ath12k_peer_delete_wait *wait) +{ + spin_lock_bh(&ar->data_lock); + list_del(&wait->list); + spin_unlock_bh(&ar->data_lock); +} + +void ath12k_peer_delete_resp_signal(struct ath12k *ar, u32 vdev_id, const u8 *addr) +{ + struct ath12k_peer_delete_wait *wait; + + guard(spinlock_bh)(&ar->data_lock); + + list_for_each_entry(wait, &ar->peer_delete_waits, list) { + if (wait->vdev_id == vdev_id && + ether_addr_equal(wait->addr, addr)) { + complete(&wait->done); + return; + } + } + + ath12k_warn(ar->ab, "failed to find link peer with vdev id %u addr %pM\n", + vdev_id, addr); +} + +void ath12k_peer_delete_wait_flush(struct ath12k *ar) +{ + struct ath12k_peer_delete_wait *wait; + + spin_lock_bh(&ar->data_lock); + list_for_each_entry(wait, &ar->peer_delete_waits, list) + complete(&wait->done); + spin_unlock_bh(&ar->data_lock); +} + static int ath12k_wait_for_dp_link_peer_common(struct ath12k_base *ab, int vdev_id, const u8 *addr, bool expect_mapped) { @@ -62,20 +111,19 @@ static int ath12k_wait_for_peer_deleted(struct ath12k *ar, int vdev_id, const u8 return ath12k_wait_for_dp_link_peer_common(ar->ab, vdev_id, addr, false); } -int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id, - const u8 *addr) +int ath12k_wait_for_peer_delete_done(struct ath12k *ar, + struct ath12k_peer_delete_wait *wait) { - int ret; unsigned long time_left; + int ret; - ret = ath12k_wait_for_peer_deleted(ar, vdev_id, addr); + ret = ath12k_wait_for_peer_deleted(ar, wait->vdev_id, wait->addr); if (ret) { - ath12k_warn(ar->ab, "failed wait for peer deleted"); + ath12k_warn(ar->ab, "failed wait for peer deleted\n"); return ret; } - time_left = wait_for_completion_timeout(&ar->peer_delete_done, - 3 * HZ); + time_left = wait_for_completion_timeout(&wait->done, 3 * HZ); if (time_left == 0) { ath12k_warn(ar->ab, "Timeout in receiving peer delete response\n"); return -ETIMEDOUT; @@ -91,8 +139,6 @@ static int ath12k_peer_delete_send(struct ath12k *ar, u32 vdev_id, const u8 *add lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - reinit_completion(&ar->peer_delete_done); - ret = ath12k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); if (ret) { ath12k_warn(ab, @@ -106,6 +152,7 @@ static int ath12k_peer_delete_send(struct ath12k *ar, u32 vdev_id, const u8 *add int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr) { + struct ath12k_peer_delete_wait wait; int ret; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -114,17 +161,25 @@ int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr) &(ath12k_ar_to_ah(ar)->dp_hw), vdev_id, addr, ar->hw_link_id); + /* + * Register the stack waiter before sending so the resp_event for + * this peer cannot arrive while no waiter is queued. + */ + ath12k_peer_delete_wait_register(ar, &wait, vdev_id, addr); + ret = ath12k_peer_delete_send(ar, vdev_id, addr); if (ret) - return ret; + goto out; - ret = ath12k_wait_for_peer_delete_done(ar, vdev_id, addr); + ret = ath12k_wait_for_peer_delete_done(ar, &wait); if (ret) - return ret; + goto out; ar->num_peers--; - return 0; +out: + ath12k_peer_delete_wait_unregister(ar, &wait); + return ret; } static int ath12k_wait_for_peer_created(struct ath12k *ar, int vdev_id, const u8 *addr) @@ -184,22 +239,26 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arg->vdev_id, arg->peer_addr); if (!peer) { + struct ath12k_peer_delete_wait wait; + spin_unlock_bh(&dp->dp_lock); ath12k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", arg->peer_addr, arg->vdev_id); - reinit_completion(&ar->peer_delete_done); + ath12k_peer_delete_wait_register(ar, &wait, arg->vdev_id, + arg->peer_addr); ret = ath12k_wmi_send_peer_delete_cmd(ar, arg->peer_addr, arg->vdev_id); if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arg->vdev_id, arg->peer_addr); + ath12k_peer_delete_wait_unregister(ar, &wait); return ret; } - ret = ath12k_wait_for_peer_delete_done(ar, arg->vdev_id, - arg->peer_addr); + ret = ath12k_wait_for_peer_delete_done(ar, &wait); + ath12k_peer_delete_wait_unregister(ar, &wait); if (ret) return ret; @@ -218,7 +277,13 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, ahsta->link[link_id]); - + /* TODO: Split DP related field usage to DP peer structure */ + arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); + arsta->ast_hash = peer->ast_hash; + arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; /* Fill ML info into created peer */ @@ -249,6 +314,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ar->hw_link_id); } + if (vif->type == NL80211_IFTYPE_AP && peer->dp_peer) + peer->dp_peer->ucast_ra_only = true; + return ret; } @@ -274,13 +342,14 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah) int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta) { + DECLARE_BITMAP(registered, IEEE80211_MLD_MAX_NUM_LINKS); struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta); struct ath12k_hw *ah = ahvif->ah; struct ath12k_link_vif *arvif; struct ath12k_link_sta *arsta; + int ret, err_ret = 0; unsigned long links; struct ath12k *ar; - int ret, err_ret = 0; u8 link_id; lockdep_assert_wiphy(ah->hw->wiphy); @@ -288,8 +357,19 @@ int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_st if (!sta->mlo) return -EINVAL; - /* FW expects delete of all link peers at once before waiting for reception - * of peer unmap or delete responses + struct ath12k_peer_delete_wait *waits __free(kfree) = + kcalloc(IEEE80211_MLD_MAX_NUM_LINKS, sizeof(*waits), GFP_KERNEL); + if (!waits) + return -ENOMEM; + + bitmap_zero(registered, IEEE80211_MLD_MAX_NUM_LINKS); + + /* + * Firmware expects delete of all link peers at once before waiting + * for reception of peer unmap or delete responses. Phase 1 registers + * a per-link stack waiter and sends WMI peer delete for every + * link; the resp_event handler matches each response to its + * (vdev_id, addr) waiter on ar->peer_delete_waits. */ links = ahsta->links_map; for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { @@ -309,29 +389,36 @@ int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_st arvif->vdev_id, arsta->addr, ar->hw_link_id); + ath12k_peer_delete_wait_register(ar, &waits[link_id], + arvif->vdev_id, arsta->addr); + ret = ath12k_peer_delete_send(ar, arvif->vdev_id, arsta->addr); if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM ret %d\n", arvif->vdev_id, arsta->addr, ret); err_ret = ret; + ath12k_peer_delete_wait_unregister(ar, &waits[link_id]); continue; } + + set_bit(link_id, registered); } - /* Ensure all link peers are deleted and unmapped */ + /* + * Phase 2: wait for unmap + delete_resp on each registered link + * and tear down the waiter. + */ links = ahsta->links_map; for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { - arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); - arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); - if (!arvif || !arsta) + if (!test_bit(link_id, registered)) continue; + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); ar = arvif->ar; - if (!ar) - continue; - ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arsta->addr); + ret = ath12k_wait_for_peer_delete_done(ar, &waits[link_id]); + ath12k_peer_delete_wait_unregister(ar, &waits[link_id]); if (ret) { err_ret = ret; continue; @@ -444,6 +531,9 @@ int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab) void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab) { + if (!ab->rhead_sta_addr) + return; + rhashtable_destroy(ab->rhead_sta_addr); kfree(ab->rhead_sta_addr); ab->rhead_sta_addr = NULL; diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h index 49d89796bc46e..9343944c2b8eb 100644 --- a/drivers/net/wireless/ath/ath12k/peer.h +++ b/drivers/net/wireless/ath/ath12k/peer.h @@ -9,13 +9,23 @@ #include "dp_peer.h" +struct ath12k_peer_delete_wait { + struct list_head list; + u32 vdev_id; + u8 addr[ETH_ALEN]; + struct completion done; +}; + +void ath12k_peer_delete_resp_signal(struct ath12k *ar, u32 vdev_id, const u8 *addr); +void ath12k_peer_delete_wait_flush(struct ath12k *ar); + void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id); int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr); int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_sta *sta, struct ath12k_wmi_peer_create_arg *arg); -int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id, - const u8 *addr); +int ath12k_wait_for_peer_delete_done(struct ath12k *ar, + struct ath12k_peer_delete_wait *wait); int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta); struct ath12k_ml_peer *ath12k_peer_ml_find(struct ath12k_hw *ah, const u8 *addr); diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 71786216d6473..8017132f844ca 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -13,6 +13,7 @@ #include #include #include +#include #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 @@ -21,45 +22,45 @@ static const struct qmi_elem_info wlfw_host_mlo_chip_info_s_v01_ei[] = { { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = 1, - .elem_size = sizeof(u8), + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, chip_id), }, { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = 1, - .elem_size = sizeof(u8), + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, num_local_links), }, { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01, - .elem_size = sizeof(u8), - .array_type = STATIC_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01, + .elem_size = sizeof(u8), + .array_type = STATIC_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, hw_link_id), }, { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01, - .elem_size = sizeof(u8), - .array_type = STATIC_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01, + .elem_size = sizeof(u8), + .array_type = STATIC_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_mlo_chip_info_s_v01, valid_mlo_link_id), }, { - .data_type = QMI_EOTI, + .data_type = QMI_EOTI, .array_type = NO_ARRAY, - .tlv_type = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -506,6 +507,24 @@ static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, feature_list), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x33, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + dynamic_mem_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x33, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + dynamic_mem_support), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -585,23 +604,41 @@ static const struct qmi_elem_info qmi_wlanfw_phy_cap_resp_msg_v01_ei[] = { board_id), }, { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x13, - .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, single_chip_mlo_support_valid), }, { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x13, - .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, single_chip_mlo_support), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + dynamic_ddr_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01, + dynamic_ddr_support), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -1625,42 +1662,45 @@ static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_req_msg_v01_ei[] = { { - .data_type = QMI_UNSIGNED_8_BYTE, - .elem_len = 1, - .elem_size = sizeof(u64), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, addr), + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, + addr), }, { - .data_type = QMI_UNSIGNED_4_BYTE, - .elem_len = 1, - .elem_size = sizeof(u32), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, size), + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, + size), }, { - .data_type = QMI_EOTI, - .array_type = NO_ARRAY, - .tlv_type = QMI_COMMON_TLV_TYPE, + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_resp_msg_v01_ei[] = { { - .data_type = QMI_STRUCT, - .elem_len = 1, - .elem_size = sizeof(struct qmi_response_type_v01), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct qmi_wlanfw_aux_uc_info_resp_msg_v01, resp), - .ei_array = qmi_response_type_v01_ei, + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, }, { - .data_type = QMI_EOTI, - .array_type = NO_ARRAY, - .tlv_type = QMI_COMMON_TLV_TYPE, + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1772,7 +1812,8 @@ static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { }, { .data_type = QMI_EOTI, - .array_type = QMI_COMMON_TLV_TYPE, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1925,7 +1966,7 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(u8), - .array_type = NO_ARRAY, + .array_type = NO_ARRAY, .tlv_type = 0x13, .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, shadow_reg_valid), @@ -1934,7 +1975,7 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(u8), - .array_type = NO_ARRAY, + .array_type = NO_ARRAY, .tlv_type = 0x13, .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, shadow_reg_len), @@ -1943,7 +1984,7 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { .data_type = QMI_STRUCT, .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V01, .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_cfg_s_v01), - .array_type = VAR_LEN_ARRAY, + .array_type = VAR_LEN_ARRAY, .tlv_type = 0x13, .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, shadow_reg), @@ -2003,15 +2044,17 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { { - .data_type = QMI_EOTI, - .array_type = NO_ARRAY, + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { { - .data_type = QMI_EOTI, - .array_type = NO_ARRAY, + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2094,14 +2137,14 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, if (!ag->mlo_capable) { ath12k_dbg(ab, ATH12K_DBG_QMI, - "MLO is disabled hence skip QMI MLO cap"); + "MLO is disabled hence skip QMI MLO cap\n"); return 0; } if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { ag->mlo_capable = false; ath12k_dbg(ab, ATH12K_DBG_QMI, - "skip QMI MLO cap due to invalid num_radio %d\n", + "skip QMI MLO cap due to invalid num_radio %u\n", ab->qmi.num_radios); return 0; } @@ -2125,7 +2168,7 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, req->mlo_num_chips_valid = 1; req->mlo_num_chips = ag->num_devices; - ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo capability advertisement device_id %d group_id %d num_devices %d", + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo capability advertisement device_id %u group_id %u num_devices %u\n", req->mlo_chip_id, req->mlo_group_id, req->mlo_num_chips); mutex_lock(&ag->mutex); @@ -2146,14 +2189,14 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, info->chip_id = partner_ab->device_id; info->num_local_links = partner_ab->qmi.num_radios; - ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo device id %d num_link %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo device id %u num_link %u\n", info->chip_id, info->num_local_links); for (j = 0; j < info->num_local_links; j++) { info->hw_link_id[j] = partner_ab->wsi_info.hw_link_id_base + j; info->valid_mlo_link_id[j] = 1; - ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo hw_link_id %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo hw_link_id %u\n", info->hw_link_id[j]); hw_link_id++; @@ -2248,6 +2291,11 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab) if (ret < 0) goto out; + if (ab->qmi.dynamic_ddr_support) { + req.dynamic_mem_support_valid = 1; + req.dynamic_mem_support = 1; + } + ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); if (ret < 0) @@ -2268,7 +2316,7 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab) goto out; if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "Host capability request failed, result: %d, err: %d\n", + ath12k_warn(ab, "Host capability request failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -2319,11 +2367,15 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = resp.num_phy; + if (resp.dynamic_ddr_support_valid) + ab->qmi.dynamic_ddr_support = resp.dynamic_ddr_support; + ath12k_dbg(ab, ATH12K_DBG_QMI, - "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n", + "phy capability resp valid %u single_chip_mlo_support %u valid %u num_phy %u valid %u board_id %u dynamic_ddr_valid %u dynamic_ddr_support %u\n", resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support, resp.num_phy_valid, resp.num_phy, - resp.board_id_valid, resp.board_id); + resp.board_id_valid, resp.board_id, resp.dynamic_ddr_support_valid, + resp.dynamic_ddr_support); return; @@ -2332,7 +2384,7 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = ab->hw_params->def_num_link; ath12k_dbg(ab, ATH12K_DBG_QMI, - "no valid response from PHY capability, choose default num_phy %d\n", + "no valid response from PHY capability, choose default num_phy %u\n", ab->qmi.num_radios); } @@ -2393,7 +2445,7 @@ static int ath12k_qmi_fw_ind_register_send(struct ath12k_base *ab) } if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "FW Ind register request failed, result: %d, err: %d\n", + ath12k_warn(ab, "FW Ind register request failed, result: %u, err: %u\n", resp->resp.result, resp->resp.error); ret = -EINVAL; goto out; @@ -2428,7 +2480,7 @@ int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) if (!test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && ab->qmi.target_mem_delayed) { delayed = true; - ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %u\n", ab->qmi.mem_seg_count); } else { delayed = false; @@ -2474,7 +2526,7 @@ int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) if (delayed && resp.resp.error == 0) goto out; - ath12k_warn(ab, "Respond mem req failed, result: %d, err: %d\n", + ath12k_warn(ab, "Respond mem req failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -2606,13 +2658,13 @@ static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { ab->qmi.target_mem_delayed = true; ath12k_warn(ab, - "qmi dma allocation failed (%d B type %u), will try later with small size\n", + "qmi dma allocation failed (%u B type %u), will try later with small size\n", chunk->size, chunk->type); ath12k_qmi_free_target_mem_chunk(ab); return -EAGAIN; } - ath12k_warn(ab, "memory allocation failure for %u size: %d\n", + ath12k_warn(ab, "memory allocation failure for %u size: %u\n", chunk->type, chunk->size); return -ENOMEM; } @@ -2659,7 +2711,7 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) mlo_size += chunk->size; if (ag->mlo_mem.mlo_mem_size && mlo_size > ag->mlo_mem.mlo_mem_size) { - ath12k_err(ab, "QMI MLO memory allocation failure, requested size %d is more than allocated size %d", + ath12k_err(ab, "QMI MLO memory allocation failure, requested size %d is more than allocated size %d\n", mlo_size, ag->mlo_mem.mlo_mem_size); ret = -EINVAL; goto err; @@ -2668,7 +2720,7 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) mlo_chunk = &ag->mlo_mem.chunk[mlo_idx]; if (mlo_chunk->paddr) { if (chunk->size != mlo_chunk->size) { - ath12k_err(ab, "QMI MLO chunk memory allocation failure for index %d, requested size %d is more than allocated size %d", + ath12k_err(ab, "QMI MLO chunk memory allocation failure for index %d, requested size %u is more than allocated size %u\n", mlo_idx, chunk->size, mlo_chunk->size); ret = -EINVAL; goto err; @@ -2699,7 +2751,7 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) if (!ag->mlo_mem.mlo_mem_size) { ag->mlo_mem.mlo_mem_size = mlo_size; } else if (ag->mlo_mem.mlo_mem_size != mlo_size) { - ath12k_err(ab, "QMI MLO memory size error, expected size is %d but requested size is %d", + ath12k_err(ab, "QMI MLO memory size error, expected size is %d but requested size is %d\n", ag->mlo_mem.mlo_mem_size, mlo_size); ret = -EINVAL; goto err; @@ -2725,121 +2777,96 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) return ret; } +static const char *ath12k_qmi_get_mem_reg_name(int mem_type) +{ + switch (mem_type) { + case HOST_DDR_REGION_TYPE: + case BDF_MEM_REGION_TYPE: + return "q6-region"; + case M3_DUMP_REGION_TYPE: + return "m3-dump"; + case CALDB_MEM_REGION_TYPE: + return "q6-caldb"; + case MLO_GLOBAL_MEM_REGION_TYPE: + return "mlo-global-mem"; + default: + return NULL; + } +} + static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab) { - struct reserved_mem *rmem; - size_t avail_rmem_size; + struct device_node *np = ab->dev->of_node; + size_t avail_rmem_size, offset = 0; + struct target_mem_chunk *chunk; + struct resource res; + const char *rname; int i, idx, ret; for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { - switch (ab->qmi.target_mem[i].type) { - case HOST_DDR_REGION_TYPE: - rmem = ath12k_core_get_reserved_mem(ab, 0); - if (!rmem) { - ret = -ENODEV; - goto out; - } - - avail_rmem_size = rmem->size; - if (avail_rmem_size < ab->qmi.target_mem[i].size) { - ath12k_dbg(ab, ATH12K_DBG_QMI, - "failed to assign mem type %u req size %u avail size %zu\n", - ab->qmi.target_mem[i].type, - ab->qmi.target_mem[i].size, - avail_rmem_size); - ret = -EINVAL; - goto out; - } - - ab->qmi.target_mem[idx].paddr = rmem->base; - ab->qmi.target_mem[idx].v.ioaddr = - ioremap(ab->qmi.target_mem[idx].paddr, - ab->qmi.target_mem[i].size); - if (!ab->qmi.target_mem[idx].v.ioaddr) { - ret = -EIO; - goto out; - } - ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; - ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; - idx++; - break; - case BDF_MEM_REGION_TYPE: - rmem = ath12k_core_get_reserved_mem(ab, 0); - if (!rmem) { - ret = -ENODEV; - goto out; - } - - avail_rmem_size = rmem->size - ab->hw_params->bdf_addr_offset; - if (avail_rmem_size < ab->qmi.target_mem[i].size) { - ath12k_dbg(ab, ATH12K_DBG_QMI, - "failed to assign mem type %u req size %u avail size %zu\n", - ab->qmi.target_mem[i].type, - ab->qmi.target_mem[i].size, - avail_rmem_size); - ret = -EINVAL; - goto out; - } - ab->qmi.target_mem[idx].paddr = - rmem->base + ab->hw_params->bdf_addr_offset; - ab->qmi.target_mem[idx].v.ioaddr = - ioremap(ab->qmi.target_mem[idx].paddr, - ab->qmi.target_mem[i].size); - if (!ab->qmi.target_mem[idx].v.ioaddr) { - ret = -EIO; - goto out; - } - ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; - ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; - idx++; - break; - case CALDB_MEM_REGION_TYPE: - /* Cold boot calibration is not enabled in Ath12k. Hence, + chunk = &ab->qmi.target_mem[i]; + if (chunk->type == CALDB_MEM_REGION_TYPE) { + /* + * Cold boot calibration is not enabled in Ath12k. Hence, * assign paddr = 0. * Once cold boot calibration is enabled add support to * assign reserved memory from DT. */ ab->qmi.target_mem[idx].paddr = 0; ab->qmi.target_mem[idx].v.ioaddr = NULL; - ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; - ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + ab->qmi.target_mem[idx].size = chunk->size; + ab->qmi.target_mem[idx].type = chunk->type; idx++; - break; - case M3_DUMP_REGION_TYPE: - rmem = ath12k_core_get_reserved_mem(ab, 1); - if (!rmem) { - ret = -EINVAL; - goto out; - } + continue; + } + + rname = ath12k_qmi_get_mem_reg_name(chunk->type); + if (!rname) { + ath12k_warn(ab, "qmi ignore invalid mem req type %u\n", + chunk->type); + continue; + } - avail_rmem_size = rmem->size; - if (avail_rmem_size < ab->qmi.target_mem[i].size) { - ath12k_dbg(ab, ATH12K_DBG_QMI, - "failed to assign mem type %u req size %u avail size %zu\n", - ab->qmi.target_mem[i].type, - ab->qmi.target_mem[i].size, + ret = of_reserved_mem_region_to_resource_byname(np, rname, &res); + if (ret) + goto out; + + avail_rmem_size = resource_size(&res); + if (chunk->type == BDF_MEM_REGION_TYPE || + chunk->type == HOST_DDR_REGION_TYPE) { + if (ab->hw_params->bdf_addr_offset > avail_rmem_size || + offset > avail_rmem_size - ab->hw_params->bdf_addr_offset) { + ath12k_err(ab, "qmi mem offset overflow: bdf_offset=%u offset=%zu size=%zu\n", + ab->hw_params->bdf_addr_offset, offset, avail_rmem_size); ret = -EINVAL; goto out; } - ab->qmi.target_mem[idx].paddr = rmem->base; - ab->qmi.target_mem[idx].v.ioaddr = - ioremap(ab->qmi.target_mem[idx].paddr, - ab->qmi.target_mem[i].size); - if (!ab->qmi.target_mem[idx].v.ioaddr) { - ret = -EIO; - goto out; - } - ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; - ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; - idx++; - break; - default: - ath12k_warn(ab, "qmi ignore invalid mem req type %u\n", - ab->qmi.target_mem[i].type); - break; + avail_rmem_size -= ab->hw_params->bdf_addr_offset + offset; + res.start += ab->hw_params->bdf_addr_offset + offset; + offset += chunk->size; + } + + if (avail_rmem_size < chunk->size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + chunk->type, chunk->size, avail_rmem_size); + ret = -EINVAL; + goto out; } + + ab->qmi.target_mem[idx].paddr = res.start; + ab->qmi.target_mem[idx].v.ioaddr = ioremap(ab->qmi.target_mem[idx].paddr, + chunk->size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + + ab->qmi.target_mem[idx].size = chunk->size; + ab->qmi.target_mem[idx].type = chunk->type; + idx++; } ab->qmi.mem_seg_count = idx; @@ -2884,7 +2911,7 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "qmi targetcap req failed, result: %d, err: %d\n", + ath12k_warn(ab, "qmi targetcap req failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -2936,7 +2963,7 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) ab->qmi.target.chip_id, ab->qmi.target.chip_family, ab->qmi.target.board_id, ab->qmi.target.soc_id); - ath12k_info(ab, "fw_version 0x%x fw_build_timestamp %s fw_build_id %s", + ath12k_info(ab, "fw_version 0x%x fw_build_timestamp %s fw_build_id %s\n", ab->qmi.target.fw_version, ab->qmi.target.fw_build_timestamp, ab->qmi.target.fw_build_id); @@ -3006,7 +3033,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, if (ret < 0) goto out; - ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi bdf download req fixed addr type %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi bdf download req fixed addr type %u\n", type); ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, @@ -3023,7 +3050,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, goto out; if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "qmi BDF download failed, result: %d, err: %d\n", + ath12k_warn(ab, "qmi BDF download failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3036,7 +3063,7 @@ static int ath12k_qmi_load_file_target_mem(struct ath12k_base *ab, temp += req->data_len; req->seg_id++; ath12k_dbg(ab, ATH12K_DBG_QMI, - "qmi bdf download request remaining %i\n", + "qmi bdf download request remaining %u\n", remaining); } } @@ -3127,7 +3154,7 @@ int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, release_firmware(fw_entry); return ret; default: - ath12k_warn(ab, "unknown file type for load %d", type); + ath12k_warn(ab, "unknown file type for load %d\n", type); goto out; } @@ -3239,7 +3266,7 @@ int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) { ret = ath12k_qmi_m3_load(ab); if (ret) { - ath12k_err(ab, "failed to load m3 firmware: %d", ret); + ath12k_err(ab, "failed to load m3 firmware: %d\n", ret); return ret; } req.addr = m3_mem->paddr; @@ -3269,7 +3296,7 @@ int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "qmi M3 info request failed, result: %d, err: %d\n", + ath12k_warn(ab, "qmi M3 info request failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3339,7 +3366,7 @@ static int ath12k_qmi_aux_uc_load(struct ath12k_base *ab) goto out; } - aux_uc_mem->total_size = aux_uc_len; + aux_uc_mem->total_size = aux_uc_len; copy: memcpy(aux_uc_mem->vaddr, aux_uc_data, aux_uc_len); @@ -3364,7 +3391,7 @@ int ath12k_qmi_wlanfw_aux_uc_info_send(struct ath12k_base *ab) ret = ath12k_qmi_aux_uc_load(ab); if (ret) { - ath12k_err(ab, "failed to load aux_uc firmware: %d", ret); + ath12k_err(ab, "failed to load aux_uc firmware: %d\n", ret); return ret; } @@ -3394,7 +3421,7 @@ int ath12k_qmi_wlanfw_aux_uc_info_send(struct ath12k_base *ab) } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "qmi AUX_UC info request failed, result: %d, err: %d\n", + ath12k_warn(ab, "qmi AUX_UC info request failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3426,7 +3453,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req); if (ret < 0) { qmi_txn_cancel(&txn); - ath12k_warn(ab, "qmi failed to send mode request, mode: %d, err = %d\n", + ath12k_warn(ab, "qmi failed to send mode request, mode: %u, err = %d\n", mode, ret); goto out; } @@ -3437,13 +3464,13 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, ath12k_warn(ab, "WLFW service is dis-connected\n"); return 0; } - ath12k_warn(ab, "qmi failed set mode request, mode: %d, err = %d\n", + ath12k_warn(ab, "qmi failed set mode request, mode: %u, err = %d\n", mode, ret); goto out; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "Mode request failed, mode: %d, result: %d err: %d\n", + ath12k_warn(ab, "Mode request failed, mode: %u, result: %u err: %u\n", mode, resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3536,7 +3563,7 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab) } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "qmi wlan config request failed, result: %d, err: %d\n", + ath12k_warn(ab, "qmi wlan config request failed, result: %u, err: %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3580,7 +3607,7 @@ static int ath12k_qmi_wlanfw_wlan_ini_send(struct ath12k_base *ab) } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { - ath12k_warn(ab, "QMI wlan ini response failure: %d %d\n", + ath12k_warn(ab, "QMI wlan ini response failure: %u %u\n", resp.resp.result, resp.resp.error); ret = -EINVAL; goto out; @@ -3663,7 +3690,7 @@ void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab) spin_unlock(&qmi->event_lock); - ath12k_dbg(ab, ATH12K_DBG_QMI, "trigger host cap for device id %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "trigger host cap for device id %u\n", ab->device_id); ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_HOST_CAP, NULL); @@ -3833,7 +3860,7 @@ static void ath12k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, for (i = 0; i < qmi->mem_seg_count ; i++) { ab->qmi.target_mem[i].type = msg->mem_seg[i].type; ab->qmi.target_mem[i].size = msg->mem_seg[i].size; - ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi mem seg type %d size %d\n", + ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi mem seg type %d size %u\n", msg->mem_seg[i].type, msg->mem_seg[i].size); } @@ -3954,7 +3981,7 @@ static int ath12k_qmi_event_host_cap(struct ath12k_qmi *qmi) ret = ath12k_qmi_host_cap_send(ab); if (ret < 0) { - ath12k_warn(ab, "failed to send qmi host cap for device id %d: %d\n", + ath12k_warn(ab, "failed to send qmi host cap for device id %u: %d\n", ab->device_id, ret); return ret; } @@ -4024,7 +4051,7 @@ static void ath12k_qmi_driver_event_work(struct work_struct *work) set_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags); break; default: - ath12k_warn(ab, "invalid event type: %d", event->type); + ath12k_warn(ab, "invalid event type: %d\n", event->type); break; } @@ -4061,7 +4088,7 @@ int ath12k_qmi_init_service(struct ath12k_base *ab) spin_lock_init(&ab->qmi.event_lock); INIT_WORK(&ab->qmi.event_work, ath12k_qmi_driver_event_work); - ret = qmi_add_lookup(&ab->qmi.handle, ATH12K_QMI_WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW, ATH12K_QMI_WLFW_SERVICE_VERS_V01, ab->qmi.service_ins_id); if (ret < 0) { diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index b5a4a01391cba..cbe5be30053a0 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -13,9 +13,7 @@ #define ATH12K_HOST_VERSION_STRING "WIN" #define ATH12K_QMI_WLANFW_TIMEOUT_MS 10000 #define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64 -#define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000 #define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 -#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45 #define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1 @@ -25,9 +23,7 @@ #define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 #define ATH12K_QMI_RESP_LEN_MAX 8192 #define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 -#define ATH12K_QMI_CALDB_SIZE 0x480000 #define ATH12K_QMI_BDF_EXT_STR_LENGTH 0x20 -#define ATH12K_QMI_FW_MEM_REQ_SEGMENT_CNT 3 #define ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01 4 #define ATH12K_QMI_DEVMEM_CMEM_INDEX 0 @@ -157,12 +153,11 @@ struct ath12k_qmi { struct m3_mem_region aux_uc_mem; unsigned int service_ins_id; struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01]; + u8 dynamic_ddr_support; }; -#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 261 +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 265 #define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034 -#define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7 -#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 #define QMI_WLFW_MAX_NUM_GPIO_V01 32 #define QMI_WLANFW_MAX_PLATFORM_NAME_LEN_V01 64 #define QMI_WLANFW_MAX_HOST_DDR_RANGE_SIZE_V01 3 @@ -259,7 +254,8 @@ struct qmi_wlanfw_host_cap_req_msg_v01 { struct wlfw_host_mlo_chip_info_s_v01 mlo_chip_info[QMI_WLFW_MAX_NUM_MLO_CHIPS_V01]; u8 feature_list_valid; u64 feature_list; - + u8 dynamic_mem_support_valid; + u8 dynamic_mem_support; }; struct qmi_wlanfw_host_cap_resp_msg_v01 { @@ -268,8 +264,6 @@ struct qmi_wlanfw_host_cap_resp_msg_v01 { #define QMI_WLANFW_PHY_CAP_REQ_MSG_V01_MAX_LEN 0 #define QMI_WLANFW_PHY_CAP_REQ_V01 0x0057 -#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN 18 -#define QMI_WLANFW_PHY_CAP_RESP_V01 0x0057 struct qmi_wlanfw_phy_cap_req_msg_v01 { }; @@ -282,12 +276,12 @@ struct qmi_wlanfw_phy_cap_resp_msg_v01 { u32 board_id; u8 single_chip_mlo_support_valid; u8 single_chip_mlo_support; + u8 dynamic_ddr_support_valid; + u8 dynamic_ddr_support; }; #define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 #define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 -#define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 -#define QMI_WLANFW_IND_REGISTER_RESP_V01 0x0020 #define QMI_WLANFW_CLIENT_ID 0x4b4e454c struct qmi_wlanfw_ind_register_req_msg_v01 { @@ -323,12 +317,8 @@ struct qmi_wlanfw_ind_register_resp_msg_v01 { u64 fw_status; }; -#define QMI_WLANFW_REQUEST_MEM_IND_MSG_V01_MAX_LEN 1824 #define QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN 888 -#define QMI_WLANFW_RESPOND_MEM_RESP_MSG_V01_MAX_LEN 7 -#define QMI_WLANFW_REQUEST_MEM_IND_V01 0x0035 #define QMI_WLANFW_RESPOND_MEM_REQ_V01 0x0036 -#define QMI_WLANFW_RESPOND_MEM_RESP_V01 0x0036 #define QMI_WLANFW_MAX_NUM_MEM_CFG_V01 2 #define QMI_WLANFW_MAX_STR_LEN_V01 16 @@ -386,9 +376,7 @@ struct qmi_wlanfw_fw_ready_ind_msg_v01 { }; #define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 -#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207 #define QMI_WLANFW_CAP_REQ_V01 0x0024 -#define QMI_WLANFW_CAP_RESP_V01 0x0024 enum qmi_wlanfw_pipedir_enum_v01 { QMI_WLFW_PIPEDIR_NONE_V01 = 0, @@ -501,8 +489,6 @@ struct qmi_wlanfw_cap_req_msg_v01 { }; #define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182 -#define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 -#define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025 #define QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 0x0025 /* TODO: Need to check with MCL and FW team that data can be pointer and * can be last element in structure @@ -530,8 +516,6 @@ struct qmi_wlanfw_bdf_download_resp_msg_v01 { }; #define QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -#define QMI_WLANFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 -#define QMI_WLANFW_M3_INFO_RESP_V01 0x003C #define QMI_WLANFW_M3_INFO_REQ_V01 0x003C struct qmi_wlanfw_m3_info_req_msg_v01 { @@ -544,7 +528,6 @@ struct qmi_wlanfw_m3_info_resp_msg_v01 { }; #define QMI_WLANFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -#define QMI_WLANFW_AUX_UC_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 #define QMI_WLANFW_AUX_UC_INFO_REQ_V01 0x005A struct qmi_wlanfw_aux_uc_info_req_msg_v01 { @@ -557,13 +540,9 @@ struct qmi_wlanfw_aux_uc_info_resp_msg_v01 { }; #define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11 -#define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 #define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 -#define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 #define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 -#define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 #define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 -#define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 #define QMI_WLANFW_MAX_STR_LEN_V01 16 #define QMI_WLANFW_MAX_NUM_CE_V01 12 #define QMI_WLANFW_MAX_NUM_SVC_V01 24 @@ -606,9 +585,7 @@ struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { }; #define ATH12K_QMI_WLANFW_WLAN_INI_REQ_V01 0x002F -#define ATH12K_QMI_WLANFW_WLAN_INI_RESP_V01 0x002F #define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 7 -#define QMI_WLANFW_WLAN_INI_RESP_MSG_V01_MAX_LEN 7 struct qmi_wlanfw_wlan_ini_req_msg_v01 { /* Must be set to true if enable_fwlog is being passed */ diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 7898f6981e5a8..351a0d72e38bf 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index da5128b8c97f5..7c37c0d2a6f96 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_REG_H diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h index ef6ab21d19b82..342ed0a7fa98b 100644 --- a/drivers/net/wireless/ath/ath12k/testmode.h +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c new file mode 100644 index 0000000000000..97fc49c40ac15 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/thermal.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "debug.h" + +static const struct ath12k_wmi_tt_level_config_param +tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = { + [ATH12K_TT_CFG_IDX_IPA] = { + [0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0, + .pout_reduction_db = 12 }, + [2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50, + .pout_reduction_db = 12 }, + [3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90, + .pout_reduction_db = 12 }, + [4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100, + .pout_reduction_db = 12 }, + }, + [ATH12K_TT_CFG_IDX_XFEM] = { + [0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50, + .pout_reduction_db = 0 }, + [3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90, + .pout_reduction_db = 0 }, + [4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100, + .pout_reduction_db = 0 }, + }, +}; + +static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar) +{ + if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map)) + return ATH12K_TT_CFG_IDX_IPA; + + return ATH12K_TT_CFG_IDX_XFEM; +} + +int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + int ret; + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map)) + param.num_levels = ENHANCED_THERMAL_LEVELS; + else + param.num_levels = THERMAL_LEVELS; + + param.levelconf = ar->thermal.tt_level_configs; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, + "failed to send thermal mitigation cmd for default config: %d\n", + ret); + return ret; +} + +void ath12k_thermal_init_configs(struct ath12k *ar) +{ + enum ath12k_thermal_cfg_idx cfg_idx; + + cfg_idx = ath12k_thermal_cfg_index(ar); + ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0]; +} + +static int +ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH12K_THERMAL_THROTTLE_MAX; + + return 0; +} + +static int +ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath12k *ar = cdev->devdata; + + mutex_lock(&ar->thermal.lock); + *state = ar->thermal.throttle_state; + mutex_unlock(&ar->thermal.lock); + + return 0; +} + +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + struct ath12k_wmi_tt_level_config_param cfg = {}; + int ret; + + param.num_levels = 1; + cfg.dcoffpercent = throttle_state; + param.levelconf = &cfg; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n", + ret); + + return ret; +} + +static int +ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long throttle_state) +{ + struct ath12k *ar = cdev->devdata; + + if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX) + return -EINVAL; + + scoped_guard(mutex, &ar->thermal.lock) { + if (ar->thermal.throttle_state == throttle_state) + return 0; + ar->thermal.throttle_state = throttle_state; + } + + if (throttle_state == 0) + return ath12k_thermal_throttling_config_default(ar); + + return ath12k_thermal_set_throttling(ar, throttle_state); +} + +static const struct thermal_cooling_device_ops ath12k_thermal_ops = { + .get_max_state = ath12k_thermal_get_max_throttle_state, + .get_cur_state = ath12k_thermal_get_cur_throttle_state, + .set_cur_state = ath12k_thermal_set_cur_throttle_state, +}; + +static ssize_t ath12k_thermal_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ath12k *ar = dev_get_drvdata(dev); + unsigned long time_left; + int ret, temperature; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ar->ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + reinit_completion(&ar->thermal.wmi_sync); + ret = ath12k_wmi_send_pdev_temperature_cmd(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to read temperature %d\n", ret); + return ret; + } + + if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync, + ATH12K_THERMAL_SYNC_TIMEOUT_HZ); + if (!time_left) { + ath12k_warn(ar->ab, "failed to synchronize thermal read\n"); + return -ETIMEDOUT; + } + + spin_lock_bh(&ar->data_lock); + temperature = ar->thermal.temperature; + spin_unlock_bh(&ar->data_lock); + + /* display in millidegree celsius */ + return sysfs_emit(buf, "%d\n", temperature * 1000); +} + +void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature) +{ + spin_lock_bh(&ar->data_lock); + ar->thermal.temperature = temperature; + spin_unlock_bh(&ar->data_lock); + complete_all(&ar->thermal.wmi_sync); +} + +static SENSOR_DEVICE_ATTR_RO(temp1_input, ath12k_thermal_temp, 0); + +static struct attribute *ath12k_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ath12k_hwmon); + +static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i) +{ + char pdev_name[20]; + struct ath12k *ar; + int ret; + + ar = ab->pdevs[i].ar; + if (!ar) + return 0; + + ar->thermal.cdev = + thermal_cooling_device_register("ath12k_thermal", ar, + &ath12k_thermal_ops); + if (IS_ERR(ar->thermal.cdev)) { + ret = PTR_ERR(ar->thermal.cdev); + ar->thermal.cdev = NULL; + ath12k_err(ar->ab, "failed to register cooling device: %d\n", + ret); + return ret; + } + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + + ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj, + &ar->thermal.cdev->device.kobj, pdev_name); + if (ret) { + ath12k_err(ab, "failed to create cooling device symlink: %d\n", + ret); + goto unregister_cdev; + } + + ar->thermal.hwmon_dev = + hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev, + "ath12k_hwmon", ar, + ath12k_hwmon_groups); + if (IS_ERR(ar->thermal.hwmon_dev)) { + ret = PTR_ERR(ar->thermal.hwmon_dev); + ar->thermal.hwmon_dev = NULL; + ath12k_err(ar->ab, "failed to register hwmon device: %d\n", + ret); + goto remove_sysfs; + } + + return 0; + +remove_sysfs: + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); +unregister_cdev: + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; + return ret; +} + +static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i) +{ + char pdev_name[20]; + struct ath12k *ar; + + ar = ab->pdevs[i].ar; + if (!ar) + return; + + hwmon_device_unregister(ar->thermal.hwmon_dev); + ar->thermal.hwmon_dev = NULL; + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); + + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; +} + +int ath12k_thermal_register(struct ath12k_base *ab) +{ + int i, ret; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + for (i = 0; i < ab->num_radios; i++) { + ret = ath12k_thermal_setup_radio(ab, i); + if (ret) + goto out; + } + + return 0; +out: + for (i--; i >= 0; i--) + ath12k_thermal_cleanup_radio(ab, i); + + return ret; +} + +void ath12k_thermal_unregister(struct ath12k_base *ab) +{ + int i; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return; + + for (i = 0; i < ab->num_radios; i++) + ath12k_thermal_cleanup_radio(ab, i); +} diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h new file mode 100644 index 0000000000000..30e7b0880e056 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/thermal.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _ATH12K_THERMAL_ +#define _ATH12K_THERMAL_ + +#include + +#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ) + +#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100 +#define ATH12K_THERMAL_THROTTLE_MAX 100 + +enum ath12k_thermal_cfg_idx { + /* Internal Power Amplifier Device */ + ATH12K_TT_CFG_IDX_IPA, + /* External Power Amplifier Device or External Front End Module */ + ATH12K_TT_CFG_IDX_XFEM, + ATH12K_TT_CFG_IDX_MAX, +}; + +struct ath12k_thermal { + struct completion wmi_sync; + + /* temperature value in Celsius degree protected by data_lock. */ + int temperature; + struct device *hwmon_dev; + const struct ath12k_wmi_tt_level_config_param *tt_level_configs; + struct thermal_cooling_device *cdev; + /* Serialize thermal operations and hwmon reads */ + struct mutex lock; + u32 throttle_state; +}; + +#if IS_REACHABLE(CONFIG_THERMAL) +int ath12k_thermal_register(struct ath12k_base *ab); +void ath12k_thermal_unregister(struct ath12k_base *ab); +void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature); +int ath12k_thermal_throttling_config_default(struct ath12k *ar); +void ath12k_thermal_init_configs(struct ath12k *ar); +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state); +#else +static inline int ath12k_thermal_register(struct ath12k_base *ab) +{ + return 0; +} + +static inline void ath12k_thermal_unregister(struct ath12k_base *ab) +{ +} + +static inline void ath12k_thermal_event_temperature(struct ath12k *ar, + int temperature) +{ +} + +static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + return 0; +} + +static inline void ath12k_thermal_init_configs(struct ath12k *ar) +{ +} + +static inline int ath12k_thermal_set_throttling(struct ath12k *ar, + u32 throttle_state) +{ + return 0; +} +#endif +#endif /* _ATH12K_THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath12k/trace.c b/drivers/net/wireless/ath/ath12k/trace.c index 0d0edf4204b7b..772a5a7db98b3 100644 --- a/drivers/net/wireless/ath/ath12k/trace.c +++ b/drivers/net/wireless/ath/ath12k/trace.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath12k/trace.h b/drivers/net/wireless/ath/ath12k/trace.h index 253c67accb0e1..053d681302f00 100644 --- a/drivers/net/wireless/ath/ath12k/trace.h +++ b/drivers/net/wireless/ath/ath12k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c index a6c5f7689edd1..6a8b8b2a56f90 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c @@ -19,6 +19,9 @@ static const struct of_device_id ath12k_wifi7_ahb_of_match[] = { { .compatible = "qcom,ipq5332-wifi", .data = (void *)ATH12K_HW_IPQ5332_HW10, }, + { .compatible = "qcom,ipq5424-wifi", + .data = (void *)ATH12K_HW_IPQ5424_HW10, + }, { } }; @@ -38,6 +41,11 @@ static int ath12k_wifi7_ahb_probe(struct platform_device *pdev) switch (hw_rev) { case ATH12K_HW_IPQ5332_HW10: ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; + ab_ahb->scm_auth_enabled = true; + break; + case ATH12K_HW_IPQ5424_HW10: + ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; + ab_ahb->scm_auth_enabled = false; break; default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp.c b/drivers/net/wireless/ath/ath12k/wifi7/dp.c index 2b194879ee80d..14479083fc8c7 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp.c @@ -139,7 +139,7 @@ static int ath12k_wifi7_dp_service_srng(struct ath12k_dp *dp, return tot_work_done; } -static struct ath12k_dp_arch_ops ath12k_wifi7_dp_arch_ops = { +static const struct ath12k_dp_arch_ops ath12k_wifi7_dp_arch_ops = { .service_srng = ath12k_wifi7_dp_service_srng, .tx_get_vdev_bank_config = ath12k_wifi7_dp_tx_get_vdev_bank_config, .reo_cmd_send = ath12k_wifi7_dp_reo_cmd_send, diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 2013b4146b1aa..681f007d3ee73 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -405,6 +405,42 @@ ath12k_wifi7_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *r } } +static __always_inline u8 +ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(u8 rate) +{ + u8 ath12k_rate; + + /* Map hal_rx_legacy_rate to ath12k_hw_rate_cck */ + switch (rate) { + case HAL_RX_LEGACY_RATE_LP_1_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_1M; + break; + case HAL_RX_LEGACY_RATE_LP_2_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_2M; + break; + case HAL_RX_LEGACY_RATE_LP_5_5_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_5_5M; + break; + case HAL_RX_LEGACY_RATE_LP_11_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_LP_11M; + break; + case HAL_RX_LEGACY_RATE_SP_2_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_2M; + break; + case HAL_RX_LEGACY_RATE_SP_5_5_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_5_5M; + break; + case HAL_RX_LEGACY_RATE_SP_11_MBPS: + ath12k_rate = ATH12K_HW_RATE_CCK_SP_11M; + break; + default: + ath12k_rate = rate; + break; + } + + return ath12k_rate; +} + static void ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, struct hal_rx_mon_ppdu_info *ppdu_info) @@ -415,25 +451,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, rate = u32_get_bits(info0, HAL_RX_LSIG_B_INFO_INFO0_RATE); switch (rate) { case 1: - rate = HAL_RX_LEGACY_RATE_1_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_1_MBPS; break; case 2: - case 5: - rate = HAL_RX_LEGACY_RATE_2_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_2_MBPS; break; case 3: - case 6: - rate = HAL_RX_LEGACY_RATE_5_5_MBPS; + rate = HAL_RX_LEGACY_RATE_LP_5_5_MBPS; break; case 4: + rate = HAL_RX_LEGACY_RATE_LP_11_MBPS; + break; + case 5: + rate = HAL_RX_LEGACY_RATE_SP_2_MBPS; + break; + case 6: + rate = HAL_RX_LEGACY_RATE_SP_5_5_MBPS; + break; case 7: - rate = HAL_RX_LEGACY_RATE_11_MBPS; + rate = HAL_RX_LEGACY_RATE_SP_11_MBPS; break; default: rate = HAL_RX_LEGACY_RATE_INVALID; + break; } - ppdu_info->rate = rate; + ppdu_info->rate = ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(rate); ppdu_info->cck_flag = 1; } @@ -447,31 +490,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, rate = u32_get_bits(info0, HAL_RX_LSIG_A_INFO_INFO0_RATE); switch (rate) { case 8: - rate = HAL_RX_LEGACY_RATE_48_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_48_MBPS; break; case 9: - rate = HAL_RX_LEGACY_RATE_24_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_24_MBPS; break; case 10: - rate = HAL_RX_LEGACY_RATE_12_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_12_MBPS; break; case 11: - rate = HAL_RX_LEGACY_RATE_6_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_6_MBPS; break; case 12: - rate = HAL_RX_LEGACY_RATE_54_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_54_MBPS; break; case 13: - rate = HAL_RX_LEGACY_RATE_36_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_36_MBPS; break; case 14: - rate = HAL_RX_LEGACY_RATE_18_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_18_MBPS; break; case 15: - rate = HAL_RX_LEGACY_RATE_9_MBPS; + rate = HAL_RX_LEGACY_RATE_OFDM_9_MBPS; break; default: - rate = HAL_RX_LEGACY_RATE_INVALID; + rate = HAL_RX_LEGACY_RATE_OFDM_INVALID; + break; } ppdu_info->rate = rate; @@ -2437,7 +2481,6 @@ ath12k_wifi7_dp_mon_rx_deliver(struct ath12k_pdev_dp *dp_pdev, { struct sk_buff *mon_skb, *skb_next, *header; struct ieee80211_rx_status *rxs = &dp_pdev->rx_status; - u8 decap = DP_RX_DECAP_TYPE_RAW; mon_skb = ath12k_dp_mon_rx_merg_msdus(dp_pdev, mon_mpdu, ppduinfo, rxs); if (!mon_skb) @@ -2464,12 +2507,8 @@ ath12k_wifi7_dp_mon_rx_deliver(struct ath12k_pdev_dp *dp_pdev, } rxs->flag |= RX_FLAG_ONLY_MONITOR; - if (!(rxs->flag & RX_FLAG_ONLY_MONITOR)) - decap = mon_mpdu->decap_format; - ath12k_dp_mon_update_radiotap(dp_pdev, ppduinfo, mon_skb, rxs); - ath12k_dp_mon_rx_deliver_msdu(dp_pdev, napi, mon_skb, ppduinfo, - rxs, decap); + ath12k_dp_mon_rx_deliver_msdu(dp_pdev, napi, mon_skb, rxs); mon_skb = skb_next; } while (mon_skb); rxs->flag = 0; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index d43e1c4d0d67f..42c3881c10fe8 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -324,10 +324,9 @@ static void ath12k_wifi7_dp_rx_h_csum_offload(struct sk_buff *msdu, CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, - struct sk_buff *msdu, - struct hal_rx_desc *rx_desc, - struct hal_rx_desc_data *rx_info) +static int ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) { struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; @@ -349,6 +348,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) { + /* + * Drop the 3-address multicast packet from 4-address + * peer To avoid receiving the duplicate multicast packet + * Specifically from AP interface in 3-address format + */ + if (rxcb->is_mcbc && + rx_info->decap_type == DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (peer->use_4addr && + !(rx_info->is_from_ds && rx_info->is_to_ds)) + return -EINVAL; + } + /* resetting mcbc bit because mcbc packets are unicast * packets only for AP as STA sends unicast packets. */ @@ -389,16 +400,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_wifi7_dp_rx_h_csum_offload(msdu, rx_info); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, rx_desc, - enctype, is_decrypted, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info, + peer); if (!is_decrypted || rx_info->is_mcbc) - return; + return 0; if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } + + return 0; } static int ath12k_wifi7_dp_rx_msdu_coalesce(struct ath12k_hal *hal, @@ -550,14 +563,16 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, } } - if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, rx_desc, msdu, + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) { ret = -EINVAL; goto free_out; } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_desc, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + goto free_out; rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; @@ -986,7 +1001,7 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, struct ieee80211_key_conf *key_conf; struct ieee80211_hdr *hdr; u8 mic[IEEE80211_CCMP_MIC_LEN]; - int head_len, tail_len, ret; + int head_len, tail_len; size_t data_len; u32 hdr_len, hal_rx_desc_sz = hal->hal_desc_sz; u8 *key, *data; @@ -1014,9 +1029,8 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, data_len = msdu->len - head_len - tail_len; key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; - ret = ath12k_dp_rx_h_michael_mic(peer->tfm_mmic, key, hdr, data, - data_len, mic); - if (ret || memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) + michael_mic(key, hdr, data, data_len, mic); + if (memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) goto mic_fail; return 0; @@ -1031,15 +1045,15 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); - if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, rx_desc, msdu, + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) { dev_kfree_skb_any(msdu); return -EINVAL; } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, rx_desc, - HAL_ENCRYPT_TYPE_TKIP_MIC, true, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, + rx_info, NULL); ieee80211_rx(ath12k_pdev_dp_to_hw(dp_pdev), msdu); return -EINVAL; } @@ -1591,10 +1605,10 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_base *ab = dp->ab; u16 msdu_len = rx_info->msdu_len; - struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; u8 l3pad_bytes = rx_info->l3_pad_bytes; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + int ret; if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ @@ -1635,11 +1649,13 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } - if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, desc, msdu, rx_info))) + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) return -EINVAL; ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, desc, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return ret; rxcb->tid = rx_info->tid; @@ -1676,7 +1692,7 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); - if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, desc, msdu, rx_info))) + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) return true; ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); @@ -1684,8 +1700,58 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, rx_info->rx_status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | RX_FLAG_DECRYPTED); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, desc, - HAL_ENCRYPT_TYPE_TKIP_MIC, false, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, false, + rx_info, NULL); + return false; +} + +static bool ath12k_wifi7_dp_rx_h_unauth_wds_err(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) +{ + struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ath12k_dp *dp = dp_pdev->dp; + u32 hdr_len, hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + u8 l3pad_bytes = rx_info->l3_pad_bytes; + struct ath12k_dp_rx_rfc1042_hdr *llc; + u16 msdu_len = rx_info->msdu_len; + struct ath12k_dp_peer *peer; + struct ieee80211_hdr *hdr; + int ret; + + guard(rcu)(); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + if (!peer) { + ath12k_dbg(dp->ab, ATH12K_DBG_DATA, + "failed to find the peer to process unauth wds err handling peer_id %d\n", + rxcb->peer_id); + return true; + } + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return true; + + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); + skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, + rx_info))) + return true; + + ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); + + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return true; + + rxcb->tid = rx_info->tid; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + hdr_len); + if (llc->snap_type != cpu_to_be16(ETH_P_PAE)) + return true; + return false; } @@ -1700,6 +1766,9 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, dp->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR: + drop = ath12k_wifi7_dp_rx_h_unauth_wds_err(dp_pdev, msdu, rx_info); + break; case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: if (rx_info->err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 629084aa36d85..d2749de445534 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -57,10 +57,12 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) return 0; } +#define ATH12K_AST_HASH_MASK 0xF + /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast) + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast) { struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_hal *hal = dp->hal; @@ -78,6 +80,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a struct ath12k_dp_vif *dp_vif = &ahvif->dp_vif; struct ath12k_dp_link_vif *dp_link_vif; struct dp_tx_ring *tx_ring; + struct ethhdr *eth = NULL; u8 pool_id; u8 hal_ring_id; int ret; @@ -96,6 +99,9 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a !ieee80211_is_data(hdr->frame_control)) return -EOPNOTSUPP; + if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) + eth = (struct ethhdr *)skb->data; + pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1); /* Let the default ring selection be based on current processor @@ -124,6 +130,19 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + ti.bss_ast_hash = dp_link_vif->ast_hash; + ti.bss_ast_idx = dp_link_vif->ast_idx; + + if (eth && is_multicast_ether_addr(eth->h_dest) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.bss_ast_hash = arsta->ast_hash & ATH12K_AST_HASH_MASK; + ti.bss_ast_idx = arsta->ast_idx; + ti.lookup_override = true; + } else if (!eth && ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + } if (dp_vif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) { @@ -140,7 +159,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a msdu_ext_desc = true; } - if (gsn_valid) { + if (gsn_valid && !ti.lookup_override) { /* Reset and Initialize meta_data_flags with Global Sequence * Number (GSN) info. */ @@ -148,6 +167,14 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, HTT_TCL_META_DATA_TYPE) | u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + + /* + * Since NAWDS enabled for this vdev firmware expects + * this flag to be set for sending 3-address multicast frame. + */ + ti.meta_data_flags |= + u32_encode_bits(arvif->nawds_enabled, + HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED); } ti.encap_type = ath12k_dp_tx_get_encap_type(ab, skb); @@ -158,11 +185,13 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.lmac_id = dp_link_vif->lmac_id; ti.vdev_id = dp_link_vif->vdev_id; - if (gsn_valid) + + if (gsn_valid && !ti.lookup_override) ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + else if (arvif->nawds_enabled && is_mcast && !ti.lookup_override) + ti.meta_data_flags |= + u32_encode_bits(1, HTT_TCL_META_DATA_HOST_INSPECTED_MISSION); - ti.bss_ast_hash = dp_link_vif->ast_hash; - ti.bss_ast_idx = dp_link_vif->ast_idx; ti.dscp_tid_tbl_idx = 0; if (skb->ip_summed == CHECKSUM_PARTIAL && diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h index 24cf7972d41ba..86bc813878c07 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h @@ -8,8 +8,8 @@ #define ATH12K_DP_TX_WIFI7_H int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast); + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast); void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id); u32 ath12k_wifi7_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_link_vif *arvif); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal.c b/drivers/net/wireless/ath/ath12k/wifi7/hal.c index bd1753ca0db67..a0a1902fb4915 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal.c @@ -50,6 +50,13 @@ static const struct ath12k_hw_version_map ath12k_wifi7_hw_ver_map[] = { .hal_params = &ath12k_hw_hal_params_wcn7850, .hw_regs = &qcc2072_regs, }, + [ATH12K_HW_IPQ5424_HW10] = { + .hal_ops = &hal_qcn9274_ops, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9274_compact), + .tcl_to_wbm_rbm_map = ath12k_hal_tcl_to_wbm_rbm_map_qcn9274, + .hal_params = &ath12k_hw_hal_params_ipq5332, + .hw_regs = &ipq5424_regs, + }, }; int ath12k_wifi7_hal_init(struct ath12k_base *ab) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal.h b/drivers/net/wireless/ath/ath12k/wifi7/hal.h index 9337225a52533..3d93861988938 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal.h @@ -364,6 +364,9 @@ #define HAL_IPQ5332_CE_WFSS_REG_BASE 0x740000 #define HAL_IPQ5332_CE_SIZE 0x100000 +#define HAL_IPQ5424_CE_WFSS_REG_BASE 0x200000 +#define HAL_IPQ5424_CE_SIZE 0x100000 + #define HAL_RX_MAX_BA_WINDOW 256 #define HAL_DEFAULT_BE_BK_VI_REO_TIMEOUT_USEC (100 * 1000) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c index 80ffadc47d48c..7cf00fda996f3 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c @@ -184,6 +184,20 @@ static u8 ath12k_hal_rx_desc_get_l3_pad_bytes_qcc2072(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static u32 ath12k_hal_rx_desc_get_mpdu_start_tag_qcc2072(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcc2072.mpdu_start_tag, @@ -397,6 +411,8 @@ static void ath12k_hal_extract_rx_desc_data_qcc2072(struct hal_rx_desc_data *rx_ rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcc2072(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcc2072(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcc2072(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcc2072(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcc2072(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_qcc2072(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_qcc2072(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_qcc2072(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c index e51b6f2afffc6..052b59265af85 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c @@ -484,6 +484,94 @@ const struct ath12k_hw_regs ipq5332_regs = { HAL_IPQ5332_CE_WFSS_REG_BASE, }; +const struct ath12k_hw_regs ipq5424_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .tcl1_ring_id = 0x00000918, + .tcl1_ring_misc = 0x00000920, + .tcl1_ring_tp_addr_lsb = 0x0000092c, + .tcl1_ring_tp_addr_msb = 0x00000930, + .tcl1_ring_consumer_int_setup_ix0 = 0x00000940, + .tcl1_ring_consumer_int_setup_ix1 = 0x00000944, + .tcl1_ring_msi1_base_lsb = 0x00000958, + .tcl1_ring_msi1_base_msb = 0x0000095c, + .tcl1_ring_base_lsb = 0x00000910, + .tcl1_ring_base_msb = 0x00000914, + .tcl1_ring_msi1_data = 0x00000960, + .tcl2_ring_base_lsb = 0x00000988, + .tcl_ring_base_lsb = 0x00000b68, + + /* TCL STATUS ring address */ + .tcl_status_ring_base_lsb = 0x00000d48, + + /* REO DEST ring address */ + .reo2_ring_base = 0x00000578, + .reo1_misc_ctrl_addr = 0x00000b9c, + .reo1_sw_cookie_cfg0 = 0x0000006c, + .reo1_sw_cookie_cfg1 = 0x00000070, + .reo1_qdesc_lut_base0 = 0x00000074, + .reo1_qdesc_lut_base1 = 0x00000078, + .reo1_ring_base_lsb = 0x00000500, + .reo1_ring_base_msb = 0x00000504, + .reo1_ring_id = 0x00000508, + .reo1_ring_misc = 0x00000510, + .reo1_ring_hp_addr_lsb = 0x00000514, + .reo1_ring_hp_addr_msb = 0x00000518, + .reo1_ring_producer_int_setup = 0x00000524, + .reo1_ring_msi1_base_lsb = 0x00000548, + .reo1_ring_msi1_base_msb = 0x0000054C, + .reo1_ring_msi1_data = 0x00000550, + .reo1_aging_thres_ix0 = 0x00000B28, + .reo1_aging_thres_ix1 = 0x00000B2C, + .reo1_aging_thres_ix2 = 0x00000B30, + .reo1_aging_thres_ix3 = 0x00000B34, + + /* REO Exception ring address */ + .reo2_sw0_ring_base = 0x000008c0, + + /* REO Reinject ring address */ + .sw2reo_ring_base = 0x00000320, + .sw2reo1_ring_base = 0x00000398, + + /* REO cmd ring address */ + .reo_cmd_ring_base = 0x000002A8, + + /* REO status ring address */ + .reo_status_ring_base = 0x00000aa0, + + /* WBM idle link ring address */ + .wbm_idle_ring_base_lsb = 0x00000d3c, + .wbm_idle_ring_misc_addr = 0x00000d4c, + .wbm_r0_idle_list_cntl_addr = 0x00000240, + .wbm_r0_idle_list_size_addr = 0x00000244, + .wbm_scattered_ring_base_lsb = 0x00000250, + .wbm_scattered_ring_base_msb = 0x00000254, + .wbm_scattered_desc_head_info_ix0 = 0x00000260, + .wbm_scattered_desc_head_info_ix1 = 0x00000264, + .wbm_scattered_desc_tail_info_ix0 = 0x00000270, + .wbm_scattered_desc_tail_info_ix1 = 0x00000274, + .wbm_scattered_desc_ptr_hp_addr = 0x0000027c, + + /* SW2WBM release ring address */ + .wbm_sw_release_ring_base_lsb = 0x0000037c, + + /* WBM2SW release ring address */ + .wbm0_release_ring_base_lsb = 0x00000e08, + .wbm1_release_ring_base_lsb = 0x00000e80, + + /* PPE release ring address */ + .ppe_rel_ring_base = 0x0000046c, + + /* CE address */ + .umac_ce0_src_reg_base = 0x00200000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce0_dest_reg_base = 0x00201000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce1_src_reg_base = 0x00202000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce1_dest_reg_base = 0x00203000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, +}; + static inline bool ath12k_hal_rx_desc_get_first_msdu_qcn9274(struct hal_rx_desc *desc) { @@ -611,6 +699,20 @@ u8 ath12k_hal_rx_desc_get_mpdu_tid_qcn9274(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_TID); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline u16 ath12k_hal_rx_desc_get_mpdu_peer_id_qcn9274(struct hal_rx_desc *desc) { @@ -826,6 +928,8 @@ void ath12k_hal_extract_rx_desc_data_qcn9274(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_ctl_valid = ath12k_hal_rx_desc_get_mpdu_seq_ctl_vld_qcn9274(rx_desc); rx_desc_data->fc_valid = ath12k_hal_rx_desc_get_mpdu_fc_valid_qcn9274(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcn9274(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcn9274(rx_desc); rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcn9274(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcn9274(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcn9274(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h index 08c0a04694741..03cf3792d5236 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h @@ -17,6 +17,7 @@ extern const struct hal_ops hal_qcn9274_ops; extern const struct ath12k_hw_regs qcn9274_v1_regs; extern const struct ath12k_hw_regs qcn9274_v2_regs; extern const struct ath12k_hw_regs ipq5332_regs; +extern const struct ath12k_hw_regs ipq5424_regs; extern const struct ath12k_hal_tcl_to_wbm_rbm_map ath12k_hal_tcl_to_wbm_rbm_map_qcn9274[DP_TCL_NUM_RING_MAX]; extern const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h index ac2a8ac03288f..dd60626b3240a 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h @@ -559,7 +559,6 @@ struct hal_eht_sig_ofdma_cmn_eb1 { #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27) #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36) #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45) -#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54) struct hal_eht_sig_ofdma_cmn_eb2 { __le64 info0; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c index 02d3cadf03fea..eeabe9db776eb 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c @@ -59,7 +59,9 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) | le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID); - tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx, + tcl_cmd->info4 = le32_encode_bits(ti->lookup_override, + HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE) | + le32_encode_bits(ti->bss_ast_idx, HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) | le32_encode_bits(ti->bss_ast_hash, HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h index 9d2b1552c2f51..c548a0fbf1bba 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h @@ -34,6 +34,7 @@ struct hal_tx_info { u8 dscp_tid_tbl_idx; bool enable_mesh; int bank_id; + bool lookup_override; }; /* TODO: Check if the actual desc macros can be used instead */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c index 8819860755489..61be8443e46ee 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c @@ -280,6 +280,20 @@ u8 ath12k_hal_rx_desc_get_l3_pad_bytes_wcn7850(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline bool ath12k_hal_rx_desc_encrypt_valid_wcn7850(struct hal_rx_desc *desc) { @@ -599,6 +613,8 @@ void ath12k_hal_extract_rx_desc_data_wcn7850(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_wcn7850(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_wcn7850(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_wcn7850(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_wcn7850(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_wcn7850(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_wcn7850(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_wcn7850(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_wcn7850(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 094e5495976d7..e2184d736f941 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -332,9 +332,15 @@ static const struct ath12k_hw_ring_mask ath12k_wifi7_hw_ring_mask_wcn7850 = { }; static const struct ce_ie_addr ath12k_wifi7_ce_ie_addr_ipq5332 = { - .ie1_reg_addr = CE_HOST_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, - .ie2_reg_addr = CE_HOST_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, - .ie3_reg_addr = CE_HOST_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie1_reg_addr = CE_HOST_IPQ5332_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IPQ5332_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IPQ5332_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, +}; + +static const struct ce_ie_addr ath12k_wifi7_ce_ie_addr_ipq5424 = { + .ie1_reg_addr = CE_HOST_IPQ5424_IE_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IPQ5424_IE_2_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IPQ5424_IE_3_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, }; static const struct ce_remap ath12k_wifi7_ce_remap_ipq5332 = { @@ -343,6 +349,12 @@ static const struct ce_remap ath12k_wifi7_ce_remap_ipq5332 = { .cmem_offset = HAL_SEQ_WCSS_CMEM_OFFSET, }; +static const struct ce_remap ath12k_wifi7_ce_remap_ipq5424 = { + .base = HAL_IPQ5424_CE_WFSS_REG_BASE, + .size = HAL_IPQ5424_CE_SIZE, + .cmem_offset = HAL_SEQ_WCSS_CMEM_OFFSET, +}; + static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { { .name = "qcn9274 hw1.0", @@ -381,6 +393,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_AP_VLAN), .supports_monitor = false, + .supports_cong_ctrl_max_msdus = true, .idle_ps = false, .download_calib = true, @@ -425,6 +438,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "wcn7850 hw2.0", @@ -466,6 +484,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = true, + .supports_cong_ctrl_max_msdus = false, .idle_ps = true, .download_calib = false, @@ -511,6 +530,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcn9274 hw2.0", @@ -549,6 +573,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_AP_VLAN), .supports_monitor = true, + .supports_cong_ctrl_max_msdus = true, .idle_ps = false, .download_calib = true, @@ -593,6 +618,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5332 hw1.0", @@ -620,7 +650,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { ath12k_wifi7_target_service_to_ce_map_wlan_ipq5332, .svc_to_ce_map_len = 18, - .rxdma1_enable = false, + .rxdma1_enable = true, .num_rxdma_per_pdev = 1, .num_rxdma_dst_ring = 0, .rx_mac_buf_ring = false, @@ -629,7 +659,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT), - .supports_monitor = false, + .supports_monitor = true, + .supports_cong_ctrl_max_msdus = true, .idle_ps = false, .download_calib = true, @@ -665,9 +696,14 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .ce_ie_addr = &ath12k_wifi7_ce_ie_addr_ipq5332, .ce_remap = &ath12k_wifi7_ce_remap_ipq5332, - .bdf_addr_offset = 0xC00000, + .bdf_addr_offset = 0x1A00000, .dp_primary_link_only = true, + .client = { + .max_client_single = 256, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcc2072 hw1.0", @@ -709,6 +745,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), .supports_monitor = true, + .supports_cong_ctrl_max_msdus = false, .idle_ps = true, .download_calib = false, @@ -755,6 +792,96 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, + }, + { + .name = "ipq5424 hw1.0", + .hw_rev = ATH12K_HW_IPQ5424_HW10, + .fw = { + .dir = "IPQ5424/hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_remoteproc, + .download_aux_ucode = false, + }, + .max_radios = 1, + .single_pdev_only = false, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .internal_sleep_clock = false, + + .hw_ops = &qcn9274_ops, + .ring_mask = &ath12k_wifi7_hw_ring_mask_ipq5332, + + .host_ce_config = ath12k_wifi7_host_ce_config_ipq5332, + .ce_count = 12, + .target_ce_config = ath12k_wifi7_target_ce_config_wlan_ipq5332, + .target_ce_count = 12, + .svc_to_ce_map = + ath12k_wifi7_target_service_to_ce_map_wlan_ipq5332, + .svc_to_ce_map_len = 18, + + .rxdma1_enable = true, + .num_rxdma_per_pdev = 1, + .num_rxdma_dst_ring = 0, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, + .supports_cong_ctrl_max_msdus = true, + + .idle_ps = false, + .download_calib = true, + .supports_suspend = false, + .tcl_ring_retry = true, + .reoq_lut_support = false, + .supports_shadow_regs = false, + + .num_tcl_banks = 48, + .max_tx_ring = 4, + + .mhi_config = NULL, + + .wmi_init = &ath12k_wifi7_wmi_init_qcn9274, + + .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01), + + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, + + .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, + + .otp_board_id_register = 0, + + .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = false, + .iova_mask = 0, + .supports_aspm = false, + + .ce_ie_addr = &ath12k_wifi7_ce_ie_addr_ipq5424, + .ce_remap = &ath12k_wifi7_ce_remap_ipq5424, + .bdf_addr_offset = 0x940000, + + .current_cc_support = false, + + .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, }; @@ -771,7 +898,9 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_sta *sta = control->sta; + struct ath12k_link_sta *arsta = NULL; struct ath12k_link_vif *tmp_arvif; + struct ath12k_sta *ahsta = NULL; u32 info_flags = info->flags; struct sk_buff *msdu_copied; struct ath12k *ar, *tmp_ar; @@ -783,6 +912,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, struct ethhdr *eth; bool is_prb_rsp; u16 mcbc_gsn; + u8 cb_flags; u8 link_id; int ret; struct ath12k_dp *tmp_dp; @@ -853,6 +983,12 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) is_mcast = is_multicast_ether_addr(hdr->addr1); + if (sta) { + ahsta = ath12k_sta_to_ahsta(control->sta); + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + } + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); @@ -870,10 +1006,15 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, ieee80211_has_protected(hdr->frame_control)) is_dvlan = true; + /* + * Add a sta pointer check to differentiate multicast encapsulation + * offload packets, as the ATH12K_SKB_HW_80211_ENCAP flag is also set + * for such packets. + */ if (!vif->valid_links || !is_mcast || is_dvlan || - (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) || + ((skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) && sta) || test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { - ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, skb, false, 0, is_mcast); + ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, arsta, skb, false, 0, is_mcast); if (unlikely(ret)) { ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); ieee80211_free_txskb(ar->ah->hw, skb); @@ -883,6 +1024,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, mcbc_gsn = atomic_inc_return(&ahvif->dp_vif.mcbc_gsn) & 0xfff; links_map = ahvif->links_map; + cb_flags = skb_cb->flags; for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) { tmp_arvif = rcu_dereference(ahvif->link[link_id]); @@ -890,32 +1032,64 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, continue; tmp_ar = tmp_arvif->ar; - tmp_dp_pdev = ath12k_dp_to_pdev_dp(tmp_ar->ab->dp, + tmp_dp = ath12k_ab_to_dp(tmp_ar->ab); + tmp_dp_pdev = ath12k_dp_to_pdev_dp(tmp_dp, tmp_ar->pdev_idx); if (!tmp_dp_pdev) continue; - msdu_copied = skb_copy(skb, GFP_ATOMIC); - if (!msdu_copied) { - ath12k_err(ar->ab, - "skb copy failure link_id 0x%X vdevid 0x%X\n", - link_id, tmp_arvif->vdev_id); - continue; - } - ath12k_mlo_mcast_update_tx_link_address(vif, link_id, - msdu_copied, - info_flags); + if (cb_flags & ATH12K_SKB_HW_80211_ENCAP) { + /* + * skb->data may be modified for the + * iova_mask devices. It is better to + * use skb_copy() for such devices to + * avoid any potential skb corruption + * related issues. + */ + if (tmp_dp->hw_params->iova_mask) { + msdu_copied = skb_copy(skb, GFP_ATOMIC); + } else { + /* + * ath12k_wifi7_dp_tx() should + * treat cloned HW-encap Ethernet + * multicast frames as read-only. + */ + msdu_copied = skb_clone(skb, GFP_ATOMIC); + } + if (!msdu_copied) { + ath12k_err(ar->ab, + "skb copy/clone failure link_id 0x%X vdevid 0x%X\n", + link_id, tmp_arvif->vdev_id); + continue; + } + } else { + msdu_copied = skb_copy(skb, GFP_ATOMIC); + if (!msdu_copied) { + ath12k_err(ar->ab, + "skb copy failure link_id 0x%X vdevid 0x%X\n", + link_id, tmp_arvif->vdev_id); + continue; + } + + ath12k_mlo_mcast_update_tx_link_address(vif, link_id, + msdu_copied, + info_flags); + } skb_cb = ATH12K_SKB_CB(msdu_copied); skb_cb->link_id = link_id; skb_cb->vif = vif; skb_cb->ar = tmp_ar; + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + else + arsta = NULL; + /* For open mode, skip peer find logic */ if (unlikely(!ahvif->dp_vif.key_cipher)) goto skip_peer_find; - tmp_dp = ath12k_ab_to_dp(tmp_ar->ab); spin_lock_bh(&tmp_dp->dp_lock); peer = ath12k_dp_link_peer_find_by_addr(tmp_dp, tmp_arvif->bssid); @@ -934,15 +1108,20 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, skb_cb->cipher = key->cipher; skb_cb->flags |= ATH12K_SKB_CIPHER_SET; + if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) + goto skip_fctl_protected_check; + hdr = (struct ieee80211_hdr *)msdu_copied->data; if (!ieee80211_has_protected(hdr->frame_control)) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); } + +skip_fctl_protected_check: spin_unlock_bh(&tmp_dp->dp_lock); skip_peer_find: - ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, + ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, arsta, msdu_copied, true, mcbc_gsn, is_mcast); if (unlikely(ret)) { if (ret == -ENOMEM) { @@ -987,6 +1166,7 @@ static const struct ieee80211_ops ath12k_ops_wifi7 = { .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, .link_sta_rc_update = ath12k_mac_op_link_sta_rc_update, + .sta_set_4addr = ath12k_mac_op_sta_set_4addr, .conf_tx = ath12k_mac_op_conf_tx, .set_antenna = ath12k_mac_op_set_antenna, .get_antenna = ath12k_mac_op_get_antenna, diff --git a/drivers/net/wireless/ath/ath12k/wifi7/mhi.c b/drivers/net/wireless/ath/ath12k/wifi7/mhi.c index b8d972659314b..988affafcfd1b 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/mhi.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/mhi.c @@ -20,7 +20,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_qcn9274[] = { .lpm_notify = false, .offload_channel = false, .doorbell_mode_switch = false, - .auto_queue = false, }, { .num = 21, @@ -34,7 +33,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_qcn9274[] = { .lpm_notify = false, .offload_channel = false, .doorbell_mode_switch = false, - .auto_queue = true, }, }; @@ -85,7 +83,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_wcn7850[] = { .lpm_notify = false, .offload_channel = false, .doorbell_mode_switch = false, - .auto_queue = false, }, { .num = 21, @@ -99,7 +96,6 @@ static const struct mhi_channel_config ath12k_wifi7_mhi_channels_wcn7850[] = { .lpm_notify = false, .offload_channel = false, .doorbell_mode_switch = false, - .auto_queue = true, }, }; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 7a4fd03a37b1c..fd9caf9f65fdb 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "core.h" #include "debugfs.h" #include "debug.h" @@ -134,6 +136,10 @@ struct wmi_pdev_set_obss_bitmap_arg { const char *label; }; +static DEFINE_MUTEX(ath12k_wmi_mutex); +static refcount_t ath12k_wmi_refcount; +static void __percpu *ath12k_wmi_tb; + static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, [WMI_TAG_ARRAY_UINT32] = { .min_len = 0 }, @@ -228,6 +234,68 @@ static const int ath12k_hw_mode_pri_map[] = { PRIMAP(WMI_HOST_HW_MODE_MAX), }; +/* + * Interference bitmap transform maps used by + * ath12k_wmi_transform_interference_bitmap(). + * + * Firmware reports bitmap bits in a primary-based order where: + * - bit 0 is always the primary 20 MHz segment, + * - bit 1 is the adjacent 20 MHz in the same 40 MHz block, + * - bit 2 is the lower 20 MHz segment of the adjacent 40 MHz segment + * - bit 3 is the higher 20 MHz segment of the adjacent 40 MHz segment + * - remaining bits continue outward in 80/160/320 MHz groups. + * + * cfg80211 userspace notification expects absolute frequency order where: + * - bit 0 is the lowest-frequency 20 MHz segment in the current chandef, + * - bit N increases monotonically toward higher frequency. + * + * For each bandwidth-specific map: + * - row index = primary 20 MHz index in absolute (low->high) order, + * - column index = source bit position from firmware bitmap, + * - value = destination bit position in absolute order bitmap. + * + * Example for 80 MHz: if primary index is 2 (third 20 MHz chunk from low + * frequency), row intf_map_80[2] = { 2, 3, 0, 1 } means firmware bits {0,1,2,3} + * are remapped to destination bits {2,3,0,1} before notifying cfg80211. + */ + +static const int intf_map_80[4][4] = { + { 0, 1, 2, 3 }, + { 1, 0, 2, 3 }, + { 2, 3, 0, 1 }, + { 3, 2, 0, 1 } +}; + +static const int intf_map_160[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 1, 0, 2, 3, 4, 5, 6, 7 }, + { 2, 3, 0, 1, 4, 5, 6, 7 }, + { 3, 2, 0, 1, 4, 5, 6, 7 }, + { 4, 5, 6, 7, 0, 1, 2, 3 }, + { 5, 4, 6, 7, 0, 1, 2, 3 }, + { 6, 7, 4, 5, 0, 1, 2, 3 }, + { 7, 6, 4, 5, 0, 1, 2, 3 } +}; + +static const int intf_map_320[16][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 3, 2, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 5, 4, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 6, 7, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 7, 6, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 9, 8, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 10, 11, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 11, 10, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 12, 13, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 13, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 14, 15, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 15, 14, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 } +}; + static int ath12k_wmi_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, @@ -289,29 +357,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len, return 0; } -static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb, - const void *ptr, size_t len) -{ - return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse, - (void *)tb); -} - static const void ** -ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, - struct sk_buff *skb, gfp_t gfp) +ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb) { const void **tb; int ret; - tb = kcalloc(WMI_TAG_MAX, sizeof(*tb), gfp); - if (!tb) - return ERR_PTR(-ENOMEM); + tb = this_cpu_ptr(ath12k_wmi_tb); + memset(tb, 0, WMI_TAG_MAX * sizeof(*tb)); - ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len); - if (ret) { - kfree(tb); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_iter_parse, (void *)tb); + if (ret) return ERR_PTR(ret); - } return tb; } @@ -1172,10 +1230,14 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, le32_encode_bits(arg->ml.link_add, ATH12K_WMI_FLAG_MLO_LINK_ADD) | le32_encode_bits(arg->ml.assoc_link, - ATH12K_WMI_FLAG_MLO_START_AS_ACTIVE); + ATH12K_WMI_FLAG_MLO_START_AS_ACTIVE) | + cpu_to_le32(ATH12K_WMI_FLAG_MLO_IEEE_LINK_IDX_VALID); - ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %d start ml flags 0x%x\n", - arg->vdev_id, ml_params->flags); + ml_params->ieee_link_id = cpu_to_le32(arg->ml.ieee_link_id); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "vdev %u start link_id %u ml flags 0x%x\n", + arg->vdev_id, arg->ml.ieee_link_id, + le32_to_cpu(ml_params->flags)); ptr += sizeof(*ml_params); @@ -1188,19 +1250,23 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg, partner_info = ptr; for (i = 0; i < arg->ml.num_partner_links; i++) { + struct wmi_ml_partner_info *pinfo = &arg->ml.partner_info[i]; + partner_info->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_MLO_PARTNER_LINK_PARAMS, sizeof(*partner_info)); - partner_info->vdev_id = - cpu_to_le32(arg->ml.partner_info[i].vdev_id); - partner_info->hw_link_id = - cpu_to_le32(arg->ml.partner_info[i].hw_link_id); + partner_info->vdev_id = cpu_to_le32(pinfo->vdev_id); + partner_info->hw_link_id = cpu_to_le32(pinfo->hw_link_id); ether_addr_copy(partner_info->vdev_addr.addr, - arg->ml.partner_info[i].addr); + pinfo->addr); + partner_info->flags = + cpu_to_le32(ATH12K_WMI_FLAG_MLO_IEEE_LINK_IDX_VALID_PARTNER); + partner_info->ieee_link_id = cpu_to_le32(pinfo->ieee_link_id); - ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "partner vdev %d hw_link_id %d macaddr%pM\n", - partner_info->vdev_id, partner_info->hw_link_id, - partner_info->vdev_addr.addr); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "partner vdev %u hw_link_id %u macaddr %pM link_id %u ml flags 0x%x\n", + pinfo->vdev_id, pinfo->hw_link_id, + pinfo->addr, pinfo->ieee_link_id, + le32_to_cpu(partner_info->flags)); partner_info++; } @@ -2573,9 +2639,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, struct wmi_tlv *tlv; void *ptr; int i, ret, len; - u32 *tmp_ptr, extraie_len_with_pad = 0; - struct ath12k_wmi_hint_short_ssid_arg *s_ssid = NULL; - struct ath12k_wmi_hint_bssid_arg *hint_bssid = NULL; + __le32 *tmp_ptr; + u32 extraie_len_with_pad = 0; + struct ath12k_wmi_hint_short_ssid_params *s_ssid = NULL; + struct ath12k_wmi_hint_bssid_params *hint_bssid = NULL; len = sizeof(*cmd); @@ -2658,9 +2725,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, tlv = ptr; tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, len); ptr += TLV_HDR_SIZE; - tmp_ptr = (u32 *)ptr; + tmp_ptr = (__le32 *)ptr; - memcpy(tmp_ptr, arg->chan_list, arg->num_chan * 4); + for (i = 0; i < arg->num_chan; i++) + tmp_ptr[i] = cpu_to_le32(arg->chan_list[i]); ptr += len; @@ -2716,8 +2784,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, ptr += TLV_HDR_SIZE; s_ssid = ptr; for (i = 0; i < arg->num_hint_s_ssid; ++i) { - s_ssid->freq_flags = arg->hint_s_ssid[i].freq_flags; - s_ssid->short_ssid = arg->hint_s_ssid[i].short_ssid; + s_ssid->freq_flags = + cpu_to_le32(arg->hint_s_ssid[i].freq_flags); + s_ssid->short_ssid = + cpu_to_le32(arg->hint_s_ssid[i].short_ssid); s_ssid++; } ptr += len; @@ -2731,9 +2801,9 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, hint_bssid = ptr; for (i = 0; i < arg->num_hint_bssid; ++i) { hint_bssid->freq_flags = - arg->hint_bssid[i].freq_flags; - ether_addr_copy(&arg->hint_bssid[i].bssid.addr[0], - &hint_bssid->bssid.addr[0]); + cpu_to_le32(arg->hint_bssid[i].freq_flags); + ether_addr_copy(&hint_bssid->bssid.addr[0], + &arg->hint_bssid[i].bssid.addr[0]); hint_bssid++; } } @@ -3386,6 +3456,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, return ret; } +int +ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg) +{ + struct ath12k_wmi_therm_throt_level_config_param *lvl_conf; + struct ath12k_wmi_therm_throt_config_request_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int i, ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf)); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + cmd->enable = cpu_to_le32(1); + cmd->dc = cpu_to_le32(100); + cmd->dc_per_event = cpu_to_le32(0xffffffff); + cmd->therm_throt_levels = cpu_to_le32(arg->num_levels); + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + arg->num_levels * sizeof(*lvl_conf)); + + lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value; + + for (i = 0; i < arg->num_levels; i++) { + lvl_conf->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + sizeof(*lvl_conf)); + + lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm); + lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm); + lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION, + ar->ab->wmi_ab.svc_map)) + lvl_conf->pout_reduction_25db = + cpu_to_le32(arg->levelconf[i].pout_reduction_db); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK, + ar->ab->wmi_ab.svc_map)) + lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask); + + lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE); + lvl_conf++; + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n", + ar->pdev->pdev_id, arg->num_levels); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_THERM_THROT_SET_CONF cmd: %d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, struct wmi_11d_scan_start_arg *arg) { @@ -3913,9 +4052,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_obss_color_collision_event *ev; struct ath12k_link_vif *arvif; u32 vdev_id, evt_type; + const void **tb; u64 bitmap; - const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n", PTR_ERR(tb)); @@ -4066,7 +4206,12 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); - wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); + wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | + WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); + + if (tg_cfg->is_wds_null_frame_supported) + wmi_cfg->flags2 |= + cpu_to_le32(WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, @@ -4204,12 +4349,9 @@ int ath12k_wmi_pdev_lro_cfg(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send lro cfg req wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4279,6 +4421,9 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) ab->wmi_ab.svc_map)) arg.res_cfg.is_reg_cc_ext_event_supported = true; + if (test_bit(WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT, ab->wmi_ab.svc_map)) + arg.res_cfg.is_wds_null_frame_supported = true; + ab->hw_params->wmi_init(ab, &arg.res_cfg); ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; @@ -4340,12 +4485,9 @@ int ath12k_wmi_vdev_spectral_conf(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send spectral scan config wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4377,12 +4519,9 @@ int ath12k_wmi_vdev_spectral_enable(struct ath12k *ar, u32 vdev_id, if (ret) { ath12k_warn(ar->ab, "failed to send spectral enable wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4423,12 +4562,9 @@ int ath12k_wmi_pdev_dma_ring_cfg(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send dma ring cfg req wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -5032,6 +5168,7 @@ static void ath12k_wmi_eht_caps_parse(struct ath12k_pdev *pdev, u32 band, __le32 cap_info_internal) { struct ath12k_band_cap *cap_band = &pdev->cap.band[band]; + u8 *phy_cap = (u8 *)&cap_band->eht_cap_phy_info[0]; u32 support_320mhz; u8 i; @@ -5045,8 +5182,22 @@ static void ath12k_wmi_eht_caps_parse(struct ath12k_pdev *pdev, u32 band, for (i = 0; i < WMI_MAX_EHTCAP_PHY_SIZE; i++) cap_band->eht_cap_phy_info[i] = le32_to_cpu(cap_phy_info[i]); - if (band == NL80211_BAND_6GHZ) + if (band == NL80211_BAND_6GHZ) { cap_band->eht_cap_phy_info[0] |= support_320mhz; + } else { + /* + * Firmware may report 6 GHz/320 MHz specific capabilities for + * non-6 GHz bands, so explicitly clear them. + */ + phy_cap[0] &= ~IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + phy_cap[1] &= ~IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK; + phy_cap[2] &= ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; + phy_cap[3] &= ~IEEE80211_EHT_PHY_CAP3_SOUNDING_DIM_320MHZ_MASK; + phy_cap[6] &= ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ; + phy_cap[6] &= ~IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP; + phy_cap[7] &= ~IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ; + phy_cap[7] &= ~IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ; + } cap_band->eht_mcs_20_only = le32_to_cpu(supp_mcs[0]); cap_band->eht_mcs_80 = le32_to_cpu(supp_mcs[1]); @@ -5716,7 +5867,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf const struct wmi_vdev_start_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5726,13 +5877,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev start resp ev"); - kfree(tb); return -EPROTO; } *vdev_rsp = *ev; - kfree(tb); return 0; } @@ -5811,7 +5960,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5821,7 +5970,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n"); - kfree(tb); return -EPROTO; } @@ -5851,7 +5999,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) { ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n", num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5861,7 +6008,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n", i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5886,14 +6032,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n", i); - kfree(tb); return -EINVAL; } } if (!total_reg_rules) { ath12k_warn(ab, "No reg rules available\n"); - kfree(tb); return -EINVAL; } @@ -5995,7 +6139,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_2g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); return -ENOMEM; } @@ -6029,7 +6172,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_5g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); return -ENOMEM; } @@ -6048,7 +6190,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_ap_ptr[i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n"); return -ENOMEM; } @@ -6063,7 +6204,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_client_ptr[j][i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n"); return -ENOMEM; } @@ -6098,7 +6238,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n"); - kfree(tb); return 0; } @@ -6109,7 +6248,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * const struct wmi_peer_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6119,7 +6258,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer delete resp ev"); - kfree(tb); return -EPROTO; } @@ -6129,7 +6267,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ether_addr_copy(peer_del_resp->peer_macaddr.addr, ev->peer_macaddr.addr); - kfree(tb); return 0; } @@ -6141,7 +6278,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, const struct wmi_vdev_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6151,13 +6288,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev delete resp ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6169,7 +6304,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, const struct wmi_bcn_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6179,14 +6314,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch bcn tx status ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); *tx_status = le32_to_cpu(ev->tx_status); - kfree(tb); return 0; } @@ -6197,7 +6330,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ const struct wmi_vdev_stopped_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6207,13 +6340,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ ev = tb[WMI_TAG_VDEV_STOPPED_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev stop ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6352,7 +6483,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, const struct wmi_mgmt_tx_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6362,7 +6493,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mgmt tx compl ev"); - kfree(tb); return -EPROTO; } @@ -6372,7 +6502,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, param->ppdu_id = ev->ppdu_id; param->ack_rssi = ev->ack_rssi; - kfree(tb); return 0; } @@ -6535,7 +6664,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_scan_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6545,7 +6674,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_SCAN_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch scan ev"); - kfree(tb); return -EPROTO; } @@ -6557,7 +6685,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, scan_evt_param->vdev_id = ev->vdev_id; scan_evt_param->tsf_timestamp = ev->tsf_timestamp; - kfree(tb); return 0; } @@ -6568,7 +6695,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf const struct wmi_peer_sta_kickout_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6578,7 +6705,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer sta kickout ev"); - kfree(tb); return -EPROTO; } @@ -6586,7 +6712,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf arg->reason = le32_to_cpu(ev->reason); arg->rssi = le32_to_cpu(ev->rssi); - kfree(tb); return 0; } @@ -6597,7 +6722,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_roam_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6607,7 +6732,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_ROAM_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch roam ev"); - kfree(tb); return -EPROTO; } @@ -6615,20 +6739,15 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, roam_ev->reason = ev->reason; roam_ev->rssi = ev->rssi; - kfree(tb); return 0; } -static int freq_to_idx(struct ath12k *ar, int freq) +static int freq_to_idx(struct ieee80211_hw *hw, int freq) { struct ieee80211_supported_band *sband; - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); int band, ch, idx = 0; for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { - if (!ar->mac.sbands[band].channels) - continue; - sband = hw->wiphy->bands[band]; if (!sband) continue; @@ -6649,7 +6768,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6659,7 +6778,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch chan info ev"); - kfree(tb); return -EPROTO; } @@ -6676,7 +6794,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; ch_info_ev->vdev_id = ev->vdev_id; - kfree(tb); return 0; } @@ -6688,7 +6805,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_pdev_bss_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6698,7 +6815,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev bss chan info ev"); - kfree(tb); return -EPROTO; } @@ -6716,7 +6832,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low; bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high; - kfree(tb); return 0; } @@ -6728,7 +6843,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_vdev_install_key_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6738,7 +6853,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev install key compl ev"); - kfree(tb); return -EPROTO; } @@ -6748,7 +6862,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk arg->key_flags = le32_to_cpu(ev->key_flags); arg->status = le32_to_cpu(ev->status); - kfree(tb); return 0; } @@ -6759,7 +6872,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff const struct wmi_peer_assoc_conf_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6769,39 +6882,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer assoc conf ev"); - kfree(tb); return -EPROTO; } peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id); peer_assoc_conf->macaddr = ev->peer_macaddr.addr; - kfree(tb); - return 0; -} - -static int -ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, struct sk_buff *skb, - const struct wmi_pdev_temperature_event *ev) -{ - const void **tb; - int ret; - - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); - if (IS_ERR(tb)) { - ret = PTR_ERR(tb); - ath12k_warn(ab, "failed to parse tlv: %d\n", ret); - return ret; - } - - ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT]; - if (!ev) { - ath12k_warn(ab, "failed to fetch pdev temp ev"); - kfree(tb); - return -EPROTO; - } - - kfree(tb); return 0; } @@ -6819,7 +6905,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6828,7 +6914,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; if (!ev) { - kfree(tb); ath12k_warn(ab, "failed to fetch 11d new cc ev"); return -EPROTO; } @@ -6841,8 +6926,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ab->new_alpha2[0], ab->new_alpha2[1]); - kfree(tb); - for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; @@ -7014,25 +7097,29 @@ static void ath12k_peer_delete_resp_event(struct ath12k_base *ab, struct sk_buff { struct wmi_peer_delete_resp_event peer_del_resp; struct ath12k *ar; + u32 vdev_id; if (ath12k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) { - ath12k_warn(ab, "failed to extract peer delete resp"); + ath12k_warn(ab, "failed to extract peer delete resp\n"); return; } + vdev_id = le32_to_cpu(peer_del_resp.vdev_id); + rcu_read_lock(); - ar = ath12k_mac_get_ar_by_vdev_id(ab, le32_to_cpu(peer_del_resp.vdev_id)); + ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id); if (!ar) { - ath12k_warn(ab, "invalid vdev id in peer delete resp ev %d", - peer_del_resp.vdev_id); + ath12k_warn(ab, "invalid vdev id in peer delete resp ev %d\n", + vdev_id); rcu_read_unlock(); return; } - complete(&ar->peer_delete_done); + ath12k_peer_delete_resp_signal(ar, vdev_id, + peer_del_resp.peer_macaddr.addr); rcu_read_unlock(); ath12k_dbg(ab, ATH12K_DBG_WMI, "peer delete resp for vdev id %d addr %pM\n", - peer_del_resp.vdev_id, peer_del_resp.peer_macaddr.addr); + vdev_id, peer_del_resp.peer_macaddr.addr); } static void ath12k_vdev_delete_resp_event(struct ath12k_base *ab, @@ -7173,7 +7260,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) struct ath12k_wmi_mgmt_rx_arg rx_ev = {}; struct ath12k *ar; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_sta *pubsta = NULL; + struct ath12k_dp_link_peer *peer; struct ieee80211_hdr *hdr; + bool is_4addr_null_pkt; + struct ath12k_dp *dp; u16 fc; struct ieee80211_supported_band *sband; s32 noise_floor; @@ -7248,6 +7339,38 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); + is_4addr_null_pkt = (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) && + ieee80211_has_a4(hdr->frame_control); + + /* + * Add check to drop frames other than 4-address NULL frame. Since + * firmware sends all NULL frames in this path (3-address and 4-address) + */ + if (ieee80211_is_data(hdr->frame_control) && !is_4addr_null_pkt) { + dev_kfree_skb(skb); + goto exit; + } + + if (is_4addr_null_pkt) { + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_pdev_and_addr(dp, ar->pdev_idx, + hdr->addr2); + if (!peer) { + spin_unlock_bh(&dp->dp_lock); + dev_kfree_skb(skb); + goto exit; + } + pubsta = peer->sta; + if (pubsta && pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + spin_unlock_bh(&dp->dp_lock); + goto send_rx; + } + /* Firmware is guaranteed to report all essential management frames via * WMI while it can deliver some extra via HTT. Since there can be * duplicates split the reporting wrt monitor/sniffing. @@ -7271,6 +7394,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (ieee80211_is_beacon(hdr->frame_control)) ath12k_mac_handle_beacon(ar, skb); +send_rx: ath12k_dbg(ab, ATH12K_DBG_MGMT, "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, @@ -7534,6 +7658,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_chan_info_event ch_info_ev = {}; struct ath12k *ar; + struct ath12k_hw *ah; struct survey_info *survey; int idx; /* HW channel counters frequency value in hertz */ @@ -7565,6 +7690,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) return; } spin_lock_bh(&ar->data_lock); + ah = ath12k_ar_to_ah(ar); switch (ar->scan.state) { case ATH12K_SCAN_IDLE: @@ -7576,8 +7702,8 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) break; } - idx = freq_to_idx(ar, le32_to_cpu(ch_info_ev.freq)); - if (idx >= ARRAY_SIZE(ar->survey)) { + idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(ch_info_ev.freq)); + if (idx >= ARRAY_SIZE(ah->survey)) { ath12k_warn(ab, "chan info: invalid frequency %d (idx %d out of bounds)\n", ch_info_ev.freq, idx); goto exit; @@ -7590,14 +7716,20 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) cc_freq_hz = (le32_to_cpu(ch_info_ev.mac_clk_mhz) * 1000); if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) { - survey = &ar->survey[idx]; - memset(survey, 0, sizeof(*survey)); - survey->noise = le32_to_cpu(ch_info_ev.noise_floor); - survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY; - survey->time = div_u64(le32_to_cpu(ch_info_ev.cycle_count), cc_freq_hz); - survey->time_busy = div_u64(le32_to_cpu(ch_info_ev.rx_clear_count), - cc_freq_hz); + scoped_guard(spinlock_bh, &ah->survey_lock) { + survey = &ah->survey[idx]; + memset(survey, 0, sizeof(*survey)); + survey->noise = le32_to_cpu(ch_info_ev.noise_floor); + survey->time = + div_u64(le32_to_cpu(ch_info_ev.cycle_count), + cc_freq_hz); + survey->time_busy = + div_u64(le32_to_cpu(ch_info_ev.rx_clear_count), + cc_freq_hz); + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + } } exit: spin_unlock_bh(&ar->data_lock); @@ -7610,6 +7742,7 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) struct wmi_pdev_bss_chan_info_event bss_ch_info_ev = {}; struct survey_info *survey; struct ath12k *ar; + struct ath12k_hw *ah; u32 cc_freq_hz = ab->cc_freq_hz; u64 busy, total, tx, rx, rx_bss; int idx; @@ -7650,28 +7783,31 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb) return; } - spin_lock_bh(&ar->data_lock); - idx = freq_to_idx(ar, le32_to_cpu(bss_ch_info_ev.freq)); - if (idx >= ARRAY_SIZE(ar->survey)) { + ah = ath12k_ar_to_ah(ar); + + idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(bss_ch_info_ev.freq)); + if (idx >= ARRAY_SIZE(ah->survey)) { ath12k_warn(ab, "bss chan info: invalid frequency %d (idx %d out of bounds)\n", bss_ch_info_ev.freq, idx); goto exit; } - survey = &ar->survey[idx]; + scoped_guard(spinlock_bh, &ah->survey_lock) { + survey = &ah->survey[idx]; + + survey->noise = le32_to_cpu(bss_ch_info_ev.noise_floor); + survey->time = div_u64(total, cc_freq_hz); + survey->time_busy = div_u64(busy, cc_freq_hz); + survey->time_rx = div_u64(rx_bss, cc_freq_hz); + survey->time_tx = div_u64(tx, cc_freq_hz); + survey->filled |= (SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX); + } - survey->noise = le32_to_cpu(bss_ch_info_ev.noise_floor); - survey->time = div_u64(total, cc_freq_hz); - survey->time_busy = div_u64(busy, cc_freq_hz); - survey->time_rx = div_u64(rx_bss, cc_freq_hz); - survey->time_tx = div_u64(tx, cc_freq_hz); - survey->filled |= (SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY | - SURVEY_INFO_TIME_RX | - SURVEY_INFO_TIME_TX); exit: - spin_unlock_bh(&ar->data_lock); complete(&ar->bss_survey_done); rcu_read_unlock(); @@ -8243,8 +8379,6 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, struct ath12k_fw_stats *stats = parse->stats; struct ath12k *ar; struct ath12k_link_vif *arvif; - struct ieee80211_sta *sta; - struct ath12k_sta *ahsta; struct ath12k_link_sta *arsta; int i, ret = 0; const void *data = ptr; @@ -8280,21 +8414,19 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, arvif = ath12k_mac_get_arvif(ar, le32_to_cpu(src->vdev_id)); if (arvif) { - sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), - arvif->bssid, - NULL); - if (sta) { - ahsta = ath12k_sta_to_ahsta(sta); - arsta = &ahsta->deflink; + spin_lock_bh(&ab->base_lock); + arsta = ath12k_link_sta_find_by_addr(ab, arvif->bssid); + if (arsta) { arsta->rssi_beacon = le32_to_cpu(src->beacon_snr); ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi stats vdev id %d snr %d\n", src->vdev_id, src->beacon_snr); } else { - ath12k_dbg(ab, ATH12K_DBG_WMI, - "not found station bssid %pM for vdev stat\n", - arvif->bssid); + ath12k_warn(ab, + "not found link sta with bssid %pM for vdev stat\n", + arvif->bssid); } + spin_unlock_bh(&ab->base_lock); } data += sizeof(*src); @@ -8365,8 +8497,6 @@ static int ath12k_wmi_tlv_rssi_chain_parse(struct ath12k_base *ab, struct ath12k_fw_stats *stats = parse->stats; struct ath12k_link_vif *arvif; struct ath12k_link_sta *arsta; - struct ieee80211_sta *sta; - struct ath12k_sta *ahsta; struct ath12k *ar; int vdev_id; int j; @@ -8402,19 +8532,15 @@ static int ath12k_wmi_tlv_rssi_chain_parse(struct ath12k_base *ab, "stats bssid %pM vif %p\n", arvif->bssid, arvif->ahvif->vif); - sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), - arvif->bssid, - NULL); - if (!sta) { - ath12k_dbg(ab, ATH12K_DBG_WMI, - "not found station of bssid %pM for rssi chain\n", - arvif->bssid); + guard(spinlock_bh)(&ab->base_lock); + arsta = ath12k_link_sta_find_by_addr(ab, arvif->bssid); + if (!arsta) { + ath12k_warn(ab, + "not found link sta with bssid %pM for rssi chain\n", + arvif->bssid); return -EPROTO; } - ahsta = ath12k_sta_to_ahsta(sta); - arsta = &ahsta->deflink; - BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ARRAY_SIZE(stats_rssi->rssi_avg_beacon)); @@ -8594,7 +8720,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, const struct wmi_pdev_ctl_failsafe_chk_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8604,7 +8730,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev"); - kfree(tb); return; } @@ -8618,8 +8743,330 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, if (ev->ctl_failsafe_status != 0) ath12k_warn(ab, "pdev ctl failsafe failure status %d", ev->ctl_failsafe_status); +} + +static int +ath12k_wmi_incumbent_signal_interference_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, + void *data) +{ + const struct ath12k_wmi_incumbent_signal_interference_params *info; + struct ath12k_wmi_incumbent_signal_interference_arg *arg = data; + + switch (tag) { + case WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE: + if (len < sizeof(*info)) { + ath12k_warn(ab, + "DCS incumbent signal interference subtlv 0x%x invalid len %u\n", + tag, len); + return -EINVAL; + } + + info = ptr; + + arg->chan_width = le32_to_cpu(info->chan_width); + arg->chan_freq = le32_to_cpu(info->chan_freq); + arg->center_freq0 = le32_to_cpu(info->center_freq0); + arg->center_freq1 = le32_to_cpu(info->center_freq1); + arg->chan_bw_interference_bitmap = + le32_to_cpu(info->chan_bw_interference_bitmap); + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference chan width %u freq %u center_freq0 %u center_freq1 %u bitmap 0x%x\n", + arg->chan_width, arg->chan_freq, + arg->center_freq0, arg->center_freq1, + arg->chan_bw_interference_bitmap); + break; + default: + ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference in subtlvs\n", + tag); + return -EINVAL; + } + + return 0; +} + +static int ath12k_wmi_dcs_interference_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + int ret = 0; + + switch (tag) { + case WMI_TAG_DCS_INTERFERENCE_EVENT: + /* Fixed param should already be processed */ + break; + case WMI_TAG_ARRAY_STRUCT: + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_incumbent_signal_interference_subtlv_parser, + data); + break; + default: + ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference event\n", + tag); + ret = -EINVAL; + break; + } + + return ret; +} + +static bool +ath12k_wmi_validate_interference_info(struct ath12k *ar, + struct ath12k_wmi_incumbent_signal_interference_arg *info) +{ + switch (info->chan_width) { + case WMI_CHAN_WIDTH_20: + if (info->chan_bw_interference_bitmap > ATH12K_WMI_DCS_SEG_PRI20) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 20 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_40: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 40 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_80: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 80 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_160: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40 | + ATH12K_WMI_DCS_SEG_SEC80)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 160 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_320: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40 | + ATH12K_WMI_DCS_SEG_SEC80 | + ATH12K_WMI_DCS_SEG_SEC160)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 320 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + default: + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with unknown channel width %u", + info->chan_width); + return false; + } + return true; +} + +static u32 +ath12k_wmi_transform_interference_bitmap(int input_bitmap, + struct cfg80211_chan_def *chandef) +{ + u16 output_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {}; + u16 input_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {}; + u32 start_freq, segment_freq; + int primary_index = -1; + u32 output_bitmap = 0; + u16 num_sub_chans; + int bandwidth; + + bandwidth = nl80211_chan_width_to_mhz(chandef->width); + if (bandwidth < 0) + return 0; + + /* + * Firmware reports bit 0 as primary 20 MHz irrespective of absolute + * frequency position. Convert to standardized lowest-to-highest 20 MHz + * ordering expected by cfg80211/mac80211 userspace consumers. + */ + num_sub_chans = bandwidth / 20; + start_freq = (chandef->center_freq1 - bandwidth / 2) + 10; + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; i++) { + segment_freq = start_freq + (i * 20); + if (segment_freq == chandef->chan->center_freq) { + primary_index = i; + break; + } + } + if (primary_index == -1) + return 0; + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i) + input_bits[i] = BIT(i) & input_bitmap; + + for (int i = 0; i < num_sub_chans; ++i) { + int src = i, dst = i; + + switch (bandwidth) { + case 40: + if (primary_index == 1) + dst = 1 - i; + break; + case 80: + dst = intf_map_80[primary_index][i]; + break; + case 160: + dst = intf_map_160[primary_index][i]; + break; + case 320: + dst = intf_map_320[primary_index][i]; + break; + } + output_bits[dst] = input_bits[src]; + } + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i) + output_bitmap |= output_bits[i] ? BIT(i) : 0; + + return output_bitmap; +} + +static void +ath12k_wmi_process_incumbent_signal_interference_evt(struct ath12k_base *ab, + struct sk_buff *skb, + const struct ath12k_wmi_intf_arg *intf_arg) +{ + struct ath12k_wmi_incumbent_signal_interference_arg info = {}; + struct ath12k_incumbent_signal_interference *incumbent; + struct ath12k_mac_get_any_chanctx_conf_arg arg; + u32 transformed_intf_bitmap; + struct ieee80211_hw *hw; + struct ath12k *ar; + int ret; + + guard(rcu)(); + + ar = ath12k_mac_get_ar_by_pdev_id(ab, intf_arg->pdev_id); + if (!ar) { + ath12k_warn(ab, "incumbent signal interference detected on invalid pdev %d\n", + intf_arg->pdev_id); + return; + } + if (!ar->supports_6ghz) { + ath12k_warn(ab, "pdev does not support 6 GHz, dropping DCS interference event\n"); + return; + } + + incumbent = &ar->incumbent_signal_interference; + spin_lock_bh(&ar->data_lock); + if (incumbent->handling_in_progress) { + spin_unlock_bh(&ar->data_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "incumbent signal interference handling ongoing, dropping DCS interference event"); + return; + } + spin_unlock_bh(&ar->data_lock); + + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_dcs_interference_event_parser, + &info); + if (ret) { + ath12k_warn(ab, + "failed to parse incumbent signal interference TLV. Error %d\n", + ret); + return; + } + + if (!ath12k_wmi_validate_interference_info(ar, &info)) { + ath12k_warn(ab, "invalid DCS incumbent signal interference TLV - Skipping event"); + return; + } + + arg.ar = ar; + arg.chanctx_conf = NULL; + hw = ath12k_ar_to_hw(ar); + ieee80211_iter_chan_contexts_atomic(hw, + ath12k_mac_get_any_chanctx_conf_iter, + &arg); + if (!arg.chanctx_conf) { + ath12k_warn(ab, "failed to find valid chanctx_conf in incumbent signal intf detected event\n"); + return; + } + + if (info.chan_freq != arg.chanctx_conf->def.chan->center_freq) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "dcs interference event received with wrong channel %d (ctx freq %d)", + info.chan_freq, arg.chanctx_conf->def.chan->center_freq); + return; + } + + spin_lock_bh(&ar->data_lock); + incumbent->center_freq = arg.chanctx_conf->def.chan->center_freq; + incumbent->width = arg.chanctx_conf->def.width; + incumbent->chan_bw_interference_bitmap = info.chan_bw_interference_bitmap; + incumbent->handling_in_progress = true; + spin_unlock_bh(&ar->data_lock); + transformed_intf_bitmap = + ath12k_wmi_transform_interference_bitmap(info.chan_bw_interference_bitmap, + &arg.chanctx_conf->def); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference bitmap 0x%x (transformed 0x%x)\n", + info.chan_bw_interference_bitmap, transformed_intf_bitmap); + cfg80211_incumbent_signal_notify(hw->wiphy, + &arg.chanctx_conf->def, + transformed_intf_bitmap, + GFP_ATOMIC); +} + +static void +ath12k_wmi_dcs_interference_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct ath12k_wmi_dcs_interference_ev_fixed_params *dcs_intf_ev; + struct ath12k_wmi_intf_arg dcs_intf_arg; + const struct wmi_tlv *tlv; + u16 tlv_tag; + u8 *ptr; + + if (skb->len < (sizeof(*dcs_intf_ev) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "DCS interference event is of incorrect length\n"); + return; + } + + ptr = skb->data; + tlv = (struct wmi_tlv *)ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_DCS_INTERFERENCE_EVENT) { + ath12k_warn(ab, "DCS interference event received with wrong tag\n"); + return; + } - kfree(tb); + dcs_intf_ev = (struct ath12k_wmi_dcs_interference_ev_fixed_params *)ptr; + + dcs_intf_arg.interference_type = + le32_to_cpu(dcs_intf_ev->interference_type); + dcs_intf_arg.pdev_id = le32_to_cpu(dcs_intf_ev->pdev_id); + + if (dcs_intf_arg.interference_type == + ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference (Type %u) detected on pdev %u.", + dcs_intf_arg.interference_type, + dcs_intf_arg.pdev_id); + ath12k_wmi_process_incumbent_signal_interference_evt(ab, skb, + &dcs_intf_arg); + } } static void @@ -8691,7 +9138,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, const u32 *vdev_ids; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8703,7 +9150,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, if (!ev || !vdev_ids) { ath12k_warn(ab, "failed to fetch pdev csa switch count ev"); - kfree(tb); return; } @@ -8713,8 +9159,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, ev->num_vdevs); ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids); - - kfree(tb); } static void @@ -8726,7 +9170,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff struct ath12k *ar; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8737,7 +9181,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff if (!ev) { ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev"); - kfree(tb); return; } @@ -8776,8 +9219,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff exit: rcu_read_unlock(); - - kfree(tb); } static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, @@ -8788,7 +9229,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, int ret; u16 length; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); @@ -8799,43 +9240,93 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, ev = tb[WMI_TAG_ARRAY_BYTE]; if (!ev) { ath12k_warn(ab, "failed to fetch ftm msg\n"); - kfree(tb); return; } length = skb->len - TLV_HDR_SIZE; ath12k_tm_process_event(ab, cmd_id, ev, length); - kfree(tb); - tb = NULL; } static void ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, struct sk_buff *skb) { + const struct wmi_pdev_temperature_event *ev; struct ath12k *ar; - struct wmi_pdev_temperature_event ev = {}; + const void **tb; + int temp; + u32 pdev_id; - if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) { - ath12k_warn(ab, "failed to extract pdev temperature event"); + tb = ath12k_wmi_tlv_parse(ab, skb); + if (IS_ERR(tb)) { + ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb)); return; } + ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT]; + if (!ev) { + ath12k_warn(ab, "failed to fetch pdev temp ev\n"); + return; + } + + temp = a_sle32_to_cpu(ev->temp); + pdev_id = le32_to_cpu(ev->pdev_id); + ath12k_dbg(ab, ATH12K_DBG_WMI, - "pdev temperature ev temp %d pdev_id %d\n", ev.temp, ev.pdev_id); + "pdev temperature ev temp %d pdev_id %u\n", + temp, pdev_id); rcu_read_lock(); - ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev.pdev_id)); + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); if (!ar) { - ath12k_warn(ab, "invalid pdev id in pdev temperature ev %d", ev.pdev_id); + ath12k_warn(ab, "invalid pdev id %u in pdev temperature ev\n", + pdev_id); goto exit; } + ath12k_thermal_event_temperature(ar, temp); + exit: rcu_read_unlock(); } +static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_therm_throt_stats_event *ev; + struct ath12k *ar; + const void **tb; + + tb = ath12k_wmi_tlv_parse(ab, skb); + if (IS_ERR(tb)) { + ath12k_err(ab, "failed to parse thermal throttling stats tlv: %ld\n", + PTR_ERR(tb)); + return; + } + + ev = tb[WMI_TAG_THERM_THROT_STATS_EVENT]; + if (!ev) { + ath12k_err(ab, "failed to fetch thermal throt stats ev\n"); + return; + } + + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id)); + if (!ar) { + ath12k_warn(ab, "received thermal_throt_stats in invalid pdev %u\n", + le32_to_cpu(ev->pdev_id)); + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "thermal stats ev level %u pdev_id %u temp %u throt_levels %u\n", + le32_to_cpu(ev->level), le32_to_cpu(ev->pdev_id), + le32_to_cpu(ev->temp), le32_to_cpu(ev->therm_throt_levels)); +} + static void ath12k_fils_discovery_event(struct ath12k_base *ab, struct sk_buff *skb) { @@ -8843,7 +9334,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, const struct wmi_fils_discovery_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8855,15 +9346,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, ev = tb[WMI_TAG_HOST_SWFDA_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch FILS discovery event\n"); - kfree(tb); return; } ath12k_warn(ab, "FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n", ev->vdev_id, ev->fils_tt, ev->tbtt); - - kfree(tb); } static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, @@ -8873,7 +9361,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, const struct wmi_probe_resp_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8886,7 +9374,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, if (!ev) { ath12k_warn(ab, "failed to fetch probe response transmission status event"); - kfree(tb); return; } @@ -8894,8 +9381,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, ath12k_warn(ab, "Probe response transmission failed for vdev_id %u, status %u\n", ev->vdev_id, ev->tx_status); - - kfree(tb); } static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, @@ -8907,7 +9392,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, struct ath12k *ar; int ret, vdev_id; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret); @@ -8917,10 +9402,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, ev = tb[WMI_TAG_P2P_NOA_EVENT]; noa = tb[WMI_TAG_P2P_NOA_INFO]; - if (!ev || !noa) { - ret = -EPROTO; - goto out; - } + if (!ev || !noa) + return -EPROTO; vdev_id = __le32_to_cpu(ev->vdev_id); @@ -8943,8 +9426,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, unlock: rcu_read_unlock(); -out: - kfree(tb); return ret; } @@ -8955,7 +9436,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8963,10 +9444,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, } ev = tb[WMI_TAG_RFKILL_EVENT]; - if (!ev) { - kfree(tb); + if (!ev) return; - } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", @@ -8979,7 +9458,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, spin_unlock_bh(&ab->base_lock); queue_work(ab->workqueue, &ab->rfkill_work); - kfree(tb); } static void @@ -8995,7 +9473,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, const struct wmi_twt_enable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt enable status event tlv: %d\n", @@ -9006,15 +9484,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt enable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, @@ -9024,7 +9499,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, const struct wmi_twt_disable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt disable status event tlv: %d\n", @@ -9035,15 +9510,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt disable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab, @@ -9116,7 +9588,7 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -9126,7 +9598,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch gtk offload status ev"); - kfree(tb); return; } @@ -9136,7 +9607,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, rcu_read_unlock(); ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n", le32_to_cpu(ev->vdev_id)); - kfree(tb); return; } @@ -9152,8 +9622,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, (void *)&replay_ctr_be, GFP_ATOMIC); rcu_read_unlock(); - - kfree(tb); } static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, @@ -9165,7 +9633,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n", @@ -9176,7 +9644,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mlo setup complete event\n"); - kfree(tb); return; } @@ -9195,14 +9662,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, if (!ar) { ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n", ev->pdev_id, ev->status); - goto out; + return; } ar->mlo_setup_status = le32_to_cpu(ev->status); complete(&ar->mlo_setup_done); - -out: - kfree(tb); } static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, @@ -9212,7 +9676,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret); @@ -9220,13 +9684,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, } ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE]; - if (!ev) { + if (!ev) ath12k_warn(ab, "failed to fetch teardown complete event\n"); - kfree(tb); - return; - } - - kfree(tb); } #ifdef CONFIG_ATH12K_DEBUGFS @@ -9796,7 +10255,7 @@ static void ath12k_wmi_rssi_dbm_conversion_params_info_event(struct ath12k_base *ab, struct sk_buff *skb) { - struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info; + struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info = {}; struct ath12k *ar; s32 noise_floor; u32 pdev_id; @@ -9840,12 +10299,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) struct wmi_cmd_hdr *cmd_hdr; enum wmi_tlv_event_id id; - cmd_hdr = (struct wmi_cmd_hdr *)skb->data; - id = le32_get_bits(cmd_hdr->cmd_id, WMI_CMD_HDR_CMD_ID); - - if (!skb_pull(skb, sizeof(struct wmi_cmd_hdr))) + cmd_hdr = skb_pull_data(skb, sizeof(*cmd_hdr)); + if (!cmd_hdr) goto out; + id = le32_get_bits(cmd_hdr->cmd_id, WMI_CMD_HDR_CMD_ID); + switch (id) { /* Process all the WMI events here */ case WMI_SERVICE_READY_EVENTID: @@ -9918,6 +10377,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_PDEV_TEMPERATURE_EVENTID: ath12k_wmi_pdev_temperature_event(ab, skb); break; + case WMI_THERM_THROT_STATS_EVENTID: + ath12k_wmi_thermal_throt_stats_event(ab, skb); + break; case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID: ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb); break; @@ -9972,6 +10434,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: ath12k_wmi_obss_color_collision_event(ab, skb); break; + case WMI_DCS_INTERFERENCE_EVENTID: + ath12k_wmi_dcs_interference_event(ab, skb); + break; /* add Unsupported events (rare) here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: @@ -10035,50 +10500,46 @@ static int ath12k_connect_pdev_htc_service(struct ath12k_base *ab, static int ath12k_wmi_send_unit_test_cmd(struct ath12k *ar, - struct wmi_unit_test_cmd ut_cmd, - u32 *test_args) + const struct wmi_unit_test_arg *ut) { struct ath12k_wmi_pdev *wmi = ar->wmi; struct wmi_unit_test_cmd *cmd; + int buf_len, arg_len; struct sk_buff *skb; struct wmi_tlv *tlv; + __le32 *ut_cmd_args; void *ptr; - u32 *ut_cmd_args; - int buf_len, arg_len; int ret; int i; - arg_len = sizeof(u32) * le32_to_cpu(ut_cmd.num_args); - buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE; + arg_len = sizeof(*ut_cmd_args) * ut->num_args; + buf_len = sizeof(*cmd) + arg_len + TLV_HDR_SIZE; skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); if (!skb) return -ENOMEM; - cmd = (struct wmi_unit_test_cmd *)skb->data; + ptr = skb->data; + cmd = ptr; cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_UNIT_TEST_CMD, - sizeof(ut_cmd)); - - cmd->vdev_id = ut_cmd.vdev_id; - cmd->module_id = ut_cmd.module_id; - cmd->num_args = ut_cmd.num_args; - cmd->diag_token = ut_cmd.diag_token; - - ptr = skb->data + sizeof(ut_cmd); + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(ut->vdev_id); + cmd->module_id = cpu_to_le32(ut->module_id); + cmd->num_args = cpu_to_le32(ut->num_args); + cmd->diag_token = cpu_to_le32(ut->diag_token); + ptr += sizeof(*cmd); tlv = ptr; tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, arg_len); ptr += TLV_HDR_SIZE; - ut_cmd_args = ptr; - for (i = 0; i < le32_to_cpu(ut_cmd.num_args); i++) - ut_cmd_args[i] = test_args[i]; + for (i = 0; i < ut->num_args; i++) + ut_cmd_args[i] = cpu_to_le32(ut->args[i]); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI unit test : module %d vdev %d n_args %d token %d\n", - cmd->module_id, cmd->vdev_id, cmd->num_args, - cmd->diag_token); + ut->module_id, ut->vdev_id, ut->num_args, ut->diag_token); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID); @@ -10094,8 +10555,7 @@ ath12k_wmi_send_unit_test_cmd(struct ath12k *ar, int ath12k_wmi_simulate_radar(struct ath12k *ar) { struct ath12k_link_vif *arvif; - u32 dfs_args[DFS_MAX_TEST_ARGS]; - struct wmi_unit_test_cmd wmi_ut; + struct wmi_unit_test_arg wmi_ut = {}; bool arvif_found = false; list_for_each_entry(arvif, &ar->arvifs, list) { @@ -10108,22 +10568,23 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) if (!arvif_found) return -EINVAL; - dfs_args[DFS_TEST_CMDID] = 0; - dfs_args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; - /* Currently we could pass segment_id(b0 - b1), chirp(b2) + wmi_ut.args[DFS_TEST_CMDID] = 0; + wmi_ut.args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; + /* + * Currently we could pass segment_id(b0 - b1), chirp(b2) * freq offset (b3 - b10) to unit test. For simulation * purpose this can be set to 0 which is valid. */ - dfs_args[DFS_TEST_RADAR_PARAM] = 0; + wmi_ut.args[DFS_TEST_RADAR_PARAM] = 0; - wmi_ut.vdev_id = cpu_to_le32(arvif->vdev_id); - wmi_ut.module_id = cpu_to_le32(DFS_UNIT_TEST_MODULE); - wmi_ut.num_args = cpu_to_le32(DFS_MAX_TEST_ARGS); - wmi_ut.diag_token = cpu_to_le32(DFS_UNIT_TEST_TOKEN); + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = DFS_UNIT_TEST_MODULE; + wmi_ut.num_args = DFS_MAX_TEST_ARGS; + wmi_ut.diag_token = DFS_UNIT_TEST_TOKEN; ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Triggering Radar Simulation\n"); - return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); + return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut); } int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, @@ -10194,6 +10655,42 @@ int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, return ret; } +int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar, + u32 chan_bw_interference_bitmap) +{ + struct wmi_unit_test_arg wmi_ut = {}; + struct ath12k_link_vif *arvif; + struct ath12k_vif *ahvif; + bool arvif_found = false; + + list_for_each_entry(arvif, &ar->arvifs, list) { + ahvif = arvif->ahvif; + if (arvif->is_started && ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + arvif_found = true; + break; + } + } + + if (!arvif_found) + return -EINVAL; + + wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF] = + ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE; + wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP] = + chan_bw_interference_bitmap; + + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE; + wmi_ut.num_args = ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS; + wmi_ut.diag_token = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN; + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "Triggering incumbent signal interference simulation, interference bitmap: 0x%x\n", + chan_bw_interference_bitmap); + + return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut); +} + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; @@ -10273,7 +10770,7 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a { struct wmi_hw_data_filter_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10297,7 +10794,13 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a "wmi hw data filter enable %d filter_bitmap 0x%x\n", arg->enable, arg->hw_filter_bitmap); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) @@ -10305,6 +10808,7 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) struct wmi_wow_host_wakeup_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10317,14 +10821,20 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_enable(struct ath12k *ar) { struct wmi_wow_enable_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10339,7 +10849,13 @@ int ath12k_wmi_wow_enable(struct ath12k *ar) cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, @@ -10349,6 +10865,7 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, struct wmi_wow_add_del_event_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10365,7 +10882,13 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", wow_wakeup_event(event), enable, vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, @@ -10378,6 +10901,7 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, struct sk_buff *skb; void *ptr; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*tlv) + /* array struct */ @@ -10457,7 +10981,13 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ", bitmap->bitmaskbuf, pattern_len); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) @@ -10465,6 +10995,7 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) struct wmi_wow_del_pattern_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10481,7 +11012,13 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", vdev_id, pattern_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static struct sk_buff * @@ -10617,6 +11154,7 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, struct wmi_pno_scan_req_arg *pno_scan) { struct sk_buff *skb; + int ret; if (pno_scan->enable) skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); @@ -10626,7 +11164,13 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, if (IS_ERR_OR_NULL(skb)) return -ENOMEM; - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static void ath12k_wmi_fill_ns_offload(struct ath12k *ar, @@ -10739,6 +11283,7 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, void *buf_ptr; size_t len; u8 ns_cnt, ns_ext_tuples = 0; + int ret; ns_cnt = offload->ipv6_count; @@ -10774,7 +11319,13 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, if (ns_ext_tuples) ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, @@ -10784,7 +11335,7 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; __le64 replay_ctr; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10811,7 +11362,13 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", arvif->vdev_id, enable); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, @@ -10819,7 +11376,7 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, { struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10833,7 +11390,13 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n", arvif->vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_sta_keepalive(struct ath12k *ar, @@ -10844,6 +11407,7 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, struct wmi_sta_keepalive_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*arp); skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -10871,7 +11435,13 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", arg->vdev_id, arg->enabled, arg->method, arg->interval); - return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params) @@ -10912,10 +11482,9 @@ int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params ath12k_warn(ar->ab, "failed to submit WMI_MLO_SETUP_CMDID command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } int ath12k_wmi_mlo_ready(struct ath12k *ar) @@ -10940,10 +11509,9 @@ int ath12k_wmi_mlo_ready(struct ath12k *ar) ath12k_warn(ar->ab, "failed to submit WMI_MLO_READY_CMDID command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } int ath12k_wmi_mlo_teardown(struct ath12k *ar) @@ -10969,10 +11537,9 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar) ath12k_warn(ar->ab, "failed to submit WMI MLO teardown command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar) @@ -11035,10 +11602,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } static int @@ -11261,3 +11827,31 @@ int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, dev_kfree_skb(skb); return ret; } + +int ath12k_wmi_alloc(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (!ath12k_wmi_tb) { + ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *), + __alignof__(void *)); + if (!ath12k_wmi_tb) + return -ENOMEM; + + refcount_set(&ath12k_wmi_refcount, 1); + } else { + refcount_inc(&ath12k_wmi_refcount); + } + + return 0; +} + +void ath12k_wmi_free(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (refcount_dec_and_test(&ath12k_wmi_refcount)) { + free_percpu(ath12k_wmi_tb); + ath12k_wmi_tb = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index c3844e91afbde..b508aa759bd8d 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -870,6 +870,7 @@ enum wmi_tlv_event_id { WMI_READ_DATA_FROM_FLASH_EVENTID, WMI_REPORT_RX_AGGR_FAILURE_EVENTID, WMI_PKGID_EVENTID, + WMI_THERM_THROT_STATS_EVENTID, WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), WMI_UPLOADH_EVENTID, WMI_CAPTUREH_EVENTID, @@ -1082,6 +1083,7 @@ enum wmi_tlv_pdev_param { WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE, WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE, WMI_PDEV_PARAM_MESH_MCAST_ENABLE, + WMI_PDEV_PARAM_SET_CONG_CTRL_MAX_MSDUS = 0xa6, WMI_PDEV_PARAM_SET_CMD_OBSS_PD_THRESHOLD = 0xbc, WMI_PDEV_PARAM_SET_CMD_OBSS_PD_PER_AC = 0xbe, WMI_PDEV_PARAM_ENABLE_SR_PROHIBIT = 0xc6, @@ -2014,7 +2016,7 @@ enum wmi_tlv_tag { WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE, WMI_TAG_EHT_RATE_SET = 0x3C4, - WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, + WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE = 0x3C5, WMI_TAG_MLO_TX_SEND_PARAMS, WMI_TAG_MLO_PARTNER_LINK_PARAMS, WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC, @@ -2269,11 +2271,18 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, + WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT = 286, + WMI_TLV_SERVICE_11BE = 289, WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361, WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365, + WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410, + WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT = 421, + WMI_TLV_SERVICE_IS_TARGET_IPA = 425, + WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426, + WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429, WMI_TLV_SERVICE_ETH_OFFLOAD = 461, WMI_MAX_EXT2_SERVICE, @@ -2322,11 +2331,31 @@ enum wmi_slot_time { WMI_VDEV_SLOT_TIME_SHORT = 2, }; +enum wmi_dtim_policy { + WMI_DTIM_POLICY_IGNORE = 1, + WMI_DTIM_POLICY_NORMAL = 2, + WMI_DTIM_POLICY_STICK = 3, + WMI_DTIM_POLICY_AUTO = 4, +}; + enum wmi_preamble { WMI_VDEV_PREAMBLE_LONG = 1, WMI_VDEV_PREAMBLE_SHORT = 2, }; +/* + * This will be used to set for WMI_VDEV_PARAM_AP_ENABLE_NAWDS + * whenever 4addr station connects in wds offload case. + * This is for enabling multicast to unicast conversion support in + * firmware + */ +#define WDS_EXT_ENABLE 1 + +enum wmi_peer_4addr_allow_frame { + WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, +}; + enum wmi_peer_smps_state { WMI_PEER_SMPS_PS_NONE = 0, WMI_PEER_SMPS_STATIC = 1, @@ -2491,6 +2520,7 @@ struct ath12k_wmi_resource_config_arg { u32 ema_max_vap_cnt; u32 ema_max_profile_period; bool is_reg_cc_ext_event_supported; + bool is_wds_null_frame_supported; }; struct ath12k_wmi_init_cmd_arg { @@ -2547,6 +2577,8 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) +#define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) +#define WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT BIT(22) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { @@ -2931,10 +2963,13 @@ struct wmi_vdev_create_mlo_params { #define ATH12K_WMI_FLAG_MLO_FORCED_INACTIVE BIT(7) #define ATH12K_WMI_FLAG_MLO_LINK_ADD BIT(8) #define ATH12K_WMI_FLAG_MLO_START_AS_ACTIVE BIT(17) +#define ATH12K_WMI_FLAG_MLO_IEEE_LINK_IDX_VALID BIT(18) +#define ATH12K_WMI_FLAG_MLO_IEEE_LINK_IDX_VALID_PARTNER BIT(19) struct wmi_vdev_start_mlo_params { __le32 tlv_header; __le32 flags; + __le32 ieee_link_id; } __packed; struct wmi_partner_link_info { @@ -2942,6 +2977,8 @@ struct wmi_partner_link_info { __le32 vdev_id; __le32 hw_link_id; struct ath12k_wmi_mac_addr_params vdev_addr; + __le32 flags; + __le32 ieee_link_id; } __packed; struct wmi_vdev_delete_cmd { @@ -3097,6 +3134,7 @@ struct wmi_ml_partner_info { bool primary_umac; bool logical_link_idx_valid; u32 logical_link_idx; + u32 ieee_link_id; }; struct wmi_ml_arg { @@ -3104,6 +3142,7 @@ struct wmi_ml_arg { bool assoc_link; bool mcast_link; bool link_add; + u32 ieee_link_id; u8 num_partner_links; struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS]; }; @@ -3526,6 +3565,16 @@ struct ath12k_wmi_hint_bssid_arg { struct ath12k_wmi_mac_addr_params bssid; }; +struct ath12k_wmi_hint_short_ssid_params { + __le32 freq_flags; + __le32 short_ssid; +}; + +struct ath12k_wmi_hint_bssid_params { + __le32 freq_flags; + struct ath12k_wmi_mac_addr_params bssid; +}; + struct ath12k_wmi_scan_req_arg { u32 scan_id; u32 scan_req_id; @@ -3588,7 +3637,6 @@ struct ath12k_wmi_scan_req_arg { u32 num_bssid; u32 num_ssids; u32 n_probes; - u32 *chan_list; u32 notify_scan_events; struct cfg80211_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; struct ath12k_wmi_mac_addr_params bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; @@ -3597,6 +3645,7 @@ struct ath12k_wmi_scan_req_arg { u32 num_hint_bssid; struct ath12k_wmi_hint_short_ssid_arg hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID]; struct ath12k_wmi_hint_bssid_arg hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; + u32 chan_list[] __counted_by(num_chan); }; struct wmi_ssid_arg { @@ -4122,6 +4171,49 @@ enum set_init_cc_flags { ALPHA_IS_SET, }; +struct wmi_therm_throt_stats_event { + __le32 pdev_id; + __le32 temp; + __le32 level; + __le32 therm_throt_levels; +} __packed; + +#define THERMAL_LEVELS 4 +#define ENHANCED_THERMAL_LEVELS 5 + +struct ath12k_wmi_tt_level_config_param { + s32 tmplwm; + s32 tmphwm; + u32 dcoffpercent; + u32 pout_reduction_db; +}; + +struct ath12k_wmi_therm_throt_config_request_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 enable; + __le32 dc; + /* After how many duty cycles the firmware sends stats to host */ + __le32 dc_per_event; + __le32 therm_throt_levels; +} __packed; + +struct ath12k_wmi_therm_throt_level_config_param { + __le32 tlv_header; + a_sle32 temp_lwm; + a_sle32 temp_hwm; + __le32 dc_off_percent; + __le32 prio; + __le32 pout_reduction_25db; + __le32 tx_chain_mask; + __le32 duty_cycle; +} __packed; + +struct ath12k_wmi_thermal_mitigation_arg { + int num_levels; + const struct ath12k_wmi_tt_level_config_param *levelconf; +}; + struct ath12k_wmi_init_country_arg { union { u16 country_code; @@ -4195,7 +4287,6 @@ struct wmi_addba_clear_resp_cmd { struct ath12k_wmi_mac_addr_params peer_macaddr; } __packed; -#define DFS_PHYERR_UNIT_TEST_CMD 0 #define DFS_UNIT_TEST_MODULE 0x2b #define DFS_UNIT_TEST_TOKEN 0xAA @@ -4206,10 +4297,25 @@ enum dfs_test_args_idx { DFS_MAX_TEST_ARGS, }; -struct wmi_dfs_unit_test_arg { - u32 cmd_id; - u32 pdev_id; - u32 radar_param; +#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE 0x18 +#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN 0 +#define ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE 1 + +enum ath12k_wmi_incumbent_signal_test_args_idx { + ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF, + ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP, + ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS, +}; + +/* update if another test command requires more */ +#define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS + +struct wmi_unit_test_arg { + u32 vdev_id; + u32 module_id; + u32 diag_token; + u32 num_args; + u32 args[WMI_UNIT_TEST_ARGS_MAX]; }; struct wmi_unit_test_cmd { @@ -4485,6 +4591,62 @@ struct ath12k_wmi_pdev_radar_event { a_sle32 sidx; } __packed; +#define ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE 0x04 + +struct ath12k_wmi_dcs_interference_ev_fixed_params { + __le32 interference_type; + __le32 pdev_id; +} __packed; + +struct ath12k_wmi_incumbent_signal_interference_params { + __le32 chan_width; + __le32 chan_freq; + __le32 center_freq0; + __le32 center_freq1; + __le32 chan_bw_interference_bitmap; +} __packed; + +struct ath12k_wmi_incumbent_signal_interference_arg { + u32 chan_width; + u32 chan_freq; + u32 center_freq0; + u32 center_freq1; + u32 chan_bw_interference_bitmap; +}; + +struct ath12k_wmi_intf_arg { + u32 interference_type; + u32 pdev_id; +}; + +enum ath12k_wmi_dcs_interference_chan_segment { + /* + * Firmware reports interference bitmap in primary-based order. + * Bit 0 is the primary 20 MHz, bit 1 is the adjacent 20 MHz within + * the primary 40 MHz. Bits 2-3 cover the secondary 40 MHz, bits 4-7 + * cover the secondary 80 MHz, and bits 8-15 cover the secondary 160 MHz. + */ + ATH12K_WMI_DCS_SEG_PRI20 = 0x1, + ATH12K_WMI_DCS_SEG_SEC20 = 0x2, + ATH12K_WMI_DCS_SEG_SEC40_LOW = 0x4, + ATH12K_WMI_DCS_SEG_SEC40_UP = 0x8, + ATH12K_WMI_DCS_SEG_SEC40 = 0xC, + ATH12K_WMI_DCS_SEG_SEC80_LOW = 0x10, + ATH12K_WMI_DCS_SEG_SEC80_LOW_UP = 0x20, + ATH12K_WMI_DCS_SEG_SEC80_UP_LOW = 0x40, + ATH12K_WMI_DCS_SEG_SEC80_UP = 0x80, + ATH12K_WMI_DCS_SEG_SEC80 = 0xF0, + ATH12K_WMI_DCS_SEG_SEC160_LOW = 0x0100, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP = 0x0200, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP = 0x0400, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP_UP = 0x0800, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW_LOW = 0x1000, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW = 0x2000, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW = 0x4000, + ATH12K_WMI_DCS_SEG_SEC160_UP = 0x8000, + ATH12K_WMI_DCS_SEG_SEC160 = 0xFF00, +}; + struct wmi_pdev_temperature_event { /* temperature value in Celsius degree */ a_sle32 temp; @@ -6512,6 +6674,8 @@ __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); +int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) @@ -6574,4 +6738,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, struct ath12k_reg_tpc_power_info *param); int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, struct wmi_mlo_link_set_active_arg *param); +int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar, + u32 chan_bw_interference_bitmap); +int ath12k_wmi_alloc(void); +void ath12k_wmi_free(void); + #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h index af9be5fadcc31..273705956815c 100644 --- a/drivers/net/wireless/ath/ath12k/wow.h +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_WOW_H diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 79e021ac0bdbc..397e4244b958e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1977,11 +1977,11 @@ mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif, if (changed & BSS_CHANGED_FILS_DISCOVERY) { interval = vif->bss_conf.fils_discovery.max_interval; - skb = ieee80211_get_fils_discovery_tmpl(hw, vif); + skb = ieee80211_get_fils_discovery_tmpl(hw, vif, 0); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && vif->bss_conf.unsol_bcast_probe_resp_interval) { interval = vif->bss_conf.unsol_bcast_probe_resp_interval; - skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif, 0); } if (!skb) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 38b8743409a0c..31d73b9a291ee 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2868,11 +2868,13 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, if (changed & BSS_CHANGED_FILS_DISCOVERY && link_conf->fils_discovery.max_interval) { interval = link_conf->fils_discovery.max_interval; - skb = ieee80211_get_fils_discovery_tmpl(hw, vif); + skb = ieee80211_get_fils_discovery_tmpl(hw, vif, + link_conf->link_id); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && link_conf->unsol_bcast_probe_resp_interval) { interval = link_conf->unsol_bcast_probe_resp_interval; - skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif, + link_conf->link_id); } if (!skb) { diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 018857d3569a8..b0cdaa38f32d4 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6326,9 +6326,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) if (!chip->support_rnr) hw->wiphy->flags |= WIPHY_FLAG_SPLIT_SCAN_6GHZ; - if (chip->chip_gen == RTW89_CHIP_BE) - hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - if (rtwdev->support_mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 992a0c09c1e53..ad40c8d5bdb29 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2239,6 +2239,11 @@ enum ieee80211_radio_measurement_actioncode { #define PMK_MAX_LEN 64 #define SAE_PASSWORD_MAX_LEN 128 +#define MICHAEL_MIC_LEN 8 + +void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, + const u8 *data, size_t data_len, u8 *mic); + /* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_20_40_BSS_COEX = 0, diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h index 291cdc7ef49ce..b9dcb437a0be5 100644 --- a/include/linux/soc/qcom/qmi.h +++ b/include/linux/soc/qcom/qmi.h @@ -92,6 +92,18 @@ struct qmi_elem_info { #define QMI_ERR_INCOMPATIBLE_STATE_V01 90 #define QMI_ERR_NOT_SUPPORTED_V01 94 +/* + * Enumerate the IDs of the QMI services + */ +#define QMI_SERVICE_ID_TEST 0x0f /* 15 */ +#define QMI_SERVICE_ID_SSCTL 0x2b /* 43 */ +#define QMI_SERVICE_ID_IPA 0x31 /* 49 */ +#define QMI_SERVICE_ID_SERVREG_LOC 0x40 /* 64 */ +#define QMI_SERVICE_ID_SERVREG_NOTIF 0x42 /* 66 */ +#define QMI_SERVICE_ID_WLFW 0x45 /* 69 */ +#define QMI_SERVICE_ID_SLIMBUS 0x301 /* 769 */ +#define QMI_SERVICE_ID_USB_AUDIO_STREAM 0x41d /* 1053 */ + /** * struct qmi_response_type_v01 - common response header (decoded) * @result: result of the transaction diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 820e299f06b5e..d60ae944447ef 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5269,7 +5269,6 @@ struct cfg80211_ops { * set this flag to update channels on beacon hints. * @WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY: support connection to non-primary link * of an NSTR mobile AP MLD. - * @WIPHY_FLAG_DISABLE_WEXT: disable wireless extensions for this device */ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK = BIT(0), @@ -5281,7 +5280,7 @@ enum wiphy_flags { WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), - WIPHY_FLAG_DISABLE_WEXT = BIT(9), + /* reuse bit 9 */ WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_EXT_KCK_32 = BIT(11), WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY = BIT(12), @@ -5683,9 +5682,13 @@ struct wiphy_iftype_akm_suites { * * @rts_threshold: RTS threshold (dot11RTSThreshold); * -1 (default) = RTS/CTS disabled + * @radio_debugfsdir: Pointer to debugfs directory containing the radio- + * specific parameters. + * NULL (default) = Debugfs directory not created */ struct wiphy_radio_cfg { u32 rts_threshold; + struct dentry *radio_debugfsdir; }; /** @@ -10328,4 +10331,27 @@ cfg80211_s1g_get_primary_sibling(struct wiphy *wiphy, return ieee80211_get_channel_khz(wiphy, sibling_1mhz_khz); } + +/** + * cfg80211_incumbent_signal_notify - Notify userspace of incumbent signal detection + * @wiphy: the wiphy to use + * @chandef: channel definition in which the interference was detected + * @signal_interference_bitmap: bitmap indicating interference across 20 MHz segments + * @gfp: allocation context for message creation and multicast; pass GFP_ATOMIC + * if called from atomic context (e.g. firmware event handler), otherwise + * GFP_KERNEL + * + * Use this function to notify userspace when an incumbent signal is detected on + * the operating channel in the 6 GHz band. The notification includes the + * current channel definition and a bitmap representing interference across + * the operating bandwidth. Each bit in the bitmap corresponds to a 20 MHz + * segment, with the lowest bit representing the lowest frequency segment. + * Punctured sub-channels are included in the bitmap structure but are always + * set to zero since interference detection is not performed on them. + */ +void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ac2546b121385..42d8113cea00f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1892,12 +1892,16 @@ enum ieee80211_vif_flags { * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled * The driver supports passing received 802.11 frames as 802.3 frames to * mac80211. + * @IEEE80211_OFFLOAD_ENCAP_MCAST: tx multicast encapsulation offload is enabled + * The driver supports sending multicast frames passed as 802.3 frames + * by mac80211. */ enum ieee80211_offload_flags { IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0), IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1), IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2), + IEEE80211_OFFLOAD_ENCAP_MCAST = BIT(3), }; /** @@ -7650,19 +7654,22 @@ u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, * ieee80211_get_fils_discovery_tmpl - Get FILS discovery template. * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @link_id: valid link_id during MLO or 0 for non-MLO. * * The driver is responsible for freeing the returned skb. * * Return: FILS discovery template. %NULL on error. */ struct sk_buff *ieee80211_get_fils_discovery_tmpl(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); + struct ieee80211_vif *vif, + unsigned int link_id); /** * ieee80211_get_unsol_bcast_probe_resp_tmpl - Get unsolicited broadcast * probe response template. * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @link_id: valid link_id during MLO or 0 for non-MLO. * * The driver is responsible for freeing the returned skb. * @@ -7670,7 +7677,8 @@ struct sk_buff *ieee80211_get_fils_discovery_tmpl(struct ieee80211_hw *hw, */ struct sk_buff * ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); + struct ieee80211_vif *vif, + unsigned int link_id); /** * ieee80211_obss_color_collision_notify - notify userland about a BSS color diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8134f10e4e6c0..68edda32d6e7b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1361,6 +1361,12 @@ * user space that the NAN new cluster has been joined. The cluster ID is * indicated by %NL80211_ATTR_MAC. * + * @NL80211_CMD_INCUMBENT_SIGNAL_DETECT: Once any incumbent signal is detected + * on the operating channel in 6 GHz band, userspace is notified with the + * signal interference bitmap using + * %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel + * definition is also sent. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1624,6 +1630,8 @@ enum nl80211_commands { NL80211_CMD_NAN_NEXT_DW_NOTIFICATION, NL80211_CMD_NAN_CLUSTER_JOINED, + NL80211_CMD_INCUMBENT_SIGNAL_DETECT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2973,6 +2981,15 @@ enum nl80211_commands { * primary channel is 2 MHz wide, and the control channel designates * the 1 MHz primary subchannel within that 2 MHz primary. * + * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying + * the signal interference bitmap detected on the operating bandwidth for + * %NL80211_CMD_INCUMBENT_SIGNAL_DETECT. Each bit represents a 20 MHz + * segment, lowest bit corresponds to the lowest 20 MHz segment, in the + * operating bandwidth where the interference is detected. Punctured + * sub-channels are included in the bitmap structure; however, since + * interference detection is not performed on these sub-channels, their + * corresponding bits are consistently set to zero. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3541,6 +3558,8 @@ enum nl80211_attrs { NL80211_ATTR_S1G_PRIMARY_2MHZ, + NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index a33884967f212..85eaf72ba803c 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -18,7 +18,6 @@ mac80211-y := \ iface.o \ link.o \ rate.o \ - michael.o \ tkip.o \ aes_cmac.o \ aes_gmac.o \ diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h deleted file mode 100644 index a7fdb8e84615a..0000000000000 --- a/net/mac80211/michael.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Michael MIC implementation - optimized for TKIP MIC operations - * Copyright 2002-2003, Instant802 Networks, Inc. - */ - -#ifndef MICHAEL_H -#define MICHAEL_H - -#include -#include - -#define MICHAEL_MIC_LEN 8 - -struct michael_mic_ctx { - u32 l, r; -}; - -void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, - const u8 *data, size_t data_len, u8 *mic); - -#endif /* MICHAEL_H */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 98f0a275b60c3..43fa7e2b62dd2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4614,7 +4614,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, { struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; - struct tid_ampdu_tx *tid_tx; + struct tid_ampdu_tx *tid_tx = NULL; struct sk_buff *seg, *next; unsigned int skbs = 0, len = 0; u16 queue; @@ -4634,7 +4634,9 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_aggr_check(sdata, sta, skb); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + + if (sta) + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (tid_tx) { if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { /* fall back to non-offload slow path */ @@ -4682,8 +4684,11 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, } dev_sw_netstats_tx_add(dev, skbs, len); - sta->deflink.tx_stats.packets[queue] += skbs; - sta->deflink.tx_stats.bytes[queue] += len; + + if (sta) { + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + } ieee80211_tpt_led_trig_tx(local, len); @@ -4695,19 +4700,35 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, kfree_skb(skb); } -netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, - struct net_device *dev) +static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if ((ieee80211_vif_is_mld(&sdata->vif) && + (sdata->vif.type == NL80211_IFTYPE_AP && + !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX))) || + (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + !sdata->wdev.use_4addr)) + return false; + + if (!is_multicast_ether_addr(skb->data) || + !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_MCAST) || + sdata->control_port_protocol == ehdr->h_proto) + return false; + + return true; +} + +static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_link_data *link; struct ieee80211_key *key; struct sta_info *sta; - if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - rcu_read_lock(); if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { @@ -4715,14 +4736,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, goto out; } - if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || - !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) + /* + * If STA is invalid, use the multicast offload path when applicable. + * In AP mode, drop the frame if there are no associated stations; + * otherwise use the default link and multicast key for transmission. + */ + if (unlikely(IS_ERR_OR_NULL(sta) && + ieee80211_check_mcast_offload(sdata, skb))) { + sta = NULL; + if (ieee80211_vif_get_num_mcast_if(sdata) <= 0) { + /* No associated STAs - no need to send multicast frames. */ + kfree_skb(skb); + goto out; + } + link = &sdata->deflink; + key = rcu_dereference(link->default_multicast_key); + } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || + !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || + sdata->control_port_protocol == ehdr->h_proto)) { goto skip_offload; - - key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); + } else { + key = rcu_dereference(sta->ptk[sta->ptk_idx]); + if (!key) + key = rcu_dereference(sdata->default_unicast_key); + } if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) @@ -4736,6 +4773,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, ieee80211_subif_start_xmit(skb, dev); out: rcu_read_unlock(); +} + +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (unlikely(is_multicast_ether_addr(ehdr->h_dest) && + ieee80211_multicast_to_unicast(skb, dev))) { + struct sk_buff_head queue; + + __skb_queue_head_init(&queue); + ieee80211_convert_to_unicast(skb, dev, &queue); + while ((skb = __skb_dequeue(&queue))) + __ieee80211_subif_start_xmit_8023(skb, dev); + } else { + __ieee80211_subif_start_xmit_8023(skb, dev); + } return NETDEV_TX_OK; } @@ -5839,21 +5900,28 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, EXPORT_SYMBOL(ieee80211_proberesp_get); struct sk_buff *ieee80211_get_fils_discovery_tmpl(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + unsigned int link_id) { struct sk_buff *skb = NULL; struct fils_discovery_data *tmpl = NULL; struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_link_data *link; if (sdata->vif.type != NL80211_IFTYPE_AP) return NULL; - rcu_read_lock(); - tmpl = rcu_dereference(sdata->deflink.u.ap.fils_discovery); - if (!tmpl) { - rcu_read_unlock(); + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + guard(rcu)(); + link = rcu_dereference(sdata->link[link_id]); + if (!link) + return NULL; + + tmpl = rcu_dereference(link->u.ap.fils_discovery); + if (!tmpl) return NULL; - } skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + tmpl->len); if (skb) { @@ -5861,28 +5929,34 @@ struct sk_buff *ieee80211_get_fils_discovery_tmpl(struct ieee80211_hw *hw, skb_put_data(skb, tmpl->data, tmpl->len); } - rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_get_fils_discovery_tmpl); struct sk_buff * ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + unsigned int link_id) { struct sk_buff *skb = NULL; struct unsol_bcast_probe_resp_data *tmpl = NULL; struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_link_data *link; if (sdata->vif.type != NL80211_IFTYPE_AP) return NULL; - rcu_read_lock(); - tmpl = rcu_dereference(sdata->deflink.u.ap.unsol_bcast_probe_resp); - if (!tmpl) { - rcu_read_unlock(); + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + guard(rcu)(); + link = rcu_dereference(sdata->link[link_id]); + if (!link) + return NULL; + + tmpl = rcu_dereference(link->u.ap.unsol_bcast_probe_resp); + if (!tmpl) return NULL; - } skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + tmpl->len); if (skb) { @@ -5890,7 +5964,6 @@ ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw, skb_put_data(skb, tmpl->data, tmpl->len); } - rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_get_unsol_bcast_probe_resp_tmpl); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index bb0fa505cdcae..42bbe64617a11 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -18,7 +18,6 @@ #include #include "ieee80211_i.h" -#include "michael.h" #include "tkip.h" #include "aes_ccm.h" #include "aes_cmac.h" diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 62a83faf0e07d..a77fd5ba63686 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o -cfg80211-y += pmsr.o +cfg80211-y += michael-mic.o pmsr.o cfg80211-$(CONFIG_OF) += of.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o diff --git a/net/wireless/core.c b/net/wireless/core.c index 6b204ca91a140..beed7c35bdd30 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -34,6 +34,9 @@ /* name for sysfs, %d is appended */ #define PHY_NAME "phy" +/* maximum length of radio debugfs directory name */ +#define RADIO_DEBUGFSDIR_MAX_LEN 8 + MODULE_AUTHOR("Johannes Berg"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("wireless configuration support"); @@ -1038,6 +1041,18 @@ int wiphy_register(struct wiphy *wiphy) /* add to debugfs */ rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy), ieee80211_debugfs_dir); + if (wiphy->n_radio > 0) { + int idx; + char radio_name[RADIO_DEBUGFSDIR_MAX_LEN]; + + for (idx = 0; idx < wiphy->n_radio; idx++) { + scnprintf(radio_name, sizeof(radio_name), "radio%d", + idx); + wiphy->radio_cfg[idx].radio_debugfsdir = + debugfs_create_dir(radio_name, + rdev->wiphy.debugfsdir); + } + } cfg80211_debugfs_rdev_add(rdev); nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 40e49074e2eeb..f9e7fff1ef257 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -29,6 +29,24 @@ static const struct file_operations name## _ops = { \ .llseek = generic_file_llseek, \ } +#define DEBUGFS_RADIO_READONLY_FILE(name, buflen, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wiphy_radio_cfg *radio_cfg = file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + res = scnprintf(buf, buflen, fmt "\n", ##value); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", wiphy->rts_threshold); DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", @@ -38,6 +56,9 @@ DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", wiphy->retry_long); +DEBUGFS_RADIO_READONLY_FILE(radio_rts_threshold, 20, "%d", + radio_cfg->rts_threshold); + static int ht_print_chan(struct ieee80211_channel *chan, char *buf, int buf_size, int offset) { @@ -100,15 +121,27 @@ static const struct file_operations ht40allow_map_ops = { #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops) +#define DEBUGFS_RADIO_ADD(name, radio_idx) \ + debugfs_create_file(#name, 0444, radiod, \ + &rdev->wiphy.radio_cfg[radio_idx], \ + &name## _ops) + void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) { struct dentry *phyd = rdev->wiphy.debugfsdir; + struct dentry *radiod; + u8 i; DEBUGFS_ADD(rts_threshold); DEBUGFS_ADD(fragmentation_threshold); DEBUGFS_ADD(short_retry_limit); DEBUGFS_ADD(long_retry_limit); DEBUGFS_ADD(ht40allow_map); + + for (i = 0; i < rdev->wiphy.n_radio; i++) { + radiod = rdev->wiphy.radio_cfg[i].radio_debugfsdir; + DEBUGFS_RADIO_ADD(radio_rts_threshold, i); + } } struct debugfs_read_work { diff --git a/net/mac80211/michael.c b/net/wireless/michael-mic.c similarity index 96% rename from net/mac80211/michael.c rename to net/wireless/michael-mic.c index 8a1afc93e7499..50cdb67f0503d 100644 --- a/net/mac80211/michael.c +++ b/net/wireless/michael-mic.c @@ -8,7 +8,9 @@ #include #include -#include "michael.h" +struct michael_mic_ctx { + u32 l, r; +}; static void michael_block(struct michael_mic_ctx *mctx, u32 val) { @@ -81,3 +83,4 @@ void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, put_unaligned_le32(mctx.l, mic); put_unaligned_le32(mctx.r, mic + 4); } +EXPORT_SYMBOL_GPL(michael_mic); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 54a4585eb3a2d..160b50f1df449 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -20988,6 +20988,46 @@ void cfg80211_ch_switch_notify(struct net_device *dev, } EXPORT_SYMBOL(cfg80211_ch_switch_notify); +void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_incumbent_signal_notify(wiphy, chandef, signal_interference_bitmap); + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_INCUMBENT_SIGNAL_DETECT); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + signal_interference_bitmap)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + NL80211_MCGRP_MLME, gfp); + return; + +nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_incumbent_signal_notify); + void cfg80211_ch_switch_started_notify(struct net_device *dev, struct cfg80211_chan_def *chandef, unsigned int link_id, u8 count, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 2b71f1d867a08..27779d11b3733 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -4222,6 +4222,25 @@ TRACE_EVENT(cfg80211_nan_cluster_joined, WDEV_PR_ARG, __entry->cluster_id, __entry->new_cluster ? " [new]" : "") ); + +TRACE_EVENT(cfg80211_incumbent_signal_notify, + TP_PROTO(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + u32 signal_interference_bitmap), + TP_ARGS(wiphy, chandef, signal_interference_bitmap), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_DEF_ENTRY + __field(u32, signal_interference_bitmap) + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + __entry->signal_interference_bitmap = signal_interference_bitmap; + ), + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", signal_interference_bitmap=0x%x", + WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->signal_interference_bitmap) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 7b8e94214b072..0440ae61a886b 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -661,8 +661,7 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev) dev->ieee80211_ptr->wiphy->wext && dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); } @@ -704,8 +703,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; handlers = dev->ieee80211_ptr->wiphy->wext; }