summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c28
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode.c253
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode_i.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h19
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.h38
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c455
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c20
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.h18
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c2
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c20
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h18
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c24
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h4
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.c14
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.c19
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.c74
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.c10
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c770
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.h14
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c24
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.c13
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.h5
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c98
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.h55
-rw-r--r--drivers/net/wireless/ath/ath12k/wow.c1
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h74
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c60
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h1
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c1
32 files changed, 1571 insertions, 586 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 6f78f1752cd6..7c2939cbde5f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, 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.
*/
@@ -1187,7 +1186,7 @@ static int ath10k_download_fw(struct ath10k *ar)
u32 address, data_len;
const void *data;
int ret;
- struct pm_qos_request latency_qos;
+ struct pm_qos_request latency_qos = {};
address = ar->hw_params.patch_load_addr;
@@ -1221,7 +1220,6 @@ static int ath10k_download_fw(struct ath10k *ar)
ret);
}
- memset(&latency_qos, 0, sizeof(latency_qos));
cpu_latency_qos_add_request(&latency_qos, 0);
ret = ath10k_bmi_fast_download(ar, address, data, data_len);
@@ -2493,8 +2491,9 @@ static int ath10k_init_hw_params(struct ath10k *ar)
return 0;
}
-static bool ath10k_core_needs_recovery(struct ath10k *ar)
+static void ath10k_core_recovery_check_work(struct work_struct *work)
{
+ struct ath10k *ar = container_of(work, struct ath10k, recovery_check_work);
long time_left;
/* Sometimes the recovery will fail and then the next all recovery fail,
@@ -2504,7 +2503,7 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
ath10k_err(ar, "consecutive fail %d times, will shutdown driver!",
atomic_read(&ar->fail_cont_count));
ar->state = ATH10K_STATE_WEDGED;
- return false;
+ return;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "total recovery count: %d", ++ar->recovery_count);
@@ -2518,27 +2517,24 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
ATH10K_RECOVERY_TIMEOUT_HZ);
if (time_left) {
ath10k_warn(ar, "previous recovery succeeded, skip this!\n");
- return false;
+ return;
}
/* Record the continuous recovery fail count when recovery failed. */
atomic_inc(&ar->fail_cont_count);
/* Avoid having multiple recoveries at the same time. */
- return false;
+ return;
}
atomic_inc(&ar->pending_recovery);
-
- return true;
+ queue_work(ar->workqueue, &ar->restart_work);
}
void ath10k_core_start_recovery(struct ath10k *ar)
{
- if (!ath10k_core_needs_recovery(ar))
- return;
-
- queue_work(ar->workqueue, &ar->restart_work);
+ /* Use workqueue_aux to avoid blocking recovery tracking */
+ queue_work(ar->workqueue_aux, &ar->recovery_check_work);
}
EXPORT_SYMBOL(ath10k_core_start_recovery);
@@ -3356,7 +3352,7 @@ EXPORT_SYMBOL(ath10k_core_stop);
*/
static int ath10k_core_probe_fw(struct ath10k *ar)
{
- struct bmi_target_info target_info;
+ struct bmi_target_info target_info = {};
int ret = 0;
ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -3367,7 +3363,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
switch (ar->hif.bus) {
case ATH10K_BUS_SDIO:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info_sdio(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3379,7 +3374,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
case ATH10K_BUS_PCI:
case ATH10K_BUS_AHB:
case ATH10K_BUS_USB:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3389,7 +3383,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ar->hw->wiphy->hw_version = target_info.version;
break;
case ATH10K_BUS_SNOC:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_hif_get_target_info(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3734,6 +3727,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
INIT_WORK(&ar->register_work, ath10k_core_register_work);
INIT_WORK(&ar->restart_work, ath10k_core_restart);
+ INIT_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
INIT_WORK(&ar->set_coverage_class_work,
ath10k_core_set_coverage_class_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8c72ed386edb..73a9db302245 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -1208,6 +1207,7 @@ struct ath10k {
struct work_struct register_work;
struct work_struct restart_work;
+ struct work_struct recovery_check_work;
struct work_struct bundle_tx_work;
struct work_struct tx_complete_work;
@@ -1259,9 +1259,13 @@ struct ath10k {
struct {
/* protected by conf_mutex */
struct ath10k_fw_components utf_mode_fw;
+ u8 ftm_msgref;
/* protected by data_lock */
bool utf_monitor;
+ u32 data_pos;
+ u32 expected_seq;
+ u8 *eventdata;
} testmode;
struct {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 154ac7a70982..da6f7957a0ae 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, 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.
*/
@@ -5428,6 +5427,7 @@ static void ath10k_stop(struct ieee80211_hw *hw, bool suspend)
cancel_work_sync(&ar->set_coverage_class_work);
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->restart_work);
+ cancel_work_sync(&ar->recovery_check_work);
}
static int ath10k_config_ps(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index f1f33af0170a..8275345631a0 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -986,7 +986,7 @@ static int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,
ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)&qmi->sq,
sizeof(qmi->sq), 0);
if (ret) {
ath10k_err(ar, "failed to connect to a remote QMI service port\n");
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index 3fcefc55b74f..d3bd385694d6 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "testmode.h"
@@ -10,12 +11,17 @@
#include "debug.h"
#include "wmi.h"
+#include "wmi-tlv.h"
#include "hif.h"
#include "hw.h"
#include "core.h"
#include "testmode_i.h"
+#define ATH10K_FTM_SEG_NONE ((u32)-1)
+#define ATH10K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
+#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
+
static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
[ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,
@@ -25,41 +31,19 @@ static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
};
-/* Returns true if callee consumes the skb and the skb should be discarded.
- * Returns false if skb is not used. Does not sleep.
- */
-bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
+ struct sk_buff *skb)
{
struct sk_buff *nl_skb;
- bool consumed;
int ret;
- ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
- "testmode event wmi cmd_id %d skb %p skb->len %d\n",
- cmd_id, skb, skb->len);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
-
- spin_lock_bh(&ar->data_lock);
-
- if (!ar->testmode.utf_monitor) {
- consumed = false;
- goto out;
- }
-
- /* Only testmode.c should be handling events from utf firmware,
- * otherwise all sort of problems will arise as mac80211 operations
- * are not initialised.
- */
- consumed = true;
-
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
2 * sizeof(u32) + skb->len,
GFP_ATOMIC);
if (!nl_skb) {
ath10k_warn(ar,
"failed to allocate skb for testmode wmi event\n");
- goto out;
+ return;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
@@ -68,7 +52,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to put testmode wmi event cmd attribute: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
@@ -77,7 +61,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to put testmode wmi event cmd_id: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
}
ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
@@ -86,10 +70,122 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to copy skb to testmode wmi event: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
+ }
+
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+{
+ struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;
+ u8 total_segments, current_seq;
+ struct sk_buff *nl_skb;
+ u8 const *buf_pos;
+ u16 datalen;
+ u32 data_pos;
+ int ret;
+
+ if (skb->len < sizeof(*ftm)) {
+ ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);
+ return;
+ }
+
+ current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,
+ __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+ total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,
+ __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+ datalen = skb->len - sizeof(*ftm);
+ buf_pos = ftm->data;
+
+ if (current_seq == 0) {
+ ar->testmode.expected_seq = 0;
+ ar->testmode.data_pos = 0;
+ }
+
+ data_pos = ar->testmode.data_pos;
+
+ if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
+ ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",
+ data_pos, datalen);
+ ret = -EINVAL;
+ return;
+ }
+
+ memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);
+ data_pos += datalen;
+
+ if (++ar->testmode.expected_seq != total_segments) {
+ ar->testmode.data_pos = data_pos;
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received %u/%u\n",
+ current_seq + 1, total_segments);
+ return;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);
+
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
+ 2 * sizeof(u32) + data_pos,
+ GFP_ATOMIC);
+ if (!nl_skb) {
+ ath10k_warn(ar, "failed to allocate skb for testmode wmi event\n");
+ return;
+ }
+
+ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);
+ if (ret) {
+ ath10k_warn(ar, "failed to put testmode wmi event attribute: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
+ if (ret) {
+ ath10k_warn(ar, "failed to put testmode wmi event cmd_id: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, &ar->testmode.eventdata[0]);
+ if (ret) {
+ ath10k_warn(ar, "failed to copy skb to testmode wmi event: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
}
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+/* Returns true if callee consumes the skb and the skb should be discarded.
+ * Returns false if skb is not used. Does not sleep.
+ */
+bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+{
+ bool consumed;
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+ "testmode event wmi cmd_id %d skb %p skb->len %d\n",
+ cmd_id, skb, skb->len);
+
+ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+
+ spin_lock_bh(&ar->data_lock);
+
+ if (!ar->testmode.utf_monitor) {
+ consumed = false;
+ goto out;
+ }
+
+ /* Only testmode.c should be handling events from utf firmware,
+ * otherwise all sort of problems will arise as mac80211 operations
+ * are not initialised.
+ */
+ consumed = true;
+
+ if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
+ ath10k_tm_event_segmented(ar, cmd_id, skb);
+ else
+ ath10k_tm_event_unsegmented(ar, cmd_id, skb);
out:
spin_unlock_bh(&ar->data_lock);
@@ -281,12 +377,18 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
goto err_release_utf_mode_fw;
}
+ ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
+ if (!ar->testmode.eventdata) {
+ ret = -ENOMEM;
+ goto err_power_down;
+ }
+
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
&ar->testmode.utf_mode_fw);
if (ret) {
ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
ar->state = ATH10K_STATE_OFF;
- goto err_power_down;
+ goto err_release_eventdata;
}
ar->state = ATH10K_STATE_UTF;
@@ -302,6 +404,10 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
return 0;
+err_release_eventdata:
+ kfree(ar->testmode.eventdata);
+ ar->testmode.eventdata = NULL;
+
err_power_down:
ath10k_hif_power_down(ar);
@@ -341,6 +447,9 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
+ kfree(ar->testmode.eventdata);
+ ar->testmode.eventdata = NULL;
+
ar->state = ATH10K_STATE_OFF;
}
@@ -424,6 +533,85 @@ out:
return ret;
}
+static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])
+{
+ u16 total_bytes, num_segments;
+ u32 cmd_id, buf_len;
+ u8 segnumber = 0;
+ u8 *bufpos;
+ void *buf;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto out;
+ }
+
+ buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
+ cmd_id = WMI_PDEV_UTF_CMDID;
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+ "cmd wmi ftm cmd_id %d buffer length %d\n",
+ cmd_id, buf_len);
+ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
+
+ bufpos = buf;
+ total_bytes = buf_len;
+ num_segments = total_bytes / MAX_WMI_UTF_LEN;
+ ar->testmode.expected_seq = 0;
+
+ if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+ num_segments++;
+
+ while (buf_len) {
+ u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
+ struct wmi_ftm_cmd *ftm_cmd;
+ struct sk_buff *skb;
+ u32 hdr_info;
+ u8 seginfo;
+
+ skb = ath10k_wmi_alloc_skb(ar, (chunk_len +
+ sizeof(struct wmi_ftm_cmd)));
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
+ hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |
+ FIELD_PREP(WMI_TLV_LEN, (chunk_len +
+ sizeof(struct wmi_ftm_seg_hdr)));
+ ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);
+ ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);
+ ftm_cmd->seg_hdr.msgref = __cpu_to_le32(ar->testmode.ftm_msgref);
+ seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
+ FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
+ ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);
+ segnumber++;
+
+ memcpy(&ftm_cmd->data, bufpos, chunk_len);
+
+ ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
+ if (ret) {
+ ath10k_warn(ar, "failed to send wmi ftm command: %d\n", ret);
+ goto out;
+ }
+
+ buf_len -= chunk_len;
+ bufpos += chunk_len;
+ }
+
+ ar->testmode.ftm_msgref++;
+ ret = 0;
+
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
@@ -439,9 +627,14 @@ int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (!tb[ATH10K_TM_ATTR_CMD])
return -EINVAL;
+ ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;
+
switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
case ATH10K_TM_CMD_GET_VERSION:
- return ath10k_tm_cmd_get_version(ar, tb);
+ if (!tb[ATH10K_TM_ATTR_DATA])
+ return ath10k_tm_cmd_get_version(ar, tb);
+ else /* ATH10K_TM_CMD_TLV */
+ return ath10k_tm_cmd_tlv(ar, tb);
case ATH10K_TM_CMD_UTF_START:
return ath10k_tm_cmd_utf_start(ar, tb);
case ATH10K_TM_CMD_UTF_STOP:
diff --git a/drivers/net/wireless/ath/ath10k/testmode_i.h b/drivers/net/wireless/ath/ath10k/testmode_i.h
index ee1cb27c1d60..1603f5276682 100644
--- a/drivers/net/wireless/ath/ath10k/testmode_i.h
+++ b/drivers/net/wireless/ath/ath10k/testmode_i.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
/* "API" level of the ath10k testmode interface. Bump it after every
@@ -14,6 +15,7 @@
#define ATH10K_TESTMODE_VERSION_MINOR 0
#define ATH10K_TM_DATA_MAX_LEN 5000
+#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048
enum ath10k_tm_attr {
__ATH10K_TM_ATTR_INVALID = 0,
@@ -57,4 +59,17 @@ enum ath10k_tm_cmd {
* ATH10K_TM_ATTR_DATA.
*/
ATH10K_TM_CMD_WMI = 3,
+
+ /* The command used to transmit a test command to the firmware
+ * and the event to receive test events from the firmware. The data
+ * received only contain the TLV payload, need to add the tlv header
+ * and send the cmd to firmware with command id WMI_PDEV_UTF_CMDID.
+ * The data payload size could be large and the driver needs to
+ * send segmented data to firmware.
+ *
+ * This legacy testmode command shares the same value as the get-version
+ * command. To distinguish between them, we check whether the data attribute
+ * is present.
+ */
+ ATH10K_TM_CMD_TLV = ATH10K_TM_CMD_GET_VERSION,
};
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 0faefc0a9a40..7f50a1de6b97 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -3,7 +3,7 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, 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 _WMI_H_
@@ -7418,6 +7418,23 @@ struct wmi_pdev_bb_timing_cfg_cmd {
__le32 bb_xpa_timing;
} __packed;
+struct wmi_ftm_seg_hdr {
+ __le32 len;
+ __le32 msgref;
+ __le32 segmentinfo;
+ __le32 pdev_id;
+} __packed;
+
+struct wmi_ftm_cmd {
+ __le32 tlv_header;
+ struct wmi_ftm_seg_hdr seg_hdr;
+ u8 data[];
+} __packed;
+
+#define WMI_TLV_LEN GENMASK(15, 0)
+#define WMI_TLV_TAG GENMASK(31, 16)
+#define MAX_WMI_UTF_LEN 252
+
struct ath10k;
struct ath10k_vif;
struct ath10k_fw_stats_pdev;
diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h
index 839095af9267..82603a389bb9 100644
--- a/drivers/net/wireless/ath/ath11k/hal.h
+++ b/drivers/net/wireless/ath/ath11k/hal.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 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.
*/
#ifndef ATH11K_HAL_H
@@ -43,14 +43,14 @@ struct ath11k_base;
#define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000
#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000
#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000
-#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
-#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
-#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
-#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
+#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
+#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
+#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
+#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000
#define HAL_CE_WFSS_CE_REG_BASE 0x01b80000
@@ -209,10 +209,10 @@ struct ath11k_base;
#define HAL_REO_STATUS_HP(ab) ab->hw_params.regs->hal_reo_status_hp
/* WBM Idle R0 address */
-#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm_idle_link_ring_base_lsb)
-#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(x) \
- (ab->hw_params.regs->hal_wbm_idle_link_ring_misc)
+#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm_idle_link_ring_base_lsb)
+#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(ab) \
+ ((ab)->hw_params.regs->hal_wbm_idle_link_ring_misc)
#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048
#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c
#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058
@@ -227,17 +227,17 @@ struct ath11k_base;
#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0
/* SW2WBM R0 release address */
-#define HAL_WBM_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm_release_ring_base_lsb)
+#define HAL_WBM_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm_release_ring_base_lsb)
/* SW2WBM R2 release address */
#define HAL_WBM_RELEASE_RING_HP 0x00003018
/* WBM2SW R0 release address */
-#define HAL_WBM0_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm0_release_ring_base_lsb)
-#define HAL_WBM1_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm1_release_ring_base_lsb)
+#define HAL_WBM0_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm0_release_ring_base_lsb)
+#define HAL_WBM1_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm1_release_ring_base_lsb)
/* WBM2SW R2 release address */
#define HAL_WBM0_RELEASE_RING_HP 0x000030c0
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 0e41b5a91d66..3276fe443502 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -2235,9 +2235,9 @@ static void ath11k_peer_assoc_h_vht(struct ath11k *ar,
arg->peer_nss = min(sta->deflink.rx_nss, max_nss);
arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+ arg->rx_mcs_set = ath11k_peer_assoc_h_vht_limit(arg->rx_mcs_set, vht_mcs_mask);
arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
- arg->tx_mcs_set = ath11k_peer_assoc_h_vht_limit(
- __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask);
+ arg->tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
/* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default.
* VHT mcs rate 10 and 11 is not supported in 11ac standard.
@@ -2522,10 +2522,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
he_tx_mcs = v;
}
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
arg->peer_he_mcs_count++;
@@ -2535,10 +2535,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
default:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
arg->peer_he_mcs_count++;
@@ -4028,6 +4028,150 @@ static int ath11k_start_scan(struct ath11k *ar,
return 0;
}
+static void ath11k_mac_fw_stats_reset(struct ath11k *ar)
+{
+ spin_lock_bh(&ar->data_lock);
+ ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
+ ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
+ ar->fw_stats.num_vdev_recvd = 0;
+ ar->fw_stats.num_bcn_recvd = 0;
+ spin_unlock_bh(&ar->data_lock);
+}
+
+int ath11k_mac_fw_stats_request(struct ath11k *ar,
+ struct stats_request_params *req_param)
+{
+ struct ath11k_base *ab = ar->ab;
+ unsigned long time_left;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ ath11k_mac_fw_stats_reset(ar);
+
+ reinit_completion(&ar->fw_stats_complete);
+ reinit_completion(&ar->fw_stats_done);
+
+ ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
+
+ if (ret) {
+ ath11k_warn(ab, "could not request fw stats (%d)\n",
+ ret);
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ /* FW stats can get split when exceeding the stats data buffer limit.
+ * In that case, since there is no end marking for the back-to-back
+ * received 'update stats' event, we keep a 3 seconds timeout in case,
+ * fw_stats_done is not marked yet
+ */
+ time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id,
+ u32 vdev_id, u32 stats_id)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct stats_request_params req_param;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (ar->state != ATH11K_STATE_ON)
+ return -ENETDOWN;
+
+ req_param.pdev_id = pdev_id;
+ req_param.vdev_id = vdev_id;
+ req_param.stats_id = stats_id;
+
+ ret = ath11k_mac_fw_stats_request(ar, &req_param);
+ if (ret)
+ ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
+ pdev_id, vdev_id, stats_id);
+
+ return ret;
+}
+
+static int ath11k_mac_handle_get_txpower(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ int *dbm)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct ath11k_fw_stats_pdev *pdev;
+ int ret;
+
+ /* Final Tx power is minimum of Target Power, CTL power, Regulatory
+ * Power, PSD EIRP Power. We just know the Regulatory power from the
+ * regulatory rules obtained. FW knows all these power and sets the min
+ * of these. Hence, we request the FW pdev stats in which FW reports
+ * the minimum of all vdev's channel Tx power.
+ */
+ lockdep_assert_held(&ar->conf_mutex);
+
+ /* Firmware doesn't provide Tx power during CAC hence no need to fetch
+ * the stats.
+ */
+ if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags))
+ return -EAGAIN;
+
+ ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
+ WMI_REQUEST_PDEV_STAT);
+ if (ret) {
+ ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
+ goto err_fallback;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
+ struct ath11k_fw_stats_pdev, list);
+ if (!pdev) {
+ spin_unlock_bh(&ar->data_lock);
+ goto err_fallback;
+ }
+
+ /* tx power is set as 2 units per dBm in FW. */
+ *dbm = pdev->chan_tx_power / 2;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware %d, reported %d dBm\n",
+ pdev->chan_tx_power, *dbm);
+ return 0;
+
+err_fallback:
+ /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
+ *dbm = vif->bss_conf.txpower;
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
+ *dbm);
+ return 0;
+}
+
+static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id,
+ int *dbm)
+{
+ struct ath11k *ar = hw->priv;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+ ret = ath11k_mac_handle_get_txpower(ar, vif, dbm);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
@@ -6107,6 +6251,159 @@ static void ath11k_mgmt_over_wmi_tx_purge(struct ath11k *ar)
ath11k_mgmt_over_wmi_tx_drop(ar, skb);
}
+static int ath11k_mac_mgmt_action_frame_fill_elem_data(struct ath11k_vif *arvif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ u8 category, *buf, iv_len, action_code, dialog_token;
+ int cur_tx_power, max_tx_power;
+ struct ath11k *ar = arvif->ar;
+ struct cfg80211_chan_def def;
+ struct ath11k_skb_cb *skb_cb;
+ struct ieee80211_mgmt *mgmt;
+ unsigned int remaining_len;
+ bool has_protected;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ /* make sure category field is present */
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return -EINVAL;
+
+ remaining_len = skb->len - IEEE80211_MIN_ACTION_SIZE;
+ has_protected = ieee80211_has_protected(hdr->frame_control);
+
+ /* In case of SW crypto and hdr protected (PMF), packet will already be encrypted,
+ * we can't put in data in this case
+ */
+ if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
+ has_protected)
+ return 0;
+
+ mgmt = (struct ieee80211_mgmt *)hdr;
+ buf = (u8 *)&mgmt->u.action;
+
+ /* FCTL_PROTECTED frame might have extra space added for HDR_LEN. Offset that
+ * many bytes if it is there
+ */
+ if (has_protected) {
+ skb_cb = ATH11K_SKB_CB(skb);
+
+ switch (skb_cb->cipher) {
+ /* Cipher suite having flag %IEEE80211_KEY_FLAG_GENERATE_IV_MGMT set in
+ * key needs to be processed. See ath11k_install_key()
+ */
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ iv_len = IEEE80211_CCMP_HDR_LEN;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ iv_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (remaining_len < iv_len)
+ return -EINVAL;
+
+ buf += iv_len;
+ remaining_len -= iv_len;
+ }
+
+ category = *buf++;
+ /* category code is already taken care in %IEEE80211_MIN_ACTION_SIZE hence
+ * no need to adjust remaining_len
+ */
+
+ switch (category) {
+ case WLAN_CATEGORY_RADIO_MEASUREMENT:
+ /* need action code and dialog token */
+ if (remaining_len < 2)
+ return -EINVAL;
+
+ /* Packet Format:
+ * Action Code | Dialog Token | Variable Len (based on Action Code)
+ */
+ action_code = *buf++;
+ dialog_token = *buf++;
+ remaining_len -= 2;
+
+ if (ath11k_mac_vif_chan(arvif->vif, &def))
+ return -ENOENT;
+
+ cur_tx_power = arvif->vif->bss_conf.txpower;
+ max_tx_power = min(def.chan->max_reg_power, (int)ar->max_tx_power / 2);
+ ath11k_mac_handle_get_txpower(ar, arvif->vif, &cur_tx_power);
+
+ switch (action_code) {
+ case WLAN_RM_ACTION_LINK_MEASUREMENT_REQUEST:
+ /* need variable fields to be present in len */
+ if (remaining_len < 2)
+ return -EINVAL;
+
+ /* Variable length format as defined in IEEE 802.11-2024,
+ * Figure 9-1187-Link Measurement Request frame Action field
+ * format.
+ * Transmit Power | Max Tx Power
+ * We fill both of these.
+ */
+ *buf++ = cur_tx_power;
+ *buf = max_tx_power;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "RRM: Link Measurement Req dialog_token %u cur_tx_power %d max_tx_power %d\n",
+ dialog_token, cur_tx_power, max_tx_power);
+ break;
+ case WLAN_RM_ACTION_LINK_MEASUREMENT_REPORT:
+ /* need variable fields to be present in len */
+ if (remaining_len < 3)
+ return -EINVAL;
+
+ /* Variable length format as defined in IEEE 802.11-2024,
+ * Figure 9-1188-Link Measurement Report frame Action field format
+ * TPC Report | Variable Fields
+ *
+ * TPC Report Format:
+ * Element ID | Len | Tx Power | Link Margin
+ *
+ * We fill Tx power in the TPC Report (2nd index)
+ */
+ buf[2] = cur_tx_power;
+
+ /* TODO: At present, Link margin data is not present so can't
+ * really fill it now. Once it is available, it can be added
+ * here
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "RRM: Link Measurement Report dialog_token %u cur_tx_power %d\n",
+ dialog_token, cur_tx_power);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* nothing to fill */
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ath11k_mac_mgmt_frame_fill_elem_data(struct ath11k_vif *arvif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_action(hdr->frame_control))
+ return 0;
+
+ return ath11k_mac_mgmt_action_frame_fill_elem_data(arvif, skb);
+}
+
static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
{
struct ath11k *ar = container_of(work, struct ath11k, wmi_mgmt_tx_work);
@@ -6126,6 +6423,19 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
arvif = ath11k_vif_to_arvif(skb_cb->vif);
mutex_lock(&ar->conf_mutex);
if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) {
+ /* Fill in the data which is required to be filled by the driver
+ * For example: Max Tx power in Link Measurement Request/Report
+ */
+ ret = ath11k_mac_mgmt_frame_fill_elem_data(arvif, skb);
+ if (ret) {
+ /* If we couldn't fill the data due to any reason,
+ * let's not discard transmitting the packet.
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "Failed to fill the required data for the mgmt packet err %d\n",
+ ret);
+ }
+
ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb);
if (ret) {
ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n",
@@ -9079,81 +9389,6 @@ static void ath11k_mac_put_chain_rssi(struct station_info *sinfo,
}
}
-static void ath11k_mac_fw_stats_reset(struct ath11k *ar)
-{
- spin_lock_bh(&ar->data_lock);
- ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
- ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
- ar->fw_stats.num_vdev_recvd = 0;
- ar->fw_stats.num_bcn_recvd = 0;
- spin_unlock_bh(&ar->data_lock);
-}
-
-int ath11k_mac_fw_stats_request(struct ath11k *ar,
- struct stats_request_params *req_param)
-{
- struct ath11k_base *ab = ar->ab;
- unsigned long time_left;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- ath11k_mac_fw_stats_reset(ar);
-
- reinit_completion(&ar->fw_stats_complete);
- reinit_completion(&ar->fw_stats_done);
-
- ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
-
- if (ret) {
- ath11k_warn(ab, "could not request fw stats (%d)\n",
- ret);
- return ret;
- }
-
- time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
- if (!time_left)
- return -ETIMEDOUT;
-
- /* FW stats can get split when exceeding the stats data buffer limit.
- * In that case, since there is no end marking for the back-to-back
- * received 'update stats' event, we keep a 3 seconds timeout in case,
- * fw_stats_done is not marked yet
- */
- time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
- if (!time_left)
- return -ETIMEDOUT;
-
- return 0;
-}
-
-static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id,
- u32 vdev_id, u32 stats_id)
-{
- struct ath11k_base *ab = ar->ab;
- struct stats_request_params req_param;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- if (ar->state != ATH11K_STATE_ON)
- return -ENETDOWN;
-
- req_param.pdev_id = pdev_id;
- req_param.vdev_id = vdev_id;
- req_param.stats_id = stats_id;
-
- ret = ath11k_mac_fw_stats_request(ar, &req_param);
- if (ret)
- ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
-
- ath11k_dbg(ab, ATH11K_DBG_WMI,
- "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
- pdev_id, vdev_id, stats_id);
-
- return ret;
-}
-
static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -9539,66 +9774,6 @@ exit:
return ret;
}
-static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- unsigned int link_id,
- int *dbm)
-{
- struct ath11k *ar = hw->priv;
- struct ath11k_base *ab = ar->ab;
- struct ath11k_fw_stats_pdev *pdev;
- int ret;
-
- /* Final Tx power is minimum of Target Power, CTL power, Regulatory
- * Power, PSD EIRP Power. We just know the Regulatory power from the
- * regulatory rules obtained. FW knows all these power and sets the min
- * of these. Hence, we request the FW pdev stats in which FW reports
- * the minimum of all vdev's channel Tx power.
- */
- mutex_lock(&ar->conf_mutex);
-
- /* Firmware doesn't provide Tx power during CAC hence no need to fetch
- * the stats.
- */
- if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) {
- mutex_unlock(&ar->conf_mutex);
- return -EAGAIN;
- }
-
- ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
- WMI_REQUEST_PDEV_STAT);
- if (ret) {
- ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
- goto err_fallback;
- }
-
- spin_lock_bh(&ar->data_lock);
- pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
- struct ath11k_fw_stats_pdev, list);
- if (!pdev) {
- spin_unlock_bh(&ar->data_lock);
- goto err_fallback;
- }
-
- /* tx power is set as 2 units per dBm in FW. */
- *dbm = pdev->chan_tx_power / 2;
-
- spin_unlock_bh(&ar->data_lock);
- mutex_unlock(&ar->conf_mutex);
-
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware %d, reported %d dBm\n",
- pdev->chan_tx_power, *dbm);
- return 0;
-
-err_fallback:
- mutex_unlock(&ar->conf_mutex);
- /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
- *dbm = vif->bss_conf.txpower;
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
- *dbm);
- return 0;
-}
-
static int ath11k_mac_station_add(struct ath11k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
@@ -10368,6 +10543,8 @@ static int __ath11k_mac_register(struct ath11k *ar)
ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_AP_SCAN;
+ ar->hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
+
ar->max_num_stations = TARGET_NUM_STATIONS(ab);
ar->max_num_peers = TARGET_NUM_PEERS_PDEV(ab);
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index d8655badd96d..7114eca8810d 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/module.h>
@@ -177,6 +177,19 @@ static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci)
ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
}
+static void ath11k_pci_restore_window(struct ath11k_base *ab)
+{
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+
+ spin_lock_bh(&ab_pci->window_lock);
+
+ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | ab_pci->register_window,
+ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
+ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
+
+ spin_unlock_bh(&ab_pci->window_lock);
+}
+
static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
{
u32 val, delay;
@@ -201,6 +214,11 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
if (val == 0xffffffff)
ath11k_warn(ab, "link down error during global reset\n");
+
+ /* Restore window register as its content is cleared during
+ * hardware global reset, such that it aligns with host cache.
+ */
+ ath11k_pci_restore_window(ab);
}
static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
index c33c7865145c..1e3005a4b64c 100644
--- a/drivers/net/wireless/ath/ath11k/pci.h
+++ b/drivers/net/wireless/ath/ath11k/pci.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 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.
*/
#ifndef _ATH11K_PCI_H
#define _ATH11K_PCI_H
@@ -35,18 +35,18 @@
#define PCIE_SMLH_REQ_RST_LINK_DOWN 0x2
#define PCIE_INT_CLEAR_ALL 0xffffffff
-#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(x) \
- (ab->hw_params.regs->pcie_qserdes_sysclk_en_sel)
+#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab) \
+ ((ab)->hw_params.regs->pcie_qserdes_sysclk_en_sel)
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL 0x10
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK 0xffffffff
-#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base)
+#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base)
#define PCIE_PCS_OSC_DTCT_CONFIG1_VAL 0x02
-#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4)
+#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4)
#define PCIE_PCS_OSC_DTCT_CONFIG2_VAL 0x52
-#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc)
+#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc)
#define PCIE_PCS_OSC_DTCT_CONFIG4_VAL 0xff
#define PCIE_PCS_OSC_DTCT_CONFIG_MSK 0x000000ff
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index aea56c38bf8f..ff6a97e328b8 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -3177,7 +3177,7 @@ static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl,
sq->sq_node = service->node;
sq->sq_port = service->port;
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)sq,
sizeof(*sq), 0);
if (ret) {
ath11k_warn(ab, "failed to connect to qmi remote service: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index e3b444333dee..110035dae8a6 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
@@ -2061,10 +2061,13 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
cmd->peer_bw_rxnss_override |= param->peer_bw_rxnss_override;
if (param->vht_capable) {
- mcs->rx_max_rate = param->rx_max_rate;
- mcs->rx_mcs_set = param->rx_mcs_set;
- mcs->tx_max_rate = param->tx_max_rate;
- mcs->tx_mcs_set = param->tx_mcs_set;
+ /* firmware interprets mcs->tx_mcs_set field as peer's
+ * RX capability
+ */
+ mcs->tx_max_rate = param->rx_max_rate;
+ mcs->tx_mcs_set = param->rx_mcs_set;
+ mcs->rx_max_rate = param->tx_max_rate;
+ mcs->rx_mcs_set = param->tx_mcs_set;
}
/* HE Rates */
@@ -2088,8 +2091,11 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
FIELD_PREP(WMI_TLV_LEN,
sizeof(*he_mcs) - TLV_HDR_SIZE);
- he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i];
- he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i];
+ /* firmware interprets mcs->rx_mcs_set field as peer's
+ * RX capability
+ */
+ he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i];
+ he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i];
ptr += sizeof(*he_mcs);
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 9fcffaa2f383..0f0de24a3840 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_WMI_H
@@ -3463,20 +3463,6 @@ struct scan_cancel_param {
u32 pdev_id;
};
-struct wmi_bcn_send_from_host_cmd {
- u32 tlv_header;
- u32 vdev_id;
- u32 data_len;
- union {
- u32 frag_ptr;
- u32 frag_ptr_lo;
- };
- u32 frame_ctrl;
- u32 dtim_flag;
- u32 bcn_antenna;
- u32 frag_ptr_hi;
-};
-
#define WMI_CHAN_INFO_MODE GENMASK(5, 0)
#define WMI_CHAN_INFO_HT40_PLUS BIT(6)
#define WMI_CHAN_INFO_PASSIVE BIT(7)
@@ -4133,8 +4119,10 @@ struct wmi_rate_set {
struct wmi_vht_rate_set {
u32 tlv_header;
u32 rx_max_rate;
+ /* MCS at which the peer can transmit */
u32 rx_mcs_set;
u32 tx_max_rate;
+ /* MCS at which the peer can receive */
u32 tx_mcs_set;
u32 tx_max_mcs_nss;
} __packed;
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 5d494c5cdc0d..cc352eef1939 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1,7 +1,6 @@
// 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.
*/
@@ -1250,7 +1249,6 @@ void ath12k_fw_stats_reset(struct ath12k *ar)
spin_lock_bh(&ar->data_lock);
ath12k_fw_stats_free(&ar->fw_stats);
ar->fw_stats.num_vdev_recvd = 0;
- ar->fw_stats.num_bcn_recvd = 0;
spin_unlock_bh(&ar->data_lock);
}
@@ -2106,14 +2104,27 @@ static int ath12k_core_hw_group_create(struct ath12k_hw_group *ag)
ret = ath12k_core_soc_create(ab);
if (ret) {
mutex_unlock(&ab->core_lock);
- ath12k_err(ab, "failed to create soc core: %d\n", ret);
- return ret;
+ ath12k_err(ab, "failed to create soc %d core: %d\n", i, ret);
+ goto destroy;
}
mutex_unlock(&ab->core_lock);
}
return 0;
+
+destroy:
+ for (i--; i >= 0; i--) {
+ ab = ag->ab[i];
+ if (!ab)
+ continue;
+
+ mutex_lock(&ab->core_lock);
+ ath12k_core_soc_destroy(ab);
+ mutex_unlock(&ab->core_lock);
+ }
+
+ return ret;
}
void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
@@ -2188,7 +2199,7 @@ int ath12k_core_init(struct ath12k_base *ab)
if (ret) {
mutex_unlock(&ag->mutex);
ath12k_warn(ab, "unable to create hw group\n");
- goto err_destroy_hw_group;
+ goto err_unassign_hw_group;
}
}
@@ -2196,8 +2207,7 @@ int ath12k_core_init(struct ath12k_base *ab)
return 0;
-err_destroy_hw_group:
- ath12k_core_hw_group_destroy(ab->ag);
+err_unassign_hw_group:
ath12k_core_hw_group_unassign(ab);
err_unregister_notifier:
ath12k_core_panic_notifier_unregister(ab);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 3d1956966a48..3c1e0069be1e 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -355,6 +355,8 @@ struct ath12k_link_vif {
struct wmi_vdev_install_key_arg group_key;
bool pairwise_key_done;
u16 num_stations;
+ bool is_csa_in_progress;
+ struct wiphy_work bcn_tx_work;
};
struct ath12k_vif {
@@ -644,7 +646,6 @@ struct ath12k_fw_stats {
struct list_head vdevs;
struct list_head bcn;
u32 num_vdev_recvd;
- u32 num_bcn_recvd;
};
struct ath12k_dbg_htt_stats {
@@ -963,6 +964,7 @@ struct ath12k_device_dp_stats {
u32 tx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX];
u32 tx_enqueued[DP_TCL_NUM_RING_MAX];
u32 tx_completed[DP_TCL_NUM_RING_MAX];
+ u32 reo_excep_msdu_buf_type;
};
struct ath12k_reg_freq {
diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c
index 16601a8c3644..d6a86f075d73 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs.c
+++ b/drivers/net/wireless/ath/ath12k/debugfs.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"
@@ -1178,6 +1178,9 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file,
len += scnprintf(buf + len, size - len, "\n");
}
+ len += scnprintf(buf + len, size - len, "\nREO excep MSDU buf type:%u\n",
+ device_stats->reo_excep_msdu_buf_type);
+
len += scnprintf(buf + len, size - len, "\nRx WBM REL SRC Errors:\n");
for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) {
@@ -1283,6 +1286,7 @@ static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
@@ -1349,12 +1353,7 @@ static int ath12k_open_bcn_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
- /* since beacon stats request is looped for all active VDEVs, saved fw
- * stats is not freed for each request until done for all active VDEVs
- */
- spin_lock_bh(&ar->data_lock);
- ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
- spin_unlock_bh(&ar->data_lock);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
@@ -1415,6 +1414,7 @@ static int ath12k_open_pdev_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 009c49502148..39d1967584db 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.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-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "dp_mon.h"
@@ -105,7 +105,7 @@ static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vh
if (ppdu_info->is_stbc && nsts > 0)
nsts = ((nsts + 1) >> 1) - 1;
- ppdu_info->nss = u32_get_bits(nsts, VHT_SIG_SU_NSS_MASK);
+ ppdu_info->nss = u32_get_bits(nsts, VHT_SIG_SU_NSS_MASK) + 1;
ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW);
ppdu_info->beamformed = u32_get_bits(info1,
HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED);
@@ -129,7 +129,7 @@ static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig,
ppdu_info->is_stbc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_STBC);
ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING);
ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI);
- ppdu_info->nss = (ppdu_info->mcs >> 3);
+ ppdu_info->nss = (ppdu_info->mcs >> 3) + 1;
}
static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
@@ -233,7 +233,9 @@ ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *of
value = value << HE_STA_ID_SHIFT;
ppdu_info->he_data4 |= value;
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS);
+ ppdu_info->nss =
+ u32_get_bits(info0,
+ HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS) + 1;
ppdu_info->beamformed = u32_get_bits(info0,
HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF);
}
@@ -261,7 +263,9 @@ ath12k_dp_mon_parse_he_sig_b2_mu(const struct hal_rx_he_sig_b2_mu_info *he_sig_b
value = value << HE_STA_ID_SHIFT;
ppdu_info->he_data4 |= value;
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS);
+ ppdu_info->nss =
+ u32_get_bits(info0,
+ HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS) + 1;
}
static void
@@ -553,7 +557,8 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *
ppdu_info->is_stbc = u32_get_bits(info1, HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC);
ppdu_info->beamformed = u32_get_bits(info1, HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF);
dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM);
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS);
+ ppdu_info->nss = u32_get_bits(info0,
+ HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS) + 1;
ppdu_info->dcm = dcm;
}
@@ -2179,7 +2184,7 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar,
spin_unlock_bh(&ar->data_lock);
rxs->flag |= RX_FLAG_MACTIME_START;
- rxs->nss = ppduinfo->nss + 1;
+ rxs->nss = ppduinfo->nss;
if (test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT,
ar->ab->wmi_ab.svc_map))
rxs->signal = ppduinfo->rssi_comb;
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index 5e5c14a70316..d28d8ffec0f8 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.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 <linux/ieee80211.h>
@@ -1089,6 +1089,8 @@ static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp,
{
struct dp_reo_update_rx_queue_elem *elem;
+ lockdep_assert_held(&dp->ab->base_lock);
+
elem = kzalloc(sizeof(*elem), GFP_ATOMIC);
if (!elem)
return -ENOMEM;
@@ -3781,6 +3783,50 @@ exit:
return 0;
}
+static int ath12k_dp_h_msdu_buffer_type(struct ath12k_base *ab,
+ struct list_head *list,
+ struct hal_reo_dest_ring *desc)
+{
+ struct ath12k_rx_desc_info *desc_info;
+ struct ath12k_skb_rxcb *rxcb;
+ struct sk_buff *msdu;
+ u64 desc_va;
+
+ ab->device_stats.reo_excep_msdu_buf_type++;
+
+ desc_va = (u64)le32_to_cpu(desc->buf_va_hi) << 32 |
+ le32_to_cpu(desc->buf_va_lo);
+ desc_info = (struct ath12k_rx_desc_info *)(uintptr_t)desc_va;
+ if (!desc_info) {
+ u32 cookie;
+
+ cookie = le32_get_bits(desc->buf_addr_info.info1,
+ BUFFER_ADDR_INFO1_SW_COOKIE);
+ desc_info = ath12k_dp_get_rx_desc(ab, cookie);
+ if (!desc_info) {
+ ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n",
+ cookie);
+ return -EINVAL;
+ }
+ }
+
+ if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) {
+ ath12k_warn(ab, "rx exception, magic check failed with value: %u\n",
+ desc_info->magic);
+ return -EINVAL;
+ }
+
+ msdu = desc_info->skb;
+ desc_info->skb = NULL;
+ list_add_tail(&desc_info->list, list);
+ rxcb = ATH12K_SKB_RXCB(msdu);
+ dma_unmap_single(ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu),
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(msdu);
+
+ return 0;
+}
+
int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
int budget)
{
@@ -3825,6 +3871,26 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
drop = false;
ab->device_stats.err_ring_pkts++;
+ hw_link_id = le32_get_bits(reo_desc->info0,
+ HAL_REO_DEST_RING_INFO0_SRC_LINK_ID);
+ device_id = hw_links[hw_link_id].device_id;
+ partner_ab = ath12k_ag_to_ab(ag, device_id);
+
+ /* Below case is added to handle data packet from un-associated clients.
+ * As it is expected that AST lookup will fail for
+ * un-associated station's data packets.
+ */
+ if (le32_get_bits(reo_desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE) ==
+ HAL_REO_DEST_RING_BUFFER_TYPE_MSDU) {
+ if (!ath12k_dp_h_msdu_buffer_type(partner_ab,
+ &rx_desc_used_list[device_id],
+ reo_desc)) {
+ num_buffs_reaped[device_id]++;
+ tot_n_bufs_reaped++;
+ }
+ goto next_desc;
+ }
+
ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr,
&desc_bank);
if (ret) {
@@ -3833,11 +3899,6 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
continue;
}
- hw_link_id = le32_get_bits(reo_desc->info0,
- HAL_REO_DEST_RING_INFO0_SRC_LINK_ID);
- device_id = hw_links[hw_link_id].device_id;
- partner_ab = ath12k_ag_to_ab(ag, device_id);
-
pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params,
hw_links[hw_link_id].pdev_idx);
ar = partner_ab->pdevs[pdev_id].ar;
@@ -3886,6 +3947,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
}
}
+next_desc:
if (tot_n_bufs_reaped >= quota) {
tot_n_bufs_reaped = quota;
goto exit;
diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c
index 669096278fdd..c4443ca05cd6 100644
--- a/drivers/net/wireless/ath/ath12k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath12k/hal_rx.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 "debug.h"
@@ -323,7 +323,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
{
enum hal_reo_dest_ring_push_reason push_reason;
enum hal_reo_dest_ring_error_code err_code;
- u32 cookie, val;
+ u32 cookie;
push_reason = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_PUSH_REASON);
@@ -338,12 +338,6 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
return -EINVAL;
}
- val = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE);
- if (val != HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) {
- ath12k_warn(ab, "expected buffer type link_desc");
- return -EINVAL;
- }
-
ath12k_hal_rx_reo_ent_paddr_get(ab, &desc->buf_addr_info, paddr, &cookie);
*desc_bank = u32_get_bits(cookie, DP_LINK_DESC_BANK_MASK);
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index db351c922018..f7a2a544bef2 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -533,6 +533,30 @@ ath12k_mac_max_he_nss(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
return 1;
}
+static u32
+ath12k_mac_max_eht_nss(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+
+ for (nss = NL80211_EHT_NSS_MAX - 1; nss >= 0; nss--)
+ if (eht_mcs_mask[nss])
+ return nss + 1;
+
+ return 1;
+}
+
+static u32
+ath12k_mac_max_eht_mcs_nss(const u8 *eht_mcs, int eht_mcs_set_size)
+{
+ int i;
+ u8 nss = 0;
+
+ for (i = 0; i < eht_mcs_set_size; i++)
+ nss = max(nss, u8_get_bits(eht_mcs[i], IEEE80211_EHT_MCS_NSS_RX));
+
+ return nss;
+}
+
static u8 ath12k_parse_mpdudensity(u8 mpdudensity)
{
/* From IEEE Std 802.11-2020 defined values for "Minimum MPDU Start Spacing":
@@ -2249,7 +2273,6 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
struct cfg80211_chan_def def;
enum nl80211_band band;
u16 *vht_mcs_mask;
- u16 tx_mcs_map;
u8 ampdu_factor;
u8 max_nss, vht_mcs;
int i, vht_nss, nss_idx;
@@ -2340,10 +2363,10 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
arg->peer_nss = min(link_sta->rx_nss, max_nss);
arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
- arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+ arg->rx_mcs_set = ath12k_peer_assoc_h_vht_limit(arg->rx_mcs_set, vht_mcs_mask);
- tx_mcs_map = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
- arg->tx_mcs_set = ath12k_peer_assoc_h_vht_limit(tx_mcs_map, vht_mcs_mask);
+ arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+ arg->tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
/* In QCN9274 platform, VHT MCS rate 10 and 11 is enabled by default.
* VHT MCS rate 10 and 11 is not supported in 11ac standard.
@@ -2625,9 +2648,10 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
- v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
+ v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
arg->peer_he_mcs_count++;
@@ -2637,10 +2661,10 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
default:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
- v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
arg->peer_he_mcs_count++;
@@ -3004,6 +3028,18 @@ static enum wmi_phy_mode ath12k_mac_get_phymode_eht(struct ath12k *ar,
return MODE_UNKNOWN;
}
+static bool
+ath12k_peer_assoc_h_eht_masked(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+
+ for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++)
+ if (eht_mcs_mask[nss])
+ return false;
+
+ return true;
+}
+
static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
@@ -3015,6 +3051,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
enum wmi_phy_mode phymode = MODE_UNKNOWN;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -3029,6 +3066,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
link_sta = ath12k_mac_get_link_sta(arsta);
if (!link_sta) {
@@ -3039,7 +3077,8 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
switch (band) {
case NL80211_BAND_2GHZ:
- if (link_sta->eht_cap.has_eht) {
+ if (link_sta->eht_cap.has_eht &&
+ !ath12k_peer_assoc_h_eht_masked(eht_mcs_mask)) {
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11BE_EHT40_2G;
else
@@ -3102,37 +3141,50 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
WARN_ON(phymode == MODE_UNKNOWN);
}
+#define ATH12K_EHT_MCS_7_ENABLED 0x00FF
+#define ATH12K_EHT_MCS_9_ENABLED 0x0300
+#define ATH12K_EHT_MCS_11_ENABLED 0x0C00
+#define ATH12K_EHT_MCS_13_ENABLED 0x3000
+
static void ath12k_mac_set_eht_mcs(u8 rx_tx_mcs7, u8 rx_tx_mcs9,
u8 rx_tx_mcs11, u8 rx_tx_mcs13,
- u32 *rx_mcs, u32 *tx_mcs)
-{
- *rx_mcs = 0;
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_0_7);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_8_9);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_10_11);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_12_13);
-
- *tx_mcs = 0;
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_0_7);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_8_9);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_10_11);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_12_13);
+ u32 *rx_mcs, u32 *tx_mcs,
+ const u16 eht_mcs_limit[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+ u8 mcs_7 = 0, mcs_9 = 0, mcs_11 = 0, mcs_13 = 0;
+ u8 peer_mcs_7, peer_mcs_9, peer_mcs_11, peer_mcs_13;
+
+ for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) {
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_7_ENABLED)
+ mcs_7++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_9_ENABLED)
+ mcs_9++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_11_ENABLED)
+ mcs_11++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_13_ENABLED)
+ mcs_13++;
+ }
+
+ peer_mcs_7 = u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_9 = u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_11 = u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_13 = u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_RX);
+
+ *rx_mcs = u32_encode_bits(min(peer_mcs_7, mcs_7), WMI_EHT_MCS_NSS_0_7) |
+ u32_encode_bits(min(peer_mcs_9, mcs_9), WMI_EHT_MCS_NSS_8_9) |
+ u32_encode_bits(min(peer_mcs_11, mcs_11), WMI_EHT_MCS_NSS_10_11) |
+ u32_encode_bits(min(peer_mcs_13, mcs_13), WMI_EHT_MCS_NSS_12_13);
+
+ peer_mcs_7 = u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_9 = u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_11 = u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_13 = u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_TX);
+
+ *tx_mcs = u32_encode_bits(min(peer_mcs_7, mcs_7), WMI_EHT_MCS_NSS_0_7) |
+ u32_encode_bits(min(peer_mcs_9, mcs_9), WMI_EHT_MCS_NSS_8_9) |
+ u32_encode_bits(min(peer_mcs_11, mcs_11), WMI_EHT_MCS_NSS_10_11) |
+ u32_encode_bits(min(peer_mcs_13, mcs_13), WMI_EHT_MCS_NSS_12_13);
}
static void ath12k_mac_set_eht_ppe_threshold(const u8 *ppe_thres,
@@ -3171,13 +3223,22 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
struct ath12k_wmi_peer_assoc_arg *arg)
{
struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ const struct ieee80211_eht_mcs_nss_supp *own_eht_mcs_nss_supp;
const struct ieee80211_eht_mcs_nss_supp_20mhz_only *bw_20;
+ const struct ieee80211_sta_eht_cap *eht_cap, *own_eht_cap;
+ const struct ieee80211_sband_iftype_data *iftd;
const struct ieee80211_eht_mcs_nss_supp_bw *bw;
- const struct ieee80211_sta_eht_cap *eht_cap;
const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_link_sta *link_sta;
struct ieee80211_bss_conf *link_conf;
+ struct cfg80211_chan_def def;
+ bool user_rate_valid = true;
+ enum nl80211_band band;
+ int eht_nss, nss_idx;
u32 *rx_mcs, *tx_mcs;
+ u16 *eht_mcs_mask;
+ u8 max_nss = 0;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -3199,6 +3260,22 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
if (!he_cap->has_he || !eht_cap->has_eht)
return;
+ if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
+ return;
+
+ band = def.chan->band;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
+
+ iftd = ieee80211_get_sband_iftype_data(&ar->mac.sbands[band], vif->type);
+ if (!iftd) {
+ ath12k_warn(ar->ab,
+ "unable to access iftype_data in struct ieee80211_supported_band\n");
+ return;
+ }
+
+ own_eht_cap = &iftd->eht_cap;
+ own_eht_mcs_nss_supp = &own_eht_cap->eht_mcs_nss_supp;
+
arg->eht_flag = true;
if ((eht_cap->eht_cap_elem.phy_cap_info[5] &
@@ -3215,6 +3292,28 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
rx_mcs = arg->peer_eht_rx_mcs_set;
tx_mcs = arg->peer_eht_tx_mcs_set;
+ eht_nss = ath12k_mac_max_eht_mcs_nss((void *)own_eht_mcs_nss_supp,
+ sizeof(*own_eht_mcs_nss_supp));
+ if (eht_nss > link_sta->rx_nss) {
+ user_rate_valid = false;
+ for (nss_idx = (link_sta->rx_nss - 1); nss_idx >= 0; nss_idx--) {
+ if (eht_mcs_mask[nss_idx]) {
+ user_rate_valid = true;
+ break;
+ }
+ }
+ }
+
+ if (!user_rate_valid) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "Setting eht range MCS value to peer supported nss %d for peer %pM\n",
+ link_sta->rx_nss, arsta->addr);
+ eht_mcs_mask[link_sta->rx_nss - 1] = eht_mcs_mask[eht_nss - 1];
+ }
+
+ bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
+ bw = &eht_cap->eht_mcs_nss_supp.bw._80;
+
switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_320:
bw = &eht_cap->eht_mcs_nss_supp.bw._320;
@@ -3223,7 +3322,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320],
+ eht_mcs_mask);
arg->peer_eht_mcs_count++;
fallthrough;
case IEEE80211_STA_RX_BW_160:
@@ -3233,15 +3333,13 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160],
+ eht_mcs_mask);
arg->peer_eht_mcs_count++;
fallthrough;
default:
- if ((he_cap->he_cap_elem.phy_cap_info[0] &
- (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) {
+ if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
ath12k_mac_set_eht_mcs(bw_20->rx_tx_mcs7_max_nss,
@@ -3249,7 +3347,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw_20->rx_tx_mcs11_max_nss,
bw_20->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
+ eht_mcs_mask);
} else {
bw = &eht_cap->eht_mcs_nss_supp.bw._80;
ath12k_mac_set_eht_mcs(bw->rx_tx_mcs9_max_nss,
@@ -3257,7 +3356,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
+ eht_mcs_mask);
}
arg->peer_eht_mcs_count++;
@@ -3266,6 +3366,41 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->punct_bitmap = ~arvif->punct_bitmap;
arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15;
+
+ if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
+ if (bw_20->rx_tx_mcs13_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs11_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs9_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs7_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs7_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ } else {
+ if (bw->rx_tx_mcs13_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw->rx_tx_mcs11_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw->rx_tx_mcs9_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ }
+
+ max_nss = min(max_nss, (uint8_t)eht_nss);
+
+ arg->peer_nss = min(link_sta->rx_nss, max_nss);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "mac eht peer %pM nss %d mcs cnt %d ru_punct_bitmap 0x%x\n",
+ arsta->addr, arg->peer_nss, arg->peer_eht_mcs_count,
+ arg->punct_bitmap);
}
static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
@@ -3834,6 +3969,38 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar,
ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret);
}
+static void ath12k_mac_bcn_tx_event(struct ath12k_link_vif *arvif)
+{
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ struct ieee80211_bss_conf *link_conf;
+
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(arvif->ar->ab, "failed to get link conf for vdev %u\n",
+ arvif->vdev_id);
+ return;
+ }
+
+ if (link_conf->color_change_active) {
+ if (ieee80211_beacon_cntdwn_is_complete(vif, arvif->link_id)) {
+ ieee80211_color_change_finish(vif, arvif->link_id);
+ return;
+ }
+
+ ieee80211_beacon_update_cntdwn(vif, arvif->link_id);
+ ath12k_mac_setup_bcn_tmpl(arvif);
+ }
+}
+
+static void ath12k_mac_bcn_tx_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct ath12k_link_vif *arvif = container_of(work, struct ath12k_link_vif,
+ bcn_tx_work);
+
+ lockdep_assert_wiphy(wiphy);
+ ath12k_mac_bcn_tx_event(arvif);
+}
+
static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
struct ath12k_link_vif *arvif, int link_id)
{
@@ -3863,6 +4030,7 @@ static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
INIT_LIST_HEAD(&arvif->list);
INIT_DELAYED_WORK(&arvif->connection_loss_work,
ath12k_mac_vif_sta_connection_loss_work);
+ wiphy_work_init(&arvif->bcn_tx_work, ath12k_mac_bcn_tx_work);
arvif->num_stations = 0;
@@ -3875,6 +4043,8 @@ static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
sizeof(arvif->bitrate_mask.control[i].vht_mcs));
memset(arvif->bitrate_mask.control[i].he_mcs, 0xff,
sizeof(arvif->bitrate_mask.control[i].he_mcs));
+ memset(arvif->bitrate_mask.control[i].eht_mcs, 0xff,
+ sizeof(arvif->bitrate_mask.control[i].eht_mcs));
}
/* Handle MLO related assignments */
@@ -3900,6 +4070,7 @@ static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,
lockdep_assert_wiphy(ah->hw->wiphy);
cancel_delayed_work_sync(&arvif->connection_loss_work);
+ wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &arvif->bcn_tx_work);
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)",
arvif->vdev_id, arvif->link_id);
@@ -4202,6 +4373,30 @@ static bool ath12k_mac_supports_tpc(struct ath12k *ar, struct ath12k_vif *ahvif,
chandef->chan->band == NL80211_BAND_6GHZ;
}
+static void ath12k_wmi_vdev_params_up(struct ath12k *ar,
+ struct ath12k_link_vif *arvif,
+ struct ath12k_link_vif *tx_arvif,
+ struct ieee80211_bss_conf *info, u16 aid)
+{
+ struct ath12k_wmi_vdev_up_params params = {
+ .vdev_id = arvif->vdev_id,
+ .aid = aid,
+ .bssid = arvif->bssid
+ };
+ int ret;
+
+ if (tx_arvif) {
+ params.tx_bssid = tx_arvif->bssid;
+ params.nontx_profile_idx = info->bssid_index;
+ params.nontx_profile_cnt = 1 << info->bssid_indicator;
+ }
+
+ ret = ath12k_wmi_vdev_up(arvif->ar, &params);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to bring vdev up %d: %d\n",
+ arvif->vdev_id, ret);
+}
+
static void ath12k_mac_bss_info_changed(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *info,
@@ -4210,6 +4405,7 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
struct ath12k_vif *ahvif = arvif->ahvif;
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
struct ieee80211_vif_cfg *vif_cfg = &vif->cfg;
+ struct ath12k_link_vif *tx_arvif;
struct cfg80211_chan_def def;
u32 param_id, param_value;
enum nl80211_band band;
@@ -4218,9 +4414,9 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
u32 preamble;
u16 hw_value;
u16 bitrate;
- int ret;
u8 rateidx;
u32 rate;
+ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -4253,12 +4449,41 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
"Set burst beacon mode for VDEV: %d\n",
arvif->vdev_id);
+ /* In MBSSID case, need to install transmitting VIF's template first */
+
ret = ath12k_mac_setup_bcn_tmpl(arvif);
if (ret)
ath12k_warn(ar->ab, "failed to update bcn template: %d\n",
ret);
+
+ if (!arvif->is_csa_in_progress)
+ goto skip_vdev_up;
+
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, info);
+ if (tx_arvif && arvif != tx_arvif && tx_arvif->is_csa_in_progress)
+ /* skip non tx vif's */
+ goto skip_vdev_up;
+
+ ath12k_wmi_vdev_params_up(ar, arvif, tx_arvif, info, ahvif->aid);
+
+ arvif->is_csa_in_progress = false;
+
+ if (tx_arvif && arvif == tx_arvif) {
+ struct ath12k_link_vif *arvif_itr;
+
+ list_for_each_entry(arvif_itr, &ar->arvifs, list) {
+ if (!arvif_itr->is_csa_in_progress)
+ continue;
+
+ ath12k_wmi_vdev_params_up(ar, arvif, tx_arvif,
+ info, ahvif->aid);
+ arvif_itr->is_csa_in_progress = false;
+ }
+ }
}
+skip_vdev_up:
+
if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
arvif->dtim_period = info->dtim_period;
@@ -4475,8 +4700,25 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
ATH12K_BSS_COLOR_AP_PERIODS,
info->he_bss_color.enabled);
if (ret)
- ath12k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n",
+ ath12k_warn(ar->ab, "failed to set bss color collision on vdev %u: %d\n",
arvif->vdev_id, ret);
+
+ param_id = WMI_VDEV_PARAM_BSS_COLOR;
+ if (info->he_bss_color.enabled)
+ param_value = info->he_bss_color.color <<
+ IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET;
+ else
+ param_value = IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED;
+
+ ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+ param_id,
+ param_value);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to set bss color param on vdev %u: %d\n",
+ arvif->vdev_id, ret);
+ else
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "bss color param 0x%x set on vdev %u\n",
+ param_value, arvif->vdev_id);
} else if (vif->type == NL80211_IFTYPE_STATION) {
ret = ath12k_wmi_send_bss_color_change_enable_cmd(ar,
arvif->vdev_id,
@@ -4837,8 +5079,6 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
if (ah->state != ATH12K_HW_STATE_ON)
return -ENETDOWN;
- ath12k_fw_stats_reset(ar);
-
reinit_completion(&ar->fw_stats_complete);
reinit_completion(&ar->fw_stats_done);
@@ -4936,6 +5176,7 @@ static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
ar->chan_tx_pwr = pdev->chan_tx_power / 2;
spin_unlock_bh(&ar->data_lock);
ar->last_tx_power_update = jiffies;
+ ath12k_fw_stats_reset(ar);
send_tx_power:
*dbm = ar->chan_tx_pwr;
@@ -5059,7 +5300,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
ret = ath12k_mac_vdev_create(ar, arvif);
if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev %d\n", ret);
- return -EINVAL;
+ ath12k_mac_unassign_link_vif(arvif);
+ return ret;
}
}
@@ -5719,6 +5961,20 @@ ath12k_mac_bitrate_mask_num_he_rates(struct ath12k *ar,
}
static int
+ath12k_mac_bitrate_mask_num_eht_rates(struct ath12k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int num_rates = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++)
+ num_rates += hweight16(mask->control[band].eht_mcs[i]);
+
+ return num_rates;
+}
+
+static int
ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
const struct cfg80211_bitrate_mask *mask,
@@ -5818,6 +6074,65 @@ ath12k_mac_set_peer_he_fixed_rate(struct ath12k_link_vif *arvif,
return ret;
}
+static int
+ath12k_mac_set_peer_eht_fixed_rate(struct ath12k_link_vif *arvif,
+ struct ath12k_link_sta *arsta,
+ const struct cfg80211_bitrate_mask *mask,
+ enum nl80211_band band)
+{
+ struct ath12k_sta *ahsta = arsta->ahsta;
+ struct ath12k *ar = arvif->ar;
+ struct ieee80211_sta *sta;
+ struct ieee80211_link_sta *link_sta;
+ u8 eht_rate, nss = 0;
+ u32 rate_code;
+ int ret, i;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ sta = ath12k_ahsta_to_sta(ahsta);
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++) {
+ if (hweight16(mask->control[band].eht_mcs[i]) == 1) {
+ nss = i + 1;
+ eht_rate = ffs(mask->control[band].eht_mcs[i]) - 1;
+ }
+ }
+
+ if (!nss) {
+ ath12k_warn(ar->ab, "No single EHT Fixed rate found to set for %pM\n",
+ arsta->addr);
+ return -EINVAL;
+ }
+
+ /* Avoid updating invalid nss as fixed rate*/
+ link_sta = ath12k_mac_get_link_sta(arsta);
+ if (!link_sta || nss > link_sta->rx_nss) {
+ ath12k_warn(ar->ab,
+ "unable to access link sta for sta %pM link %u or fixed nss of %u is not supported by sta\n",
+ sta->addr, arsta->link_id, nss);
+ return -EINVAL;
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "Setting Fixed EHT Rate for peer %pM. Device will not switch to any other selected rates\n",
+ arsta->addr);
+
+ rate_code = ATH12K_HW_RATE_CODE(eht_rate, nss - 1,
+ WMI_RATE_PREAMBLE_EHT);
+
+ ret = ath12k_wmi_set_peer_param(ar, arsta->addr,
+ arvif->vdev_id,
+ WMI_PEER_PARAM_FIXED_RATE,
+ rate_code);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed to update STA %pM Fixed Rate %d: %d\n",
+ arsta->addr, rate_code, ret);
+
+ return ret;
+}
+
static int ath12k_mac_station_assoc(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
@@ -5830,7 +6145,7 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
struct cfg80211_chan_def def;
enum nl80211_band band;
struct cfg80211_bitrate_mask *mask;
- u8 num_vht_rates, num_he_rates;
+ u8 num_vht_rates, num_he_rates, num_eht_rates;
u8 link_id = arvif->link_id;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -5873,10 +6188,11 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask);
+ num_eht_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask);
- /* If single VHT/HE rate is configured (by set_bitrate_mask()),
- * peer_assoc will disable VHT/HE. This is now enabled by a peer specific
- * fixed param.
+ /* If single VHT/HE/EHT rate is configured (by set_bitrate_mask()),
+ * peer_assoc will disable VHT/HE/EHT. This is now enabled by a peer
+ * specific fixed param.
* Note that all other rates and NSS will be disabled for this peer.
*/
link_sta = ath12k_mac_get_link_sta(arsta);
@@ -5896,6 +6212,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
ret = ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band);
if (ret)
return ret;
+ } else if (link_sta->eht_cap.has_eht && num_eht_rates == 1) {
+ ret = ath12k_mac_set_peer_eht_fixed_rate(arvif, arsta, mask, band);
+ if (ret)
+ return ret;
}
/* Re-assoc is run only to update supported rates for given station. It
@@ -5958,8 +6278,9 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
u32 changed, bw, nss, mac_nss, smps, bw_prev;
- int err, num_vht_rates, num_he_rates;
+ int err, num_vht_rates, num_he_rates, num_eht_rates;
const struct cfg80211_bitrate_mask *mask;
enum wmi_phy_mode peer_phymode;
struct ath12k_link_sta *arsta;
@@ -5980,6 +6301,7 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
spin_lock_bh(&ar->data_lock);
@@ -5997,6 +6319,7 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
mac_nss = max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
ath12k_mac_max_vht_nss(vht_mcs_mask),
ath12k_mac_max_he_nss(he_mcs_mask));
+ mac_nss = max(mac_nss, ath12k_mac_max_eht_nss(eht_mcs_mask));
nss = min(nss, mac_nss);
struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) =
@@ -6082,6 +6405,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band,
mask);
+ num_eht_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band,
+ mask);
/* Peer_assoc_prepare will reject vht rates in
* bitrate_mask if its not available in range format and
@@ -6106,9 +6431,18 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
band);
} else if (link_sta->he_cap.has_he && num_he_rates == 1) {
ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band);
+ } else if (link_sta->eht_cap.has_eht && num_eht_rates == 1) {
+ err = ath12k_mac_set_peer_eht_fixed_rate(arvif, arsta,
+ mask, band);
+ if (err) {
+ ath12k_warn(ar->ab,
+ "failed to set peer EHT fixed rate for STA %pM ret %d\n",
+ arsta->addr, err);
+ return;
+ }
} else {
- /* If the peer is non-VHT/HE or no fixed VHT/HE rate
- * is provided in the new bitrate mask we set the
+ /* If the peer is non-VHT/HE/EHT or no fixed VHT/HE/EHT
+ * rate is provided in the new bitrate mask we set the
* other rates using peer_assoc command. Also clear
* the peer fixed rate settings as it has higher proprity
* than peer assoc
@@ -9687,6 +10021,12 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created)
return -EINVAL;
+ if (ar->num_created_vdevs >= TARGET_NUM_VDEVS(ab)) {
+ ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
+ TARGET_NUM_VDEVS(ab));
+ return -ENOSPC;
+ }
+
link_id = arvif->link_id;
if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
@@ -10046,12 +10386,6 @@ static struct ath12k *ath12k_mac_assign_vif_to_vdev(struct ieee80211_hw *hw,
if (arvif->is_created)
goto flush;
- if (ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) {
- ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
- TARGET_NUM_VDEVS(ab));
- goto unlock;
- }
-
ret = ath12k_mac_vdev_create(ar, arvif);
if (ret) {
ath12k_warn(ab, "failed to create vdev %pM ret %d", vif->addr, ret);
@@ -10852,9 +11186,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
int n_vifs)
{
struct ath12k_wmi_vdev_up_params params = {};
- struct ath12k_link_vif *arvif;
struct ieee80211_bss_conf *link_conf;
struct ath12k_base *ab = ar->ab;
+ struct ath12k_link_vif *arvif;
struct ieee80211_vif *vif;
struct ath12k_vif *ahvif;
u8 link_id;
@@ -10915,6 +11249,28 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
continue;
}
+ ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif,
+ vifs[i].new_ctx->def);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to update puncturing bitmap %02x and width %d: %d\n",
+ vifs[i].new_ctx->def.punctured,
+ vifs[i].new_ctx->def.width, ret);
+ continue;
+ }
+
+ /* Defer VDEV bring-up during CSA to avoid installing stale
+ * beacon templates. The beacon content is updated only
+ * after CSA finalize, so we mark CSA in progress and skip
+ * VDEV_UP for now. It will be handled later in
+ * bss_info_changed().
+ */
+ if (link_conf->csa_active &&
+ arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ arvif->is_csa_in_progress = true;
+ continue;
+ }
+
ret = ath12k_mac_setup_bcn_tmpl(arvif);
if (ret)
ath12k_warn(ab, "failed to update bcn tmpl during csa: %d\n",
@@ -10935,16 +11291,6 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
arvif->vdev_id, ret);
continue;
}
-
- ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif,
- vifs[i].new_ctx->def);
- if (ret) {
- ath12k_warn(ar->ab,
- "failed to update puncturing bitmap %02x and width %d: %d\n",
- vifs[i].new_ctx->def.punctured,
- vifs[i].new_ctx->def.width, ret);
- continue;
- }
}
/* Restart the internal monitor vdev on new channel */
@@ -11849,6 +12195,9 @@ ath12k_mac_has_single_legacy_rate(struct ath12k *ar,
if (ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask))
return false;
+ if (ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask))
+ return false;
+
return num_rates == 1;
}
@@ -11871,11 +12220,15 @@ ath12k_mac_bitrate_mask_get_single_nss(struct ath12k *ar,
{
struct ieee80211_supported_band *sband = &ar->mac.sbands[band];
u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ const struct ieee80211_sband_iftype_data *data;
const struct ieee80211_sta_he_cap *he_cap;
u16 he_mcs_map = 0;
+ u16 eht_mcs_map = 0;
u8 ht_nss_mask = 0;
u8 vht_nss_mask = 0;
u8 he_nss_mask = 0;
+ u8 eht_nss_mask = 0;
+ u8 mcs_nss_len;
int i;
/* No need to consider legacy here. Basic rates are always present
@@ -11919,7 +12272,60 @@ ath12k_mac_bitrate_mask_get_single_nss(struct ath12k *ar,
return false;
}
- if (ht_nss_mask != vht_nss_mask || ht_nss_mask != he_nss_mask)
+ data = ieee80211_get_sband_iftype_data(sband, vif->type);
+
+ mcs_nss_len = ieee80211_eht_mcs_nss_size(&data->he_cap.he_cap_elem,
+ &data->eht_cap.eht_cap_elem,
+ false);
+ if (mcs_nss_len == 4) {
+ /* 20 MHz only STA case */
+ const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
+ &data->eht_cap.eht_mcs_nss_supp.only_20mhz;
+ if (eht_mcs_nss->rx_tx_mcs13_max_nss)
+ eht_mcs_map = 0x1fff;
+ else if (eht_mcs_nss->rx_tx_mcs11_max_nss)
+ eht_mcs_map = 0x07ff;
+ else if (eht_mcs_nss->rx_tx_mcs9_max_nss)
+ eht_mcs_map = 0x01ff;
+ else
+ eht_mcs_map = 0x007f;
+ } else {
+ const struct ieee80211_eht_mcs_nss_supp_bw *eht_mcs_nss;
+
+ switch (mcs_nss_len) {
+ case 9:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._320;
+ break;
+ case 6:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._160;
+ break;
+ case 3:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._80;
+ break;
+ default:
+ return false;
+ }
+
+ if (eht_mcs_nss->rx_tx_mcs13_max_nss)
+ eht_mcs_map = 0x1fff;
+ else if (eht_mcs_nss->rx_tx_mcs11_max_nss)
+ eht_mcs_map = 0x7ff;
+ else
+ eht_mcs_map = 0x1ff;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++) {
+ if (mask->control[band].eht_mcs[i] == 0)
+ continue;
+
+ if (mask->control[band].eht_mcs[i] < eht_mcs_map)
+ eht_nss_mask |= BIT(i);
+ else
+ return false;
+ }
+
+ if (ht_nss_mask != vht_nss_mask || ht_nss_mask != he_nss_mask ||
+ ht_nss_mask != eht_nss_mask)
return false;
if (ht_nss_mask == 0)
@@ -11967,7 +12373,8 @@ ath12k_mac_get_single_legacy_rate(struct ath12k *ar,
}
static int
-ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 he_gi, u8 he_ltf)
+ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 gi, u8 ltf,
+ u32 param)
{
struct ath12k *ar = arvif->ar;
int ret;
@@ -11975,47 +12382,54 @@ ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 he_gi, u8 he_
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
/* 0.8 = 0, 1.6 = 2 and 3.2 = 3. */
- if (he_gi && he_gi != 0xFF)
- he_gi += 1;
+ if (gi && gi != 0xFF)
+ gi += 1;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
- WMI_VDEV_PARAM_SGI, he_gi);
+ WMI_VDEV_PARAM_SGI, gi);
if (ret) {
- ath12k_warn(ar->ab, "failed to set HE GI:%d, error:%d\n",
- he_gi, ret);
+ ath12k_warn(ar->ab, "failed to set GI:%d, error:%d\n",
+ gi, ret);
return ret;
}
- /* start from 1 */
- if (he_ltf != 0xFF)
- he_ltf += 1;
+
+ if (param == WMI_VDEV_PARAM_HE_LTF) {
+ /* HE values start from 1 */
+ if (ltf != 0xFF)
+ ltf += 1;
+ } else {
+ /* EHT values start from 5 */
+ if (ltf != 0xFF)
+ ltf += 4;
+ }
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
- WMI_VDEV_PARAM_HE_LTF, he_ltf);
+ param, ltf);
if (ret) {
- ath12k_warn(ar->ab, "failed to set HE LTF:%d, error:%d\n",
- he_ltf, ret);
+ ath12k_warn(ar->ab, "failed to set LTF:%d, error:%d\n",
+ ltf, ret);
return ret;
}
return 0;
}
static int
-ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_ltf)
+ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 gi, u8 ltf)
{
struct ath12k *ar = arvif->ar;
int ret;
- u32 he_ar_gi_ltf;
+ u32 ar_gi_ltf;
- if (he_gi != 0xFF) {
- switch (he_gi) {
- case NL80211_RATE_INFO_HE_GI_0_8:
- he_gi = WMI_AUTORATE_800NS_GI;
+ if (gi != 0xFF) {
+ switch (gi) {
+ case ATH12K_RATE_INFO_GI_0_8:
+ gi = WMI_AUTORATE_800NS_GI;
break;
- case NL80211_RATE_INFO_HE_GI_1_6:
- he_gi = WMI_AUTORATE_1600NS_GI;
+ case ATH12K_RATE_INFO_GI_1_6:
+ gi = WMI_AUTORATE_1600NS_GI;
break;
- case NL80211_RATE_INFO_HE_GI_3_2:
- he_gi = WMI_AUTORATE_3200NS_GI;
+ case ATH12K_RATE_INFO_GI_3_2:
+ gi = WMI_AUTORATE_3200NS_GI;
break;
default:
ath12k_warn(ar->ab, "Invalid GI\n");
@@ -12023,16 +12437,16 @@ ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_
}
}
- if (he_ltf != 0xFF) {
- switch (he_ltf) {
- case NL80211_RATE_INFO_HE_1XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_1X;
+ if (ltf != 0xFF) {
+ switch (ltf) {
+ case ATH12K_RATE_INFO_1XLTF:
+ ltf = WMI_AUTORATE_LTF_1X;
break;
- case NL80211_RATE_INFO_HE_2XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_2X;
+ case ATH12K_RATE_INFO_2XLTF:
+ ltf = WMI_AUTORATE_LTF_2X;
break;
- case NL80211_RATE_INFO_HE_4XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_4X;
+ case ATH12K_RATE_INFO_4XLTF:
+ ltf = WMI_AUTORATE_LTF_4X;
break;
default:
ath12k_warn(ar->ab, "Invalid LTF\n");
@@ -12040,15 +12454,15 @@ ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_
}
}
- he_ar_gi_ltf = he_gi | he_ltf;
+ ar_gi_ltf = gi | ltf;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_AUTORATE_MISC_CFG,
- he_ar_gi_ltf);
+ ar_gi_ltf);
if (ret) {
ath12k_warn(ar->ab,
- "failed to set HE autorate GI:%u, LTF:%u params, error:%d\n",
- he_gi, he_ltf, ret);
+ "failed to set autorate GI:%u, LTF:%u params, error:%d\n",
+ gi, ltf, ret);
return ret;
}
@@ -12069,14 +12483,16 @@ static u32 ath12k_mac_nlgi_to_wmigi(enum nl80211_txrate_gi gi)
static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
u32 rate, u8 nss, u8 sgi, u8 ldpc,
- u8 he_gi, u8 he_ltf, bool he_fixed_rate)
+ u8 he_gi, u8 he_ltf, bool he_fixed_rate,
+ u8 eht_gi, u8 eht_ltf,
+ bool eht_fixed_rate)
{
struct ieee80211_bss_conf *link_conf;
struct ath12k *ar = arvif->ar;
+ bool he_support, eht_support, gi_ltf_set = false;
u32 vdev_param;
u32 param_value;
int ret;
- bool he_support;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -12085,6 +12501,7 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
return -EINVAL;
he_support = link_conf->he_support;
+ eht_support = link_conf->eht_support;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"mac set rate params vdev %i rate 0x%02x nss 0x%02x sgi 0x%02x ldpc 0x%02x\n",
@@ -12094,7 +12511,11 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
"he_gi 0x%02x he_ltf 0x%02x he_fixed_rate %d\n", he_gi,
he_ltf, he_fixed_rate);
- if (!he_support) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "eht_gi 0x%02x eht_ltf 0x%02x eht_fixed_rate %d\n",
+ eht_gi, eht_ltf, eht_fixed_rate);
+
+ if (!he_support && !eht_support) {
vdev_param = WMI_VDEV_PARAM_FIXED_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rate);
@@ -12123,14 +12544,34 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
return ret;
}
+ if (eht_support) {
+ if (eht_fixed_rate)
+ ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, eht_gi, eht_ltf,
+ WMI_VDEV_PARAM_EHT_LTF);
+ else
+ ret = ath12k_mac_set_auto_rate_gi_ltf(arvif, eht_gi, eht_ltf);
+
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to set EHT LTF/GI params %d/%d: %d\n",
+ eht_gi, eht_ltf, ret);
+ return ret;
+ }
+ gi_ltf_set = true;
+ }
+
if (he_support) {
if (he_fixed_rate)
- ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, he_gi, he_ltf);
+ ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, he_gi, he_ltf,
+ WMI_VDEV_PARAM_HE_LTF);
else
ret = ath12k_mac_set_auto_rate_gi_ltf(arvif, he_gi, he_ltf);
if (ret)
return ret;
- } else {
+ gi_ltf_set = true;
+ }
+
+ if (!gi_ltf_set) {
vdev_param = WMI_VDEV_PARAM_SGI;
param_value = ath12k_mac_nlgi_to_wmigi(sgi);
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
@@ -12195,6 +12636,38 @@ ath12k_mac_he_mcs_range_present(struct ath12k *ar,
return true;
}
+static bool
+ath12k_mac_eht_mcs_range_present(struct ath12k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ u16 eht_mcs;
+ int i;
+
+ for (i = 0; i < NL80211_EHT_NSS_MAX; i++) {
+ eht_mcs = mask->control[band].eht_mcs[i];
+
+ switch (eht_mcs) {
+ case 0:
+ case BIT(8) - 1:
+ case BIT(10) - 1:
+ case BIT(12) - 1:
+ case BIT(14) - 1:
+ break;
+ case BIT(15) - 1:
+ case BIT(16) - 1:
+ case BIT(16) - BIT(14) - 1:
+ if (i != 0)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void ath12k_mac_set_bitrate_mask_iter(void *data,
struct ieee80211_sta *sta)
{
@@ -12249,15 +12722,16 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
const struct cfg80211_bitrate_mask *mask,
unsigned int link_id)
{
- bool he_fixed_rate = false, vht_fixed_rate = false;
- const u16 *vht_mcs_mask, *he_mcs_mask;
+ bool eht_fixed_rate = false, he_fixed_rate = false, vht_fixed_rate = false;
+ const u16 *vht_mcs_mask, *he_mcs_mask, *eht_mcs_mask;
struct ieee80211_link_sta *link_sta;
struct ath12k_peer *peer, *tmp;
- u8 vht_nss, he_nss;
+ u8 vht_nss, he_nss, eht_nss;
int ret = true;
vht_mcs_mask = mask->control[band].vht_mcs;
he_mcs_mask = mask->control[band].he_mcs;
+ eht_mcs_mask = mask->control[band].eht_mcs;
if (ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask) == 1)
vht_fixed_rate = true;
@@ -12265,11 +12739,15 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
if (ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask) == 1)
he_fixed_rate = true;
- if (!vht_fixed_rate && !he_fixed_rate)
+ if (ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask) == 1)
+ eht_fixed_rate = true;
+
+ if (!vht_fixed_rate && !he_fixed_rate && !eht_fixed_rate)
return true;
vht_nss = ath12k_mac_max_vht_nss(vht_mcs_mask);
he_nss = ath12k_mac_max_he_nss(he_mcs_mask);
+ eht_nss = ath12k_mac_max_eht_nss(eht_mcs_mask);
rcu_read_lock();
spin_lock_bh(&ar->ab->base_lock);
@@ -12291,6 +12769,11 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
ret = false;
goto exit;
}
+ if (eht_fixed_rate && (!link_sta->eht_cap.has_eht ||
+ link_sta->rx_nss < eht_nss)) {
+ ret = false;
+ goto exit;
+ }
}
}
exit:
@@ -12312,8 +12795,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
u8 he_ltf = 0;
u8 he_gi = 0;
+ u8 eht_ltf = 0, eht_gi = 0;
u32 rate;
u8 nss, mac_nss;
u8 sgi;
@@ -12322,6 +12807,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
int ret;
int num_rates;
bool he_fixed_rate = false;
+ bool eht_fixed_rate = false;
lockdep_assert_wiphy(hw->wiphy);
@@ -12337,6 +12823,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ht_mcs_mask = mask->control[band].ht_mcs;
vht_mcs_mask = mask->control[band].vht_mcs;
he_mcs_mask = mask->control[band].he_mcs;
+ eht_mcs_mask = mask->control[band].eht_mcs;
ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC);
sgi = mask->control[band].gi;
@@ -12348,6 +12835,9 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
he_gi = mask->control[band].he_gi;
he_ltf = mask->control[band].he_ltf;
+ eht_gi = mask->control[band].eht_gi;
+ eht_ltf = mask->control[band].eht_ltf;
+
/* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it
* requires passing at least one of used basic rates along with them.
* Fixed rate setting across different preambles(legacy, HT, VHT) is
@@ -12385,9 +12875,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ath12k_warn(ar->ab,
"failed to update fixed rate settings due to mcs/nss incompatibility\n");
- mac_nss = max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
- ath12k_mac_max_vht_nss(vht_mcs_mask),
- ath12k_mac_max_he_nss(he_mcs_mask));
+ mac_nss = max(max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
+ ath12k_mac_max_vht_nss(vht_mcs_mask),
+ ath12k_mac_max_he_nss(he_mcs_mask)),
+ ath12k_mac_max_eht_nss(eht_mcs_mask));
nss = min_t(u32, ar->num_tx_chains, mac_nss);
/* If multiple rates across different preambles are given
@@ -12435,6 +12926,20 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ret = -EINVAL;
goto out;
}
+
+ num_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band,
+ mask);
+ if (num_rates == 1)
+ eht_fixed_rate = true;
+
+ if (!ath12k_mac_eht_mcs_range_present(ar, band, mask) &&
+ num_rates > 1) {
+ ath12k_warn(ar->ab,
+ "Setting more than one EHT MCS Value in bitrate mask not supported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
ieee80211_iterate_stations_mtx(hw,
ath12k_mac_disable_peer_fixed_rate,
arvif);
@@ -12446,7 +12951,8 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
}
ret = ath12k_mac_set_rate_params(arvif, rate, nss, sgi, ldpc, he_gi,
- he_ltf, he_fixed_rate);
+ he_ltf, he_fixed_rate, eht_gi, eht_ltf,
+ eht_fixed_rate);
if (ret) {
ath12k_warn(ar->ab, "failed to set rate params on vdev %i: %d\n",
arvif->vdev_id, ret);
@@ -12701,14 +13207,18 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
if (!signal &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
signal = arsta->rssi_beacon;
+ ath12k_fw_stats_reset(ar);
+ }
params.stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT;
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
ath12k_mac_put_chain_rssi(sinfo, arsta);
+ ath12k_fw_stats_reset(ar);
+ }
spin_lock_bh(&ar->data_lock);
noise_floor = ath12k_pdev_get_noise_floor(ar);
@@ -12792,8 +13302,10 @@ static void ath12k_mac_op_link_sta_statistics(struct ieee80211_hw *hw,
if (!signal &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
signal = arsta->rssi_beacon;
+ ath12k_fw_stats_reset(ar);
+ }
if (signal) {
link_sinfo->signal =
@@ -12895,6 +13407,7 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev for roc: %d\n",
ret);
+ ath12k_mac_unassign_link_vif(arvif);
return ret;
}
}
@@ -13894,6 +14407,11 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ if (test_bit(WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD,
+ ab->wmi_ab.svc_map)) {
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ ieee80211_hw_set(hw, DETECTS_COLOR_COLLISION);
+ }
wiphy->cipher_suites = cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
index c05af40bd7a2..1f689e367c8a 100644
--- a/drivers/net/wireless/ath/ath12k/mac.h
+++ b/drivers/net/wireless/ath/ath12k/mac.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_MAC_H
@@ -84,6 +84,18 @@ enum ath12k_supported_bw {
ATH12K_BW_320 = 4,
};
+enum ath12k_gi {
+ ATH12K_RATE_INFO_GI_0_8,
+ ATH12K_RATE_INFO_GI_1_6,
+ ATH12K_RATE_INFO_GI_3_2,
+};
+
+enum ath12k_ltf {
+ ATH12K_RATE_INFO_1XLTF,
+ ATH12K_RATE_INFO_2XLTF,
+ ATH12K_RATE_INFO_4XLTF,
+};
+
struct ath12k_mac_get_any_chanctx_conf_arg {
struct ath12k *ar;
struct ieee80211_chanctx_conf *chanctx_conf;
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index c729d5526c75..a12c8379cb46 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.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-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/module.h>
@@ -218,6 +218,19 @@ static inline bool ath12k_pci_is_offset_within_mhi_region(u32 offset)
return (offset >= PCI_MHIREGLEN_REG && offset <= PCI_MHI_REGION_END);
}
+static void ath12k_pci_restore_window(struct ath12k_base *ab)
+{
+ struct ath12k_pci *ab_pci = ath12k_pci_priv(ab);
+
+ spin_lock_bh(&ab_pci->window_lock);
+
+ iowrite32(WINDOW_ENABLE_BIT | ab_pci->register_window,
+ ab->mem + WINDOW_REG_ADDRESS);
+ ioread32(ab->mem + WINDOW_REG_ADDRESS);
+
+ spin_unlock_bh(&ab_pci->window_lock);
+}
+
static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
{
u32 val, delay;
@@ -242,6 +255,11 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val = ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
if (val == 0xffffffff)
ath12k_warn(ab, "link down error during global reset\n");
+
+ /* Restore window register as its content is cleared during
+ * hardware global reset, such that it aligns with host cache.
+ */
+ ath12k_pci_restore_window(ab);
}
static void ath12k_pci_clear_dbg_registers(struct ath12k_base *ab)
@@ -1871,3 +1889,7 @@ void ath12k_pci_exit(void)
{
pci_unregister_driver(&ath12k_pci_driver);
}
+
+/* firmware files */
+MODULE_FIRMWARE(ATH12K_FW_DIR "/QCN9274/hw2.0/*");
+MODULE_FIRMWARE(ATH12K_FW_DIR "/WCN7850/hw2.0/*");
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index 36325e62aa24..b7c48b6706df 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.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 <linux/elf.h>
@@ -3114,9 +3114,10 @@ static void ath12k_qmi_m3_free(struct ath12k_base *ab)
if (!m3_mem->vaddr)
return;
- dma_free_coherent(ab->dev, m3_mem->size,
+ dma_free_coherent(ab->dev, m3_mem->total_size,
m3_mem->vaddr, m3_mem->paddr);
m3_mem->vaddr = NULL;
+ m3_mem->total_size = 0;
m3_mem->size = 0;
}
@@ -3152,7 +3153,7 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab)
/* In recovery/resume cases, M3 buffer is not freed, try to reuse that */
if (m3_mem->vaddr) {
- if (m3_mem->size >= m3_len)
+ if (m3_mem->total_size >= m3_len)
goto skip_m3_alloc;
/* Old buffer is too small, free and reallocate */
@@ -3164,11 +3165,13 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab)
GFP_KERNEL);
if (!m3_mem->vaddr) {
ath12k_err(ab, "failed to allocate memory for M3 with size %zu\n",
- fw->size);
+ m3_len);
ret = -ENOMEM;
goto out;
}
+ m3_mem->total_size = m3_len;
+
skip_m3_alloc:
memcpy(m3_mem->vaddr, m3_data, m3_len);
m3_mem->size = m3_len;
@@ -3740,7 +3743,7 @@ static int ath12k_qmi_ops_new_server(struct qmi_handle *qmi_hdl,
sq->sq_node = service->node;
sq->sq_port = service->port;
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)sq,
sizeof(*sq), 0);
if (ret) {
ath12k_warn(ab, "qmi failed to connect to remote service %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
index 4767d9a2e309..7a88268aa1e9 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.h
+++ b/drivers/net/wireless/ath/ath12k/qmi.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_QMI_H
@@ -120,6 +120,9 @@ struct target_info {
};
struct m3_mem_region {
+ /* total memory allocated */
+ u32 total_size;
+ /* actual memory being used */
u32 size;
dma_addr_t paddr;
void *vaddr;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index ff6b3d4ea820..be8b2943094f 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.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 <linux/skbuff.h>
#include <linux/ctype.h>
@@ -14,6 +14,7 @@
#include <linux/uuid.h>
#include <linux/time.h>
#include <linux/of.h>
+#include <linux/cleanup.h>
#include "core.h"
#include "debugfs.h"
#include "debug.h"
@@ -190,6 +191,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_11d_new_cc_event) },
[WMI_TAG_PER_CHAIN_RSSI_STATS] = {
.min_len = sizeof(struct wmi_per_chain_rssi_stat_params) },
+ [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = {
+ .min_len = sizeof(struct wmi_obss_color_collision_event) },
};
__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
@@ -2367,10 +2370,13 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
cmd->peer_bw_rxnss_override |= cpu_to_le32(arg->peer_bw_rxnss_override);
if (arg->vht_capable) {
- mcs->rx_max_rate = cpu_to_le32(arg->rx_max_rate);
- mcs->rx_mcs_set = cpu_to_le32(arg->rx_mcs_set);
- mcs->tx_max_rate = cpu_to_le32(arg->tx_max_rate);
- mcs->tx_mcs_set = cpu_to_le32(arg->tx_mcs_set);
+ /* Firmware interprets mcs->tx_mcs_set field as peer's
+ * RX capability
+ */
+ mcs->rx_max_rate = cpu_to_le32(arg->tx_max_rate);
+ mcs->rx_mcs_set = cpu_to_le32(arg->tx_mcs_set);
+ mcs->tx_max_rate = cpu_to_le32(arg->rx_max_rate);
+ mcs->tx_mcs_set = cpu_to_le32(arg->rx_mcs_set);
}
/* HE Rates */
@@ -3848,6 +3854,58 @@ int ath12k_wmi_fils_discovery(struct ath12k *ar, u32 vdev_id, u32 interval,
}
static void
+ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *skb)
+{
+ const struct wmi_obss_color_collision_event *ev;
+ struct ath12k_link_vif *arvif;
+ u32 vdev_id, evt_type;
+ u64 bitmap;
+
+ const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
+ PTR_ERR(tb));
+ return;
+ }
+
+ ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT];
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch OBSS color collision event\n");
+ return;
+ }
+
+ vdev_id = le32_to_cpu(ev->vdev_id);
+ evt_type = le32_to_cpu(ev->evt_type);
+ bitmap = le64_to_cpu(ev->obss_color_bitmap);
+
+ guard(rcu)();
+
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_id);
+ if (!arvif) {
+ ath12k_warn(ab, "no arvif found for vdev %u in OBSS color collision event\n",
+ vdev_id);
+ return;
+ }
+
+ switch (evt_type) {
+ case WMI_BSS_COLOR_COLLISION_DETECTION:
+ ieee80211_obss_color_collision_notify(arvif->ahvif->vif,
+ bitmap,
+ arvif->link_id);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "obss color collision detected vdev %u event %d bitmap %016llx\n",
+ vdev_id, evt_type, bitmap);
+ break;
+ case WMI_BSS_COLOR_COLLISION_DISABLE:
+ case WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY:
+ case WMI_BSS_COLOR_FREE_SLOT_AVAILABLE:
+ break;
+ default:
+ ath12k_warn(ab, "unknown OBSS color collision event type %d\n", evt_type);
+ }
+}
+
+static void
ath12k_fill_band_to_mac_param(struct ath12k_base *soc,
struct ath12k_wmi_pdev_band_arg *arg)
{
@@ -7011,12 +7069,26 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff
static void ath12k_bcn_tx_status_event(struct ath12k_base *ab, struct sk_buff *skb)
{
+ struct ath12k_link_vif *arvif;
+ struct ath12k *ar;
u32 vdev_id, tx_status;
if (ath12k_pull_bcn_tx_status_ev(ab, skb, &vdev_id, &tx_status) != 0) {
ath12k_warn(ab, "failed to extract bcn tx status");
return;
}
+
+ guard(rcu)();
+
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_id);
+ if (!arvif) {
+ ath12k_warn(ab, "invalid vdev %u in bcn tx status\n",
+ vdev_id);
+ return;
+ }
+
+ ar = arvif->ar;
+ wiphy_work_queue(ath12k_ar_to_hw(ar)->wiphy, &arvif->bcn_tx_work);
}
static void ath12k_vdev_stopped_event(struct ath12k_base *ab, struct sk_buff *skb)
@@ -8017,8 +8089,6 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
buf[len - 1] = 0;
else
buf[len] = 0;
-
- ath12k_fw_stats_reset(ar);
}
static void
@@ -8415,18 +8485,10 @@ static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
ath12k_warn(ab, "empty beacon stats");
return;
}
- /* Mark end until we reached the count of all started VDEVs
- * within the PDEV
- */
- if (ar->num_started_vdevs)
- is_end = ((++ar->fw_stats.num_bcn_recvd) ==
- ar->num_started_vdevs);
list_splice_tail_init(&stats->bcn,
&ar->fw_stats.bcn);
-
- if (is_end)
- complete(&ar->fw_stats_done);
+ complete(&ar->fw_stats_done);
}
}
@@ -9874,6 +9936,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_PDEV_RSSI_DBM_CONVERSION_PARAMS_INFO_EVENTID:
ath12k_wmi_rssi_dbm_conversion_params_info_event(ab, skb);
break;
+ case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
+ ath12k_wmi_obss_color_collision_event(ab, skb);
+ break;
/* add Unsupported events (rare) here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
@@ -9884,7 +9949,6 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
/* add Unsupported events (frequent) here */
case WMI_PDEV_GET_HALPHY_CAL_STATUS_EVENTID:
case WMI_MGMT_RX_FW_CONSUMED_EVENTID:
- case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
/* debug might flood hence silently ignore (no-op) */
break;
case WMI_PDEV_UTF_EVENTID:
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index a8c3190e8ad9..f99fced1610e 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.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_WMI_H
@@ -223,15 +223,15 @@ enum WMI_HOST_WLAN_BAND {
};
/* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command.
- * Used only for HE auto rate mode.
+ * Used for HE and EHT auto rate mode.
*/
enum {
- /* HE LTF related configuration */
- WMI_HE_AUTORATE_LTF_1X = BIT(0),
- WMI_HE_AUTORATE_LTF_2X = BIT(1),
- WMI_HE_AUTORATE_LTF_4X = BIT(2),
+ /* LTF related configuration */
+ WMI_AUTORATE_LTF_1X = BIT(0),
+ WMI_AUTORATE_LTF_2X = BIT(1),
+ WMI_AUTORATE_LTF_4X = BIT(2),
- /* HE GI related configuration */
+ /* GI related configuration */
WMI_AUTORATE_400NS_GI = BIT(8),
WMI_AUTORATE_800NS_GI = BIT(9),
WMI_AUTORATE_1600NS_GI = BIT(10),
@@ -1197,6 +1197,7 @@ enum wmi_tlv_vdev_param {
WMI_VDEV_PARAM_SET_HEMU_MODE,
WMI_VDEV_PARAM_HEOPS_0_31 = 0x8003,
WMI_VDEV_PARAM_SET_EHT_MU_MODE = 0x8005,
+ WMI_VDEV_PARAM_EHT_LTF,
};
enum wmi_tlv_peer_flags {
@@ -3609,20 +3610,6 @@ struct ath12k_wmi_scan_cancel_arg {
u32 pdev_id;
};
-struct wmi_bcn_send_from_host_cmd {
- __le32 tlv_header;
- __le32 vdev_id;
- __le32 data_len;
- union {
- __le32 frag_ptr;
- __le32 frag_ptr_lo;
- };
- __le32 frame_ctrl;
- __le32 dtim_flag;
- __le32 bcn_antenna;
- __le32 frag_ptr_hi;
-};
-
#define WMI_CHAN_INFO_MODE GENMASK(5, 0)
#define WMI_CHAN_INFO_HT40_PLUS BIT(6)
#define WMI_CHAN_INFO_PASSIVE BIT(7)
@@ -4218,8 +4205,10 @@ struct wmi_unit_test_cmd {
struct ath12k_wmi_vht_rate_set_params {
__le32 tlv_header;
__le32 rx_max_rate;
+ /* MCS at which the peer can transmit */
__le32 rx_mcs_set;
__le32 tx_max_rate;
+ /* MCS at which the peer can receive */
__le32 tx_mcs_set;
__le32 tx_max_mcs_nss;
} __packed;
@@ -4940,6 +4929,24 @@ struct wmi_obss_spatial_reuse_params_cmd {
#define ATH12K_BSS_COLOR_STA_PERIODS 10000
#define ATH12K_BSS_COLOR_AP_PERIODS 5000
+/**
+ * enum wmi_bss_color_collision - Event types for BSS color collision handling
+ * @WMI_BSS_COLOR_COLLISION_DISABLE: Indicates that BSS color collision detection
+ * is disabled.
+ * @WMI_BSS_COLOR_COLLISION_DETECTION: Event triggered when a BSS color collision
+ * is detected.
+ * @WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY: Event indicating that the timer for waiting
+ * on a free BSS color slot has expired.
+ * @WMI_BSS_COLOR_FREE_SLOT_AVAILABLE: Event indicating that a free BSS color slot
+ * has become available.
+ */
+enum wmi_bss_color_collision {
+ WMI_BSS_COLOR_COLLISION_DISABLE = 0,
+ WMI_BSS_COLOR_COLLISION_DETECTION,
+ WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY,
+ WMI_BSS_COLOR_FREE_SLOT_AVAILABLE,
+};
+
struct wmi_obss_color_collision_cfg_params_cmd {
__le32 tlv_header;
__le32 vdev_id;
@@ -4957,6 +4964,12 @@ struct wmi_bss_color_change_enable_params_cmd {
__le32 enable;
} __packed;
+struct wmi_obss_color_collision_event {
+ __le32 vdev_id;
+ __le32 evt_type;
+ __le64 obss_color_bitmap;
+} __packed;
+
#define ATH12K_IPV4_TH_SEED_SIZE 5
#define ATH12K_IPV6_TH_SEED_SIZE 11
diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c
index dce9bd0bcaef..e8481626f194 100644
--- a/drivers/net/wireless/ath/ath12k/wow.c
+++ b/drivers/net/wireless/ath/ath12k/wow.c
@@ -758,6 +758,7 @@ static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable)
if (ret) {
ath12k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
arvif->vdev_id, enable, ret);
+ kfree(offload);
return ret;
}
}
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index d3a9d00e65e1..ef9ea4ff891b 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -4484,80 +4484,6 @@ struct set_rssi_filter_resp {
u32 status;
};
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_req {
-
- struct wcn36xx_hal_msg_header header;
-
- /* Host setting for 11d */
- u8 dot11d_enabled;
-
- /* Lets PNO know that host has determined the regulatory domain */
- u8 dot11d_resolved;
-
- /* Channels on which PNO is allowed to scan */
- u8 channel_count;
- u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS];
-
- /* Minimum channel time */
- u16 active_min_ch_time;
-
- /* Maximum channel time */
- u16 active_max_ch_time;
-
- /* Minimum channel time */
- u16 passive_min_ch_time;
-
- /* Maximum channel time */
- u16 passive_max_ch_time;
-
- /* Cb State */
- enum phy_chan_bond_state state;
-} __packed;
-
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_req_ex {
-
- struct wcn36xx_hal_msg_header header;
-
- /* Host setting for 11d */
- u8 dot11d_enabled;
-
- /* Lets PNO know that host has determined the regulatory domain */
- u8 dot11d_resolved;
-
- /* Channels on which PNO is allowed to scan */
- u8 channel_count;
- u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
-
- /* Minimum channel time */
- u16 active_min_ch_time;
-
- /* Maximum channel time */
- u16 active_max_ch_time;
-
- /* Minimum channel time */
- u16 passive_min_ch_time;
-
- /* Maximum channel time */
- u16 passive_max_ch_time;
-
- /* Cb State */
- enum phy_chan_bond_state state;
-} __packed;
-
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_resp {
-
- struct wcn36xx_hal_msg_header header;
-
- /* status of the request */
- u32 status;
-} __packed;
-
struct wcn36xx_hal_set_tx_per_tracking_req_msg {
struct wcn36xx_hal_msg_header header;
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 2cf86fc3f8fe..136acc414714 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -1127,66 +1127,6 @@ out_nomem:
return ret;
}
-static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len)
-{
- struct wcn36xx_hal_update_scan_params_resp *rsp;
-
- rsp = buf;
-
- /* Remove the PNO version bit */
- rsp->status &= (~(WCN36XX_FW_MSG_PNO_VERSION_MASK));
-
- if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) {
- wcn36xx_warn("error response from update scan\n");
- return rsp->status;
- }
-
- return 0;
-}
-
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn,
- u8 *channels, size_t channel_count)
-{
- struct wcn36xx_hal_update_scan_params_req_ex msg_body;
- int ret;
-
- mutex_lock(&wcn->hal_mutex);
- INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ);
-
- msg_body.dot11d_enabled = false;
- msg_body.dot11d_resolved = true;
-
- msg_body.channel_count = channel_count;
- memcpy(msg_body.channels, channels, channel_count);
- msg_body.active_min_ch_time = 60;
- msg_body.active_max_ch_time = 120;
- msg_body.passive_min_ch_time = 60;
- msg_body.passive_max_ch_time = 110;
- msg_body.state = PHY_SINGLE_CHANNEL_CENTERED;
-
- PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
-
- wcn36xx_dbg(WCN36XX_DBG_HAL,
- "hal update scan params channel_count %d\n",
- msg_body.channel_count);
-
- ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
- if (ret) {
- wcn36xx_err("Sending hal_update_scan_params failed\n");
- goto out;
- }
- ret = wcn36xx_smd_update_scan_params_rsp(wcn->hal_buf,
- wcn->hal_rsp_len);
- if (ret) {
- wcn36xx_err("hal_update_scan_params response failed err=%d\n",
- ret);
- goto out;
- }
-out:
- mutex_unlock(&wcn->hal_mutex);
- return ret;
-}
-
static int wcn36xx_smd_add_sta_self_rsp(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
void *buf,
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index 2c1ed9e570bf..4e39df5589b3 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -66,7 +66,6 @@ int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
struct ieee80211_vif *vif);
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count);
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn);
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index f521af575e9b..c866cfd144c7 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -458,6 +458,5 @@ void wil_pm_runtime_put(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}