diff options
Diffstat (limited to 'drivers/net/wireless/ath')
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, ¶ms); + 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, ¶ms))) + !(ath12k_mac_get_fw_stats(ar, ¶ms))) { 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, ¶ms))) + !(ath12k_mac_get_fw_stats(ar, ¶ms))) { 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, ¶ms))) + !(ath12k_mac_get_fw_stats(ar, ¶ms))) { 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); } |
