]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath12k: Request vdev stats from firmware
authorRamya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
Fri, 24 Jan 2025 18:53:28 +0000 (00:23 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Sun, 26 Jan 2025 18:41:28 +0000 (10:41 -0800)
Add support to request and print vdev stats from firmware through WMI.

Sample output:
-------------
cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/fw_stats/vdev_stats

             ath12k VDEV stats
             =================

                       VDEV ID 0
              VDEV MAC address 00:03:7f:6c:9c:1a
                    beacon snr 96
                      data snr 255
                 num rx frames 0
                  num rts fail 0
               num rts success 0
                    num rx err 0
                num rx discard 0
              num tx not acked 0
            num tx frames [00] 0
            num tx frames [01] 0
            num tx frames [02] 0
            num tx frames [03] 2
    num tx frames retries [00] 0
    num tx frames retries [01] 0
    num tx frames retries [02] 0
    num tx frames retries [03] 0
   num tx frames failures [00] 0
   num tx frames failures [01] 0
   num tx frames failures [02] 0
   num tx frames failures [03] 0
          tx rate history [00] 0x00000000
          tx rate history [01] 0x00000000
          tx rate history [02] 0x00000000
          tx rate history [03] 0x00000000
          tx rate history [04] 0x00000000
          tx rate history [05] 0x00000000
          tx rate history [06] 0x00000000
          tx rate history [07] 0x00000000
          tx rate history [08] 0x00000000
          tx rate history [09] 0x00000000
      beacon rssi history [00] 0
      beacon rssi history [01] 0
      beacon rssi history [02] 0
      beacon rssi history [03] 0
      beacon rssi history [04] 0
      beacon rssi history [05] 0
      beacon rssi history [06] 0
      beacon rssi history [07] 0
      beacon rssi history [08] 0
      beacon rssi history [09] 0

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: Ramya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
Reviewed-by: Aditya Kumar Singh <aditya.kumar.singh@oss.qualcomm.com>
Link: https://patch.msgid.link/20250124185330.1244585-2-ramya.gnanasekar@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/debugfs.c
drivers/net/wireless/ath/ath12k/debugfs.h
drivers/net/wireless/ath/ath12k/wmi.c
drivers/net/wireless/ath/ath12k/wmi.h

index e036f2e00eb7dea5a0e476f32dbca4f0075736bb..97939ddb3cd7c834e8316476dafe623c17bf1f34 100644 (file)
@@ -557,6 +557,7 @@ struct ath12k_fw_stats {
        struct list_head pdevs;
        struct list_head vdevs;
        struct list_head bcn;
+       bool fw_stats_done;
 };
 
 struct ath12k_dbg_htt_stats {
@@ -728,6 +729,7 @@ struct ath12k {
        struct completion mlo_setup_done;
        u32 mlo_setup_status;
        u8 ftm_msgref;
+       struct ath12k_fw_stats fw_stats;
 };
 
 struct ath12k_hw {
@@ -1078,6 +1080,25 @@ struct ath12k_pdev_map {
        u8 pdev_idx;
 };
 
+struct ath12k_fw_stats_vdev {
+       struct list_head list;
+
+       u32 vdev_id;
+       u32 beacon_snr;
+       u32 data_snr;
+       u32 num_tx_frames[WLAN_MAX_AC];
+       u32 num_rx_frames;
+       u32 num_tx_frames_retries[WLAN_MAX_AC];
+       u32 num_tx_frames_failures[WLAN_MAX_AC];
+       u32 num_rts_fail;
+       u32 num_rts_success;
+       u32 num_rx_err;
+       u32 num_rx_discard;
+       u32 num_tx_not_acked;
+       u32 tx_rate_history[MAX_TX_RATE_VALUES];
+       u32 beacon_rssi_history[MAX_TX_RATE_VALUES];
+};
+
 int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab);
 int ath12k_core_pre_init(struct ath12k_base *ab);
 int ath12k_core_init(struct ath12k_base *ath12k);
index d4b32d1a431c0dfa6894cb8a42722ca20444f3db..1ee9a3e00514fe50d0c4a5f3f675257851c7084b 100644 (file)
@@ -1,10 +1,11 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include "core.h"
+#include "debug.h"
 #include "debugfs.h"
 #include "debugfs_htt_stats.h"
 
@@ -68,6 +69,199 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
         */
 }
 
+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_debugfs_fw_stats_reset(struct ath12k *ar)
+{
+       spin_lock_bh(&ar->data_lock);
+       ar->fw_stats.fw_stats_done = false;
+       ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath12k_debugfs_fw_stats_request(struct ath12k *ar,
+                                          struct ath12k_fw_stats_req_params *param)
+{
+       struct ath12k_base *ab = ar->ab;
+       unsigned long timeout, time_left;
+       int ret;
+
+       lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+       /* 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
+        */
+       timeout = jiffies + msecs_to_jiffies(3 * 1000);
+
+       ath12k_debugfs_fw_stats_reset(ar);
+
+       reinit_completion(&ar->fw_stats_complete);
+
+       ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
+                                               param->vdev_id, param->pdev_id);
+
+       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 the wait timed out, return -ETIMEDOUT */
+       if (!time_left)
+               return -ETIMEDOUT;
+
+       /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
+        * when stats data buffer limit is reached. fw_stats_complete
+        * is completed once host receives first event from firmware, but
+        * still end might not be marked in the TLV.
+        * Below loop is to confirm that firmware completed sending all the event
+        * and fw_stats_done is marked true when end is marked in the TLV
+        */
+       for (;;) {
+               if (time_after(jiffies, timeout))
+                       break;
+
+               spin_lock_bh(&ar->data_lock);
+               if (ar->fw_stats.fw_stats_done) {
+                       spin_unlock_bh(&ar->data_lock);
+                       break;
+               }
+               spin_unlock_bh(&ar->data_lock);
+       }
+       return 0;
+}
+
+void
+ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+                               struct ath12k_fw_stats *stats)
+{
+       struct ath12k_base *ab = ar->ab;
+       struct ath12k_pdev *pdev;
+       bool is_end;
+       static unsigned int num_vdev;
+       size_t total_vdevs_started = 0;
+       int i;
+
+       if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
+               if (list_empty(&stats->vdevs)) {
+                       ath12k_warn(ab, "empty vdev stats");
+                       return;
+               }
+               /* FW sends all the active VDEV stats irrespective of PDEV,
+                * hence limit until the count of all VDEVs started
+                */
+               rcu_read_lock();
+               for (i = 0; i < ab->num_radios; i++) {
+                       pdev = rcu_dereference(ab->pdevs_active[i]);
+                       if (pdev && pdev->ar)
+                               total_vdevs_started += pdev->ar->num_started_vdevs;
+               }
+               rcu_read_unlock();
+
+               is_end = ((++num_vdev) == total_vdevs_started);
+
+               list_splice_tail_init(&stats->vdevs,
+                                     &ar->fw_stats.vdevs);
+
+               if (is_end) {
+                       ar->fw_stats.fw_stats_done = true;
+                       num_vdev = 0;
+               }
+               return;
+       }
+}
+
+static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
+{
+       struct ath12k *ar = inode->i_private;
+       struct ath12k_fw_stats_req_params param;
+       struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+       int ret;
+
+       guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+       if (!ah)
+               return -ENETDOWN;
+
+       if (ah->state != ATH12K_HW_STATE_ON)
+               return -ENETDOWN;
+
+       void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
+       if (!buf)
+               return -ENOMEM;
+
+       param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
+       /* VDEV stats is always sent for all active VDEVs from FW */
+       param.vdev_id = 0;
+       param.stats_id = WMI_REQUEST_VDEV_STAT;
+
+       ret = ath12k_debugfs_fw_stats_request(ar, &param);
+       if (ret) {
+               ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
+               return ret;
+       }
+
+       ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
+                                buf);
+
+       file->private_data = no_free_ptr(buf);
+
+       return 0;
+}
+
+static int ath12k_release_vdev_stats(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+
+       return 0;
+}
+
+static ssize_t ath12k_read_vdev_stats(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       const char *buf = file->private_data;
+       size_t len = strlen(buf);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_vdev_stats = {
+       .open = ath12k_open_vdev_stats,
+       .release = ath12k_release_vdev_stats,
+       .read = ath12k_read_vdev_stats,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static
+void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
+{
+       struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
+                                                       ar->debug.debugfs_pdev);
+
+       /* all stats debugfs files created are under "fw_stats" directory
+        * created per PDEV
+        */
+       debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
+                           &fops_vdev_stats);
+
+       INIT_LIST_HEAD(&ar->fw_stats.vdevs);
+       init_completion(&ar->fw_stats_complete);
+}
+
 void ath12k_debugfs_register(struct ath12k *ar)
 {
        struct ath12k_base *ab = ar->ab;
@@ -92,6 +286,7 @@ void ath12k_debugfs_register(struct ath12k *ar)
        }
 
        ath12k_debugfs_htt_stats_register(ar);
+       ath12k_debugfs_fw_stats_register(ar);
 }
 
 void ath12k_debugfs_unregister(struct ath12k *ar)
index 8d64ba03aa9ad1a471a6f01097dc7cc8d5aca278..1c30745ee415322864159e8b75812d2befc3530f 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _ATH12K_DEBUGFS_H_
@@ -12,6 +12,9 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
 void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
 void ath12k_debugfs_register(struct ath12k *ar);
 void ath12k_debugfs_unregister(struct ath12k *ar);
+void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+                                    struct ath12k_fw_stats *stats);
+void ath12k_debugfs_fw_stats_reset(struct ath12k *ar);
 #else
 static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
 {
@@ -29,6 +32,14 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
 {
 }
 
+static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
+                                                  struct ath12k_fw_stats *stats)
+{
+}
+
+static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
+{
+}
 #endif /* CONFIG_ATH12K_DEBUGFS */
 
 #endif /* _ATH12K_DEBUGFS_H_ */
index c7d128d41a9222debdca477dc6c08e1372ccd304..8ffb215f7aba933ff9ebedef04bf79e33fb9fd17 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/time.h>
 #include <linux/of.h>
 #include "core.h"
+#include "debugfs.h"
 #include "debug.h"
 #include "mac.h"
 #include "hw.h"
@@ -6853,12 +6854,156 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff
        rcu_read_unlock();
 }
 
+static void
+ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar,
+                             struct ath12k_fw_stats *fw_stats,
+                             char *buf, u32 *length)
+{
+       const struct ath12k_fw_stats_vdev *vdev;
+       u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+       struct ath12k_link_vif *arvif;
+       u32 len = *length;
+       u8 *vif_macaddr;
+       int i;
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath12k VDEV stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                        "=================");
+
+       list_for_each_entry(vdev, &fw_stats->vdevs, list) {
+               arvif = ath12k_mac_get_arvif(ar, vdev->vdev_id);
+               if (!arvif)
+                       continue;
+               vif_macaddr = arvif->ahvif->vif->addr;
+
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "VDEV ID", vdev->vdev_id);
+               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                                "VDEV MAC address", vif_macaddr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "beacon snr", vdev->beacon_snr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "data snr", vdev->data_snr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx frames", vdev->num_rx_frames);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rts fail", vdev->num_rts_fail);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rts success", vdev->num_rts_success);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx err", vdev->num_rx_err);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num rx discard", vdev->num_rx_discard);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "num tx not acked", vdev->num_tx_not_acked);
+
+               for (i = 0 ; i < WLAN_MAX_AC; i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                       "num tx frames", i,
+                                       vdev->num_tx_frames[i]);
+
+               for (i = 0 ; i < WLAN_MAX_AC; i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                       "num tx frames retries", i,
+                                       vdev->num_tx_frames_retries[i]);
+
+               for (i = 0 ; i < WLAN_MAX_AC; i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                       "num tx frames failures", i,
+                                       vdev->num_tx_frames_failures[i]);
+
+               for (i = 0 ; i < MAX_TX_RATE_VALUES; i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] 0x%08x\n",
+                                       "tx rate history", i,
+                                       vdev->tx_rate_history[i]);
+               for (i = 0 ; i < MAX_TX_RATE_VALUES; i++)
+                       len += scnprintf(buf + len, buf_len - len,
+                                       "%25s [%02d] %u\n",
+                                       "beacon rssi history", i,
+                                       vdev->beacon_rssi_history[i]);
+
+               len += scnprintf(buf + len, buf_len - len, "\n");
+               *length = len;
+       }
+}
+
+void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
+                             struct ath12k_fw_stats *fw_stats,
+                             u32 stats_id, char *buf)
+{
+       u32 len = 0;
+       u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (stats_id) {
+       case WMI_REQUEST_VDEV_STAT:
+               ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len);
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_bh(&ar->data_lock);
+
+       if (len >= buf_len)
+               buf[len - 1] = 0;
+       else
+               buf[len] = 0;
+
+       ath12k_debugfs_fw_stats_reset(ar);
+}
+
+static void
+ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src,
+                          struct ath12k_fw_stats_vdev *dst)
+{
+       int i;
+
+       dst->vdev_id = le32_to_cpu(src->vdev_id);
+       dst->beacon_snr = le32_to_cpu(src->beacon_snr);
+       dst->data_snr = le32_to_cpu(src->data_snr);
+       dst->num_rx_frames = le32_to_cpu(src->num_rx_frames);
+       dst->num_rts_fail = le32_to_cpu(src->num_rts_fail);
+       dst->num_rts_success = le32_to_cpu(src->num_rts_success);
+       dst->num_rx_err = le32_to_cpu(src->num_rx_err);
+       dst->num_rx_discard = le32_to_cpu(src->num_rx_discard);
+       dst->num_tx_not_acked = le32_to_cpu(src->num_tx_not_acked);
+
+       for (i = 0; i < WLAN_MAX_AC; i++)
+               dst->num_tx_frames[i] =
+                       le32_to_cpu(src->num_tx_frames[i]);
+
+       for (i = 0; i < WLAN_MAX_AC; i++)
+               dst->num_tx_frames_retries[i] =
+                       le32_to_cpu(src->num_tx_frames_retries[i]);
+
+       for (i = 0; i < WLAN_MAX_AC; i++)
+               dst->num_tx_frames_failures[i] =
+                       le32_to_cpu(src->num_tx_frames_failures[i]);
+
+       for (i = 0; i < MAX_TX_RATE_VALUES; i++)
+               dst->tx_rate_history[i] =
+                       le32_to_cpu(src->tx_rate_history[i]);
+
+       for (i = 0; i < MAX_TX_RATE_VALUES; i++)
+               dst->beacon_rssi_history[i] =
+                       le32_to_cpu(src->beacon_rssi_history[i]);
+}
+
 static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
                                              struct wmi_tlv_fw_stats_parse *parse,
                                              const void *ptr,
                                              u16 len)
 {
        const struct wmi_stats_event *ev = parse->ev;
+       struct ath12k_fw_stats stats = {0};
        struct ath12k *ar;
        struct ath12k_link_vif *arvif;
        struct ieee80211_sta *sta;
@@ -6867,6 +7012,8 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
        int i, ret = 0;
        const void *data = ptr;
 
+       INIT_LIST_HEAD(&stats.vdevs);
+
        if (!ev) {
                ath12k_warn(ab, "failed to fetch update stats ev");
                return -EPROTO;
@@ -6884,6 +7031,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
 
        for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) {
                const struct wmi_vdev_stats_params *src;
+               struct ath12k_fw_stats_vdev *dst;
 
                src = data;
                if (len < sizeof(*src)) {
@@ -6912,9 +7060,16 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
 
                data += sizeof(*src);
                len -= sizeof(*src);
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+               ath12k_wmi_pull_vdev_stats(src, dst);
+               stats.stats_id = WMI_REQUEST_VDEV_STAT;
+               list_add_tail(&dst->list, &stats.vdevs);
        }
 
        complete(&ar->fw_stats_complete);
+       ath12k_debugfs_fw_stats_process(ar, &stats);
 exit:
        rcu_read_unlock();
        return ret;
index 54ac7ca019fda4e5d139e0a975d6f70642b7faf6..13644dec50f92654e0520fed85f2f284d921b351 100644 (file)
@@ -25,6 +25,7 @@
 struct ath12k_base;
 struct ath12k;
 struct ath12k_link_vif;
+struct ath12k_fw_stats;
 
 /* There is no signed version of __le32, so for a temporary solution come
  * up with our own version. The idea is from fs/ntfs/endian.h.
@@ -5695,6 +5696,12 @@ struct wmi_vdev_stats_params {
        __le32 beacon_rssi_history[MAX_TX_RATE_VALUES];
 } __packed;
 
+struct ath12k_fw_stats_req_params {
+       u32 stats_id;
+       u32 vdev_id;
+       u32 pdev_id;
+};
+
 void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
                             struct ath12k_wmi_resource_config_arg *config);
 void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
@@ -5876,5 +5883,8 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar,
 int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params);
 int ath12k_wmi_mlo_ready(struct ath12k *ar);
 int ath12k_wmi_mlo_teardown(struct ath12k *ar);
+void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
+                             struct ath12k_fw_stats *fw_stats, u32 stats_id,
+                             char *buf);
 
 #endif