mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'ath-next-20251103' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath into wireless-next
Jeff Johnson says: ================== ath.git patches for v6.19 Highlights for some specific drivers include: ath10k: Add support for Factory Test TLV commands ath11k: Add support for Tx Power insertion ath12k: Add support for BSS color change And of course there is the usual set of cleanups and bug fixes across the entire family of "ath" drivers. We do expect to have one more pull request before the v6.19 merge window to pull in the refactored ath12k driver from the ath12k-ng branch. ================== Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,14 +31,137 @@ static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
|
||||
[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *nl_skb;
|
||||
int ret;
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
|
||||
if (ret) {
|
||||
ath10k_warn(ar,
|
||||
"failed to put testmode wmi event cmd 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, skb->len, skb->data);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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",
|
||||
@@ -53,43 +182,10 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
|
||||
if (ret) {
|
||||
ath10k_warn(ar,
|
||||
"failed to put testmode wmi event cmd attribute: %d\n",
|
||||
ret);
|
||||
kfree_skb(nl_skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
|
||||
if (ret) {
|
||||
ath10k_warn(ar,
|
||||
"failed to copy skb to testmode wmi event: %d\n",
|
||||
ret);
|
||||
kfree_skb(nl_skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
|
||||
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:
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2106,14 +2106,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 +2201,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 +2209,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);
|
||||
|
||||
@@ -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 {
|
||||
@@ -963,6 +965,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 {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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/*");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
@@ -3847,6 +3853,58 @@ int ath12k_wmi_fils_discovery(struct ath12k *ar, u32 vdev_id, u32 interval,
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -9874,6 +9946,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 +9959,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user