mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-20 10:51:27 +00:00
664 lines
19 KiB
Diff
664 lines
19 KiB
Diff
From 2758579734a12c00eb40a6e455efd04e59e44721 Mon Sep 17 00:00:00 2001
|
|
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
Date: Tue, 28 Dec 2021 13:14:06 +0530
|
|
Subject: [PATCH] ath12k: add mac ops to get tx power
|
|
|
|
Currently, driver does not support get_txpower mac ops because of which
|
|
cfg80211 returns vif->bss_conf.txpower to user space. bss_conf.txpower
|
|
gets its value from ieee80211_channel->max_reg_power. However, the
|
|
final txpower is dependent on few other parameters apart from max
|
|
regulatory supported power.
|
|
|
|
Add get_txpower to get the Tx power from FW and return it accordingly.
|
|
|
|
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
|
Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com>
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/core.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/core.c
|
|
@@ -229,6 +229,57 @@ bool ath12k_core_hw_group_start_ready(st
|
|
return (ag->num_started == ag->num_chip);
|
|
}
|
|
|
|
+void ath12k_fw_stats_pdevs_free(struct list_head *head)
|
|
+{
|
|
+ struct ath12k_fw_stats_pdev *i, *tmp;
|
|
+
|
|
+ list_for_each_entry_safe(i, tmp, head, list) {
|
|
+ list_del(&i->list);
|
|
+ kfree(i);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ath12k_fw_stats_vdevs_free(struct list_head *head)
|
|
+{
|
|
+ struct ath12k_fw_stats_vdev *i, *tmp;
|
|
+
|
|
+ list_for_each_entry_safe(i, tmp, head, list) {
|
|
+ list_del(&i->list);
|
|
+ kfree(i);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath12k_fw_stats_bcn_free(struct list_head *head)
|
|
+{
|
|
+ struct ath12k_fw_stats_bcn *i, *tmp;
|
|
+
|
|
+ list_for_each_entry_safe(i, tmp, head, list) {
|
|
+ list_del(&i->list);
|
|
+ kfree(i);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath12k_fw_stats_init(struct ath12k *ar)
|
|
+{
|
|
+ INIT_LIST_HEAD(&ar->fw_stats.vdevs);
|
|
+ INIT_LIST_HEAD(&ar->fw_stats.pdevs);
|
|
+ INIT_LIST_HEAD(&ar->fw_stats.bcn);
|
|
+ init_completion(&ar->fw_stats_complete);
|
|
+}
|
|
+
|
|
+void ath12k_fw_stats_reset(struct ath12k *ar)
|
|
+{
|
|
+ ath12k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
|
|
+ ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
|
|
+}
|
|
+
|
|
+void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
|
|
+{
|
|
+ ath12k_fw_stats_pdevs_free(&stats->pdevs);
|
|
+ ath12k_fw_stats_vdevs_free(&stats->vdevs);
|
|
+ ath12k_fw_stats_bcn_free(&stats->bcn);
|
|
+}
|
|
+
|
|
int ath12k_core_suspend(struct ath12k_base *ab)
|
|
{
|
|
int ret;
|
|
--- a/drivers/net/wireless/ath/ath12k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/core.h
|
|
@@ -665,9 +665,6 @@ struct ath12k_debug {
|
|
struct dentry *debugfs_pdev;
|
|
struct ath12k_dbg_htt_stats htt_stats;
|
|
u32 extd_tx_stats;
|
|
- struct ath12k_fw_stats fw_stats;
|
|
- struct completion fw_stats_complete;
|
|
- bool fw_stats_done;
|
|
u32 extd_rx_stats;
|
|
u32 pktlog_filter;
|
|
u32 pktlog_mode;
|
|
@@ -866,6 +863,12 @@ struct ath12k {
|
|
|
|
struct completion mlo_setup_done;
|
|
u32 mlo_setup_status;
|
|
+
|
|
+ struct ath12k_fw_stats fw_stats;
|
|
+ struct completion fw_stats_complete;
|
|
+ bool fw_stats_done;
|
|
+
|
|
+ unsigned long last_tx_power_update;
|
|
};
|
|
|
|
struct ath12k_band_cap {
|
|
@@ -1315,6 +1318,11 @@ void ath12k_core_put_hw_group(struct ath
|
|
|
|
const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab,
|
|
const char *filename);
|
|
+void ath12k_fw_stats_init(struct ath12k *ar);
|
|
+void ath12k_fw_stats_pdevs_free(struct list_head *head);
|
|
+void ath12k_fw_stats_bcn_free(struct list_head *head);
|
|
+void ath12k_fw_stats_reset(struct ath12k *ar);
|
|
+void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
|
|
|
|
static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
|
|
{
|
|
--- a/drivers/net/wireless/ath/ath12k/debugfs.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/debugfs.c
|
|
@@ -53,86 +53,28 @@ static const char *htt_bp_lmac_ring[HTT_
|
|
"MONITOR_DEST_RING",
|
|
};
|
|
|
|
-static void ath12k_fw_stats_pdevs_free(struct list_head *head)
|
|
-{
|
|
- struct ath12k_fw_stats_pdev *i, *tmp;
|
|
-
|
|
- list_for_each_entry_safe(i, tmp, head, list) {
|
|
- list_del(&i->list);
|
|
- kfree(i);
|
|
- }
|
|
-}
|
|
-
|
|
-static void ath12k_fw_stats_vdevs_free(struct list_head *head)
|
|
-{
|
|
- struct ath12k_fw_stats_vdev *i, *tmp;
|
|
-
|
|
- list_for_each_entry_safe(i, tmp, head, list) {
|
|
- list_del(&i->list);
|
|
- kfree(i);
|
|
- }
|
|
-}
|
|
-
|
|
-static void ath12k_fw_stats_bcn_free(struct list_head *head)
|
|
-{
|
|
- struct ath12k_fw_stats_bcn *i, *tmp;
|
|
-
|
|
- list_for_each_entry_safe(i, tmp, head, list) {
|
|
- list_del(&i->list);
|
|
- kfree(i);
|
|
- }
|
|
-}
|
|
-
|
|
static void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
|
|
{
|
|
spin_lock_bh(&ar->data_lock);
|
|
- ar->debug.fw_stats_done = false;
|
|
- ath12k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
|
|
- ath12k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
|
|
+ ar->fw_stats_done = false;
|
|
+ ath12k_fw_stats_reset(ar);
|
|
spin_unlock_bh(&ar->data_lock);
|
|
}
|
|
|
|
-void ath12k_debugfs_fw_stats_process(struct ath12k_base *ab, struct sk_buff *skb)
|
|
+void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
|
+ struct ath12k_fw_stats *stats)
|
|
{
|
|
- struct ath12k_fw_stats stats = {};
|
|
- struct ath12k *ar;
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_pdev *pdev;
|
|
bool is_end;
|
|
static unsigned int num_vdev, num_bcn;
|
|
size_t total_vdevs_started = 0;
|
|
- int i, ret;
|
|
-
|
|
- INIT_LIST_HEAD(&stats.pdevs);
|
|
- INIT_LIST_HEAD(&stats.vdevs);
|
|
- INIT_LIST_HEAD(&stats.bcn);
|
|
-
|
|
- ret = ath12k_wmi_pull_fw_stats(ab, skb, &stats);
|
|
- if (ret) {
|
|
- ath12k_warn(ab, "failed to pull fw stats: %d\n", ret);
|
|
- goto free;
|
|
- }
|
|
-
|
|
- rcu_read_lock();
|
|
- ar = ath12k_mac_get_ar_by_pdev_id(ab, stats.pdev_id);
|
|
- if (!ar) {
|
|
- rcu_read_unlock();
|
|
- ath12k_warn(ab, "failed to get ar for pdev_id %d: %d\n",
|
|
- stats.pdev_id, ret);
|
|
- goto free;
|
|
- }
|
|
|
|
- spin_lock_bh(&ar->data_lock);
|
|
-
|
|
- if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
|
|
- list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
|
|
- ar->debug.fw_stats_done = true;
|
|
- goto complete;
|
|
- }
|
|
-
|
|
- if (stats.stats_id == WMI_REQUEST_VDEV_STAT) {
|
|
- if (list_empty(&stats.vdevs)) {
|
|
+ int i;
|
|
+ if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
|
|
+ if (list_empty(&stats->vdevs)) {
|
|
ath12k_warn(ab, "empty vdev stats");
|
|
- goto complete;
|
|
+ return;
|
|
}
|
|
/* FW sends all the active VDEV stats irrespective of PDEV,
|
|
* hence limit until the count of all VDEVs started
|
|
@@ -145,43 +87,34 @@ void ath12k_debugfs_fw_stats_process(str
|
|
|
|
is_end = ((++num_vdev) == total_vdevs_started);
|
|
|
|
- list_splice_tail_init(&stats.vdevs,
|
|
- &ar->debug.fw_stats.vdevs);
|
|
+ list_splice_tail_init(&stats->vdevs,
|
|
+ &ar->fw_stats.vdevs);
|
|
|
|
if (is_end) {
|
|
- ar->debug.fw_stats_done = true;
|
|
+ ar->fw_stats_done = true;
|
|
num_vdev = 0;
|
|
}
|
|
- goto complete;
|
|
+ return;
|
|
}
|
|
|
|
- if (stats.stats_id == WMI_REQUEST_BCN_STAT) {
|
|
- if (list_empty(&stats.bcn)) {
|
|
+ if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
|
|
+ if (list_empty(&stats->bcn)) {
|
|
ath12k_warn(ab, "empty bcn stats");
|
|
- goto complete;
|
|
+ return;
|
|
}
|
|
/* Mark end until we reached the count of all started VDEVs
|
|
* within the PDEV
|
|
*/
|
|
is_end = ((++num_bcn) == ar->num_started_vdevs);
|
|
|
|
- list_splice_tail_init(&stats.bcn,
|
|
- &ar->debug.fw_stats.bcn);
|
|
+ list_splice_tail_init(&stats->bcn,
|
|
+ &ar->fw_stats.bcn);
|
|
|
|
if (is_end) {
|
|
- ar->debug.fw_stats_done = true;
|
|
+ ar->fw_stats_done = true;
|
|
num_bcn = 0;
|
|
}
|
|
}
|
|
-complete:
|
|
- complete(&ar->debug.fw_stats_complete);
|
|
- rcu_read_unlock();
|
|
- spin_unlock_bh(&ar->data_lock);
|
|
-
|
|
-free:
|
|
- ath12k_fw_stats_pdevs_free(&stats.pdevs);
|
|
- ath12k_fw_stats_vdevs_free(&stats.vdevs);
|
|
- ath12k_fw_stats_bcn_free(&stats.bcn);
|
|
}
|
|
|
|
static int ath12k_debugfs_fw_stats_request(struct ath12k *ar,
|
|
@@ -202,7 +135,7 @@ static int ath12k_debugfs_fw_stats_reque
|
|
|
|
ath12k_debugfs_fw_stats_reset(ar);
|
|
|
|
- reinit_completion(&ar->debug.fw_stats_complete);
|
|
+ reinit_completion(&ar->fw_stats_complete);
|
|
|
|
ret = ath12k_wmi_send_stats_request_cmd(ar, req_param);
|
|
|
|
@@ -213,7 +146,7 @@ static int ath12k_debugfs_fw_stats_reque
|
|
}
|
|
|
|
time_left =
|
|
- wait_for_completion_timeout(&ar->debug.fw_stats_complete,
|
|
+ wait_for_completion_timeout(&ar->fw_stats_complete,
|
|
1 * HZ);
|
|
if (!time_left)
|
|
return -ETIMEDOUT;
|
|
@@ -223,7 +156,7 @@ static int ath12k_debugfs_fw_stats_reque
|
|
break;
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
- if (ar->debug.fw_stats_done) {
|
|
+ if (ar->fw_stats_done) {
|
|
spin_unlock_bh(&ar->data_lock);
|
|
break;
|
|
}
|
|
@@ -263,7 +196,7 @@ static int ath12k_open_pdev_stats(struct
|
|
goto err_free;
|
|
}
|
|
|
|
- ath12k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
|
|
+ ath12k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id,
|
|
buf);
|
|
|
|
file->private_data = buf;
|
|
@@ -335,7 +268,7 @@ static int ath12k_open_vdev_stats(struct
|
|
goto err_free;
|
|
}
|
|
|
|
- ath12k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
|
|
+ ath12k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id,
|
|
buf);
|
|
|
|
file->private_data = buf;
|
|
@@ -413,14 +346,14 @@ static int ath12k_open_bcn_stats(struct
|
|
}
|
|
}
|
|
|
|
- ath12k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id,
|
|
+ ath12k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_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->debug.fw_stats.bcn);
|
|
+ ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
|
file->private_data = buf;
|
|
@@ -474,12 +407,12 @@ static ssize_t ath12k_write_enable_vdev_
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
- if (enable == ar->debug.fw_stats.en_vdev_stats_ol) {
|
|
+ if (enable == ar->fw_stats.en_vdev_stats_ol) {
|
|
ret = count;
|
|
goto out;
|
|
}
|
|
|
|
- ar->debug.fw_stats.en_vdev_stats_ol = enable;
|
|
+ ar->fw_stats.en_vdev_stats_ol = enable;
|
|
ret = count;
|
|
|
|
out:
|
|
@@ -498,7 +431,7 @@ static ssize_t ath12k_read_enable_vdev_s
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
len = scnprintf(buf, sizeof(buf) - len, "%u\n",
|
|
- ar->debug.fw_stats.en_vdev_stats_ol);
|
|
+ ar->fw_stats.en_vdev_stats_ol);
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
|
return simple_read_from_buffer(ubuf, count, ppos, buf, len);
|
|
@@ -1301,7 +1234,7 @@ void ath12k_debugfs_fw_stats_init(struct
|
|
struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
|
|
ar->debug.debugfs_pdev);
|
|
|
|
- ar->debug.fw_stats.debugfs_fwstats = fwstats_dir;
|
|
+ ar->fw_stats.debugfs_fwstats = fwstats_dir;
|
|
|
|
/* all stats debugfs files created are under "fw_stats" directory
|
|
* created per PDEV
|
|
@@ -1315,11 +1248,6 @@ void ath12k_debugfs_fw_stats_init(struct
|
|
debugfs_create_file("en_vdev_stats_ol", 0600, fwstats_dir, ar,
|
|
&fops_vdev_stats_offload);
|
|
|
|
- INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
|
|
- INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
|
|
- INIT_LIST_HEAD(&ar->debug.fw_stats.bcn);
|
|
-
|
|
- init_completion(&ar->debug.fw_stats_complete);
|
|
}
|
|
|
|
int ath12k_pktlog_rx_filter_setting(struct ath12k *ar,
|
|
--- a/drivers/net/wireless/ath/ath12k/debugfs.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/debugfs.h
|
|
@@ -208,7 +208,8 @@ int ath12k_debugfs_register(struct ath12
|
|
void ath12k_debugfs_unregister(struct ath12k *ar);
|
|
int ath12k_debugfs_create(void);
|
|
void ath12k_debugfs_destroy(void);
|
|
-void ath12k_debugfs_fw_stats_process(struct ath12k_base *ab, struct sk_buff *skb);
|
|
+void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
|
+ struct ath12k_fw_stats *stats);
|
|
|
|
void ath12k_debugfs_fw_stats_init(struct ath12k *ar);
|
|
|
|
@@ -283,8 +284,8 @@ static inline void ath12k_debugfs_unregi
|
|
{
|
|
}
|
|
|
|
-static inline void ath12k_debugfs_fw_stats_process(struct ath12k_base *ab,
|
|
- struct sk_buff *skb)
|
|
+static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
|
+ struct ath11k_fw_stats *stats)
|
|
{
|
|
}
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/mac.c
|
|
@@ -8166,6 +8166,7 @@ static int ath12k_mac_radio_start(struct
|
|
ar->num_created_vdevs = 0;
|
|
ar->num_peers = 0;
|
|
ar->allocated_vdev_map = 0;
|
|
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
ar->awgn_intf_handling_in_prog = false;
|
|
@@ -8594,7 +8595,7 @@ static int ath12k_mac_vdev_create(struct
|
|
/* Send vdev stats offload commands to firmware before first vdev
|
|
* creation. ie., when num_created_vdevs = 0
|
|
*/
|
|
- if (ar->debug.fw_stats.en_vdev_stats_ol && !ar->num_created_vdevs) {
|
|
+ if (ar->fw_stats.en_vdev_stats_ol && !ar->num_created_vdevs) {
|
|
ret = ath12k_dp_tx_htt_h2t_vdev_stats_ol_req(ar, 0);
|
|
if (ret) {
|
|
ath12k_warn(ar->ab, "failed to request vdev stats offload: %d\n", ret);
|
|
@@ -9254,6 +9255,7 @@ static int ath12k_mac_op_add_chanctx(str
|
|
ar->rx_channel = ctx->def.chan;
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
|
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
|
return 0;
|
|
@@ -9285,6 +9287,7 @@ static void ath12k_mac_op_remove_chanctx
|
|
ar->rx_channel = NULL;
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
|
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
|
mutex_unlock(&ar->conf_mutex);
|
|
}
|
|
|
|
@@ -9870,6 +9873,7 @@ static void ath12k_mac_update_rx_channel
|
|
ar->rx_channel = NULL;
|
|
}
|
|
rcu_read_unlock();
|
|
+ ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
|
}
|
|
|
|
static void
|
|
@@ -12213,6 +12217,127 @@ static void ath12k_mac_op_sta_statistics
|
|
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
|
|
}
|
|
|
|
+static int ath12k_fw_stats_request(struct ath12k *ar,
|
|
+ struct stats_request_params *req_param)
|
|
+{
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+ unsigned long time_left;
|
|
+ int ret;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ar->fw_stats_done = false;
|
|
+ ath12k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ reinit_completion(&ar->fw_stats_complete);
|
|
+
|
|
+ ret = ath12k_wmi_send_stats_request_cmd(ar, req_param);
|
|
+ if (ret) {
|
|
+ ath12k_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;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ int *dbm)
|
|
+{
|
|
+ struct ath12k_hw *ah = hw->priv;
|
|
+ struct stats_request_params req_param;
|
|
+ struct ath12k_fw_stats_pdev *pdev;
|
|
+ struct ath12k *ar;
|
|
+ struct ath12k_base *ab;
|
|
+ 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(&ah->conf_mutex);
|
|
+
|
|
+ ar = ath12k_get_ar_by_vif(hw, vif, 0);
|
|
+ if (!ar) {
|
|
+ goto err_fallback;
|
|
+ }
|
|
+
|
|
+ ab = ar->ab;
|
|
+ if (ar->state != ATH12K_STATE_ON) {
|
|
+ goto err_fallback;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+ if (test_bit(ATH12K_CAC_RUNNING, &ar->dev_flags)) {
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ mutex_unlock(&ah->conf_mutex);
|
|
+ *dbm = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Limit the requests to Firmware for fetching the tx power */
|
|
+ if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID &&
|
|
+ time_before(jiffies, msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) +
|
|
+ ar->last_tx_power_update))
|
|
+ goto get_tx_power;
|
|
+
|
|
+ req_param.pdev_id = ar->pdev->pdev_id;
|
|
+ req_param.vdev_id = 0;
|
|
+ req_param.stats_id = WMI_REQUEST_PDEV_STAT;
|
|
+
|
|
+ ret = ath12k_fw_stats_request(ar, &req_param);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ goto err_fallback;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
|
|
+ struct ath12k_fw_stats_pdev,
|
|
+ list);
|
|
+ if (!pdev) {
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ goto err_fallback;
|
|
+ }
|
|
+
|
|
+ /* tx power is set as 2 units per dBm in FW. */
|
|
+ ar->chan_tx_pwr = pdev->chan_tx_power/2;
|
|
+ ar->last_tx_power_update = jiffies;
|
|
+
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+get_tx_power:
|
|
+ *dbm = ar->chan_tx_pwr;
|
|
+
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ mutex_unlock(&ah->conf_mutex);
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_MAC, "txpower reported %d dBm\n", *dbm);
|
|
+ return 0;
|
|
+
|
|
+err_fallback:
|
|
+ mutex_unlock(&ah->conf_mutex);
|
|
+ /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
|
|
+ *dbm = vif->bss_conf.txpower;
|
|
+ ath12k_dbg(NULL, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
|
|
+ *dbm);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const struct ieee80211_ops ath12k_ops = {
|
|
.tx = ath12k_mac_op_tx,
|
|
.start = ath12k_mac_op_start,
|
|
@@ -12254,6 +12379,7 @@ static const struct ieee80211_ops ath12k
|
|
#ifdef CPTCFG_ATH12K_DEBUGFS
|
|
.sta_add_debugfs = ath12k_debugfs_sta_op_add,
|
|
#endif
|
|
+ .get_txpower = ath12k_mac_op_get_txpower,
|
|
};
|
|
|
|
static void ath12k_mac_update_ch_list(struct ath12k *ar,
|
|
@@ -12978,6 +13104,11 @@ static int ath12k_mac_hw_register(struct
|
|
goto err_unregister_hw;
|
|
}
|
|
|
|
+ /* fw stats init is separately required since fw pdev stats is also used
|
|
+ * in get_txpower mac ops hence it is handled separately from debugfs
|
|
+ */
|
|
+ ath12k_fw_stats_init(ar);
|
|
+
|
|
ret = ath12k_debugfs_register(ar);
|
|
if (ret) {
|
|
ath12k_err(ar->ab, "debugfs registration failed: %d\n", ret);
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
|
|
@@ -6867,7 +6867,7 @@ void ath12k_wmi_fw_stats_fill(struct ath
|
|
spin_lock_bh(&ar->data_lock);
|
|
|
|
if (stats_id == WMI_REQUEST_PDEV_STAT) {
|
|
- pdev = list_first_entry_or_null(&fw_stats->pdevs,
|
|
+ pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
|
|
struct ath12k_fw_stats_pdev, list);
|
|
if (!pdev) {
|
|
ath12k_warn(ar->ab, "failed to get pdev stats\n");
|
|
@@ -7929,7 +7929,52 @@ static void ath12k_peer_assoc_conf_event
|
|
|
|
static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb)
|
|
{
|
|
- ath12k_debugfs_fw_stats_process(ab, skb);
|
|
+ struct ath12k_fw_stats stats = {};
|
|
+ struct ath12k *ar;
|
|
+ int ret;
|
|
+
|
|
+ INIT_LIST_HEAD(&stats.pdevs);
|
|
+ INIT_LIST_HEAD(&stats.vdevs);
|
|
+ INIT_LIST_HEAD(&stats.bcn);
|
|
+
|
|
+ ret = ath12k_wmi_pull_fw_stats(ab, skb, &stats);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "failed to pull fw stats: %d\n", ret);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ rcu_read_lock();
|
|
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, stats.pdev_id);
|
|
+ if (!ar) {
|
|
+ rcu_read_unlock();
|
|
+ ath12k_warn(ab, "failed to get ar for pdev_id %d: %d\n",
|
|
+ stats.pdev_id, ret);
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+
|
|
+ /* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via
|
|
+ * debugfs fw stats. Therfore, processing it separately.
|
|
+ */
|
|
+ if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
|
|
+ list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
|
|
+ ar->fw_stats_done = true;
|
|
+ goto complete;
|
|
+ }
|
|
+
|
|
+ /* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested
|
|
+ * only via debugfs fw stats. Hence, processing these two in debugfs context
|
|
+ */
|
|
+ ath12k_debugfs_fw_stats_process(ar, &stats);
|
|
+
|
|
+complete:
|
|
+ complete(&ar->fw_stats_complete);
|
|
+ rcu_read_unlock();
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+free:
|
|
+ ath12k_fw_stats_free(&stats);
|
|
}
|
|
|
|
/* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned
|
|
--- a/drivers/net/wireless/ath/ath12k/mac.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/mac.h
|
|
@@ -128,6 +128,9 @@ struct ath12k_generic_iter {
|
|
#define ATH12K_OBSS_PD_SRG_EN BIT(30)
|
|
#define ATH12K_OBSS_PD_NON_SRG_EN BIT(31)
|
|
|
|
+#define ATH12K_PDEV_TX_POWER_INVALID (-1)
|
|
+#define ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS 5000 /* msecs */
|
|
+
|
|
extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default;
|
|
|
|
void ath12k_mac_ap_ps_recalc(struct ath12k *ar);
|