AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/27 09:12 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch only adds a debugfs entry to query and print statistics from the firmware. It is a diagnostic/logging change that does not functionally alter the driver's execution flow.",
  "WorthFuzzing": false
}

1/1 2026/08/27 09:12 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 5e5db5bd1d3ccec498ba228472e71b468aab4db5\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Aug 27 09:12:29 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h\nindex 0ededa569e9a5..bf0f105c786e6 100644\n--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h\n+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h\n@@ -1483,6 +1483,179 @@ enum {\n \tMT_NIC_CAP_EML_CAP = 0x22,\n };\n \n+enum {\n+\tUNI_CMD_ID_STATISTICS_BASIC = 0,\n+\tUNI_CMD_ID_STATISTICS_LINK_QUALITY = 1,\n+\tUNI_CMD_ID_STATISTICS_STA = 2,\n+\tUNI_CMD_ID_STATISTICS_BUG_REPORT = 3,\n+\tUNI_CMD_ID_STATISTICS_TX_STATS = 4,\n+\tUNI_CMD_ID_STATISTICS_ALL_STATS = 5,\n+\tUNI_CMD_ID_STATISTICS_EML_STATS = 6,\n+\tUNI_CMD_ID_STATISTICS_REGULAR_STATS = 7,\n+\tUNI_CMD_ID_STATISTICS_BSS_LINK_QUALITY = 8,\n+\tUNI_CMD_ID_STATISTICS_ML_CHNL_COND = 9,\n+\tUNI_CMD_ID_STATISTICS_WTBL = 10,\n+\tUNI_CMD_ID_STATISTICS_LINK_LAYER_STATS = 128,\n+\tUNI_CMD_ID_STATISTICS_PPDU_LATENCY = 129,\n+\tUNI_CMD_ID_STATISTICS_CURRENT_TX_RATE = 130,\n+\tUNI_CMD_ID_STATISTICS_SET_TX_BITRATE_MONITOR = 131,\n+\tUNI_CMD_ID_STATISTICS_GET_PREDICT_TX_BITRATE = 132,\n+\tUNI_CMD_ID_STATISTICS_SET_ATPS_TYPE_RATE_MODE = 133,\n+\tUNI_CMD_ID_STATISTICS_SET_BEACON_REPORT = 134,\n+\tUNI_CMD_ID_STATISTICS_BSS_CURRENT_TX_RATE = 135,\n+\tUNI_CMD_ID_STATISTICS_GET_BSS_PREDICT_TX_BITRATE = 136,\n+\tUNI_CMD_ID_STATISTICS_MAX_NUM\n+};\n+\n+enum mt7925_mib_counter {\n+\tUNI_CMD_MIB_CNT_RX_FCS_ERR = 0,\n+\tUNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW = 1,\n+\tUNI_CMD_MIB_CNT_RX_MPDU = 2,\n+\tUNI_CMD_MIB_CNT_AMPDU_RX_COUNT = 3,\n+\tUNI_CMD_MIB_CNT_RX_TOTAL_BYTE = 4,\n+\tUNI_CMD_MIB_CNT_RX_VALID_AMPDU_SF = 5,\n+\tUNI_CMD_MIB_CNT_RX_VALID_BYTE = 6,\n+\tUNI_CMD_MIB_CNT_CHANNEL_IDLE = 7,\n+\tUNI_CMD_MIB_CNT_VEC_DROP = 8,\n+\tUNI_CMD_MIB_CNT_DELIMITER_FAIL = 9,\n+\tUNI_CMD_MIB_CNT_VEC_MISMATCH = 10,\n+\tUNI_CMD_MIB_CNT_MDRDY = 11,\n+\tUNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME = 12,\n+\tUNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME = 13,\n+\tUNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME = 14,\n+\tUNI_CMD_MIB_CNT_PF_DROP = 15,\n+\tUNI_CMD_MIB_CNT_LEN_MISMATCH = 16,\n+\tUNI_CMD_MIB_CNT_P_CCA_TIME = 17,\n+\tUNI_CMD_MIB_CNT_S_CCA_TIME = 18,\n+\tUNI_CMD_MIB_CNT_CCA_NAV_TX_TIME = 19,\n+\tUNI_CMD_MIB_CNT_P_ED_TIME = 20,\n+\tUNI_CMD_MIB_CNT_BCN_TX = 21,\n+\tUNI_CMD_MIB_CNT_TX_BW_20MHZ = 22,\n+\tUNI_CMD_MIB_CNT_TX_BW_40MHZ = 23,\n+\tUNI_CMD_MIB_CNT_TX_BW_80MHZ = 24,\n+\tUNI_CMD_MIB_CNT_TX_BW_160MHZ = 25,\n+\tUNI_CMD_RMAC_CNT_OBSS_AIRTIME = 26,\n+\tUNI_CMD_RMAC_CNT_NONWIFI_AIRTIME = 27,\n+\tUNI_CMD_MIB_CNT_TX_DUR_CNT = 28,\n+\tUNI_CMD_MIB_CNT_RX_DUR_CNT = 29,\n+\tUNI_CMD_MIB_CNT_BA_CNT = 30,\n+\tUNI_CMD_MIB_CNT_MAC2PHY_TX_TIME = 31,\n+\tUNI_CMD_MIB_CNT_RX_OUT_OF_RANGE_COUNT = 32,\n+\tUNI_CMD_MIB_CNT_IBF_TX = 33,\n+\tUNI_CMD_MIB_CNT_EBF_TX = 34,\n+\tUNI_CMD_MIB_CNT_MUBF_TX = 35,\n+\tUNI_CMD_MIB_CNT_RX_BF_VHTFBK = 36,\n+\tUNI_CMD_MIB_CNT_RX_BF_HTFBK = 37,\n+\tUNI_CMD_MIB_CNT_RX_BF_HEFBK = 38,\n+\tUNI_CMD_MIB_CNT_BFEE_TXFBK_BFPOLL_TRI = 39,\n+\tUNI_CMD_MIB_CNT_BFEE_TXFBK_TRI = 40,\n+\tUNI_CMD_MIB_CNT_BFEE_RX_FBKCQI = 41,\n+\tUNI_CMD_MIB_CNT_BFEE_RX_NDP = 42,\n+\tUNI_CMD_MIB_CNT_RX_BF_IBF_UPT = 43,\n+\tUNI_CMD_MIB_CNT_RX_BF_EBF_UPT = 44,\n+\tUNI_CMD_MIB_CNT_BFEE_SP_ABORT = 45,\n+\tUNI_CMD_MIB_CNT_BFEE_TB_LEN_ERR = 46,\n+\tUNI_CMD_MIB_CNT_BFEE_TXFBK_MUTE = 47,\n+\tUNI_CMD_MIB_CNT_BFEE_TMAC_ABORT = 48,\n+\tUNI_CMD_MIB_CNT_BFEE_TXFBK_CPL = 49,\n+\tUNI_CMD_MIB_CNT_BFEE_COANT_BLKTX = 50,\n+\tUNI_CMD_MIB_CNT_BFEE_FBK_SEG = 51,\n+\tUNI_CMD_MIB_CNT_NAV_TIME = 52,\n+\tUNI_CMD_MIB_CNT_TX_ABORT_CNT1 = 53,\n+\tUNI_CMD_MIB_CNT_TX_ABORT_CNT0 = 54,\n+\tUNI_CMD_MIB_CNT_TX_ABORT_CNT3 = 55,\n+\tUNI_CMD_MIB_CNT_TX_ABORT_CNT2 = 56,\n+\tUNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT = 57,\n+\tUNI_CMD_MIB_CNT_BSS1_RTS_TX_CNT = 58,\n+\tUNI_CMD_MIB_CNT_BSS2_RTS_TX_CNT = 59,\n+\tUNI_CMD_MIB_CNT_BSS3_RTS_TX_CNT = 60,\n+\tUNI_CMD_MIB_CNT_BSS0_RTS_RETRY = 61,\n+\tUNI_CMD_MIB_CNT_BSS1_RTS_RETRY = 62,\n+\tUNI_CMD_MIB_CNT_BSS2_RTS_RETRY = 63,\n+\tUNI_CMD_MIB_CNT_BSS3_RTS_RETRY = 64,\n+\tUNI_CMD_MIB_CNT_BSS0_BA_MISS = 65,\n+\tUNI_CMD_MIB_CNT_BSS1_BA_MISS = 66,\n+\tUNI_CMD_MIB_CNT_BSS2_BA_MISS = 67,\n+\tUNI_CMD_MIB_CNT_BSS3_BA_MISS = 68,\n+\tUNI_CMD_MIB_CNT_BSS0_ACK_FAIL = 69,\n+\tUNI_CMD_MIB_CNT_BSS1_ACK_FAIL = 70,\n+\tUNI_CMD_MIB_CNT_BSS2_ACK_FAIL = 71,\n+\tUNI_CMD_MIB_CNT_BSS3_ACK_FAIL = 72,\n+\tUNI_CMD_MIB_CNT_BSS0_FRAME_RETRY = 73,\n+\tUNI_CMD_MIB_CNT_BSS1_FRAME_RETRY = 74,\n+\tUNI_CMD_MIB_CNT_BSS2_FRAME_RETRY = 75,\n+\tUNI_CMD_MIB_CNT_BSS3_FRAME_RETRY = 76,\n+\tUNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2 = 77,\n+\tUNI_CMD_MIB_CNT_BSS1_FRAME_RETRY_2 = 78,\n+\tUNI_CMD_MIB_CNT_BSS2_FRAME_RETRY_2 = 79,\n+\tUNI_CMD_MIB_CNT_BSS3_FRAME_RETRY_2 = 80,\n+\tUNI_CMD_MIB_CNT_RX_A1_SEARCH = 81,\n+\tUNI_CMD_MIB_CNT_RX_DROP_MPDU = 82,\n+\tUNI_CMD_MIB_CNT_RX_UNWANTED = 83,\n+\tUNI_CMD_MIB_CNT_RX_FCS_OK = 84,\n+\tUNI_CMD_MIB_CNT_SU_TX_OK = 85,\n+\tUNI_CMD_MIB_CNT_TX_FULL_BW = 86,\n+\tUNI_CMD_MIB_CNT_TX_AUTO_BW = 87,\n+\tUNI_CMD_MIB_CNT_CCA_SAMPLE_IDLE = 88,\n+\tUNI_CMD_MIB_CNT_CCA_SAMPLE_ACTIVE = 89,\n+\tUNI_CMD_MIB_CNT_CCA_TIME = 90,\n+\tUNI_CMD_MIB_CNT_S_20BW_CCA_TIME = 91,\n+\tUNI_CMD_MIB_CNT_S_40BW_CCA_TIME = 92,\n+\tUNI_CMD_MIB_CNT_S_80BW_CCA_TIME = 93,\n+\tUNI_CMD_MIB_CNT_S_160BW_CCA_TIME = 94,\n+\tUNI_CMD_MIB_CNT_S_P20BW_0_ED_TIME = 95,\n+\tUNI_CMD_MIB_CNT_S_P20BW_1_ED_TIME = 96,\n+\tUNI_CMD_MIB_CNT_S_P20BW_2_ED_TIME = 97,\n+\tUNI_CMD_MIB_CNT_S_P20BW_3_ED_TIME = 98,\n+\tUNI_CMD_MIB_CNT_S_P20BW_4_ED_TIME = 99,\n+\tUNI_CMD_MIB_CNT_S_P20BW_5_ED_TIME = 100,\n+\tUNI_CMD_MIB_CNT_S_P20BW_6_ED_TIME = 101,\n+\tUNI_CMD_MIB_CNT_S_P20BW_7_ED_TIME = 102,\n+\tUNI_CMD_MIB_CNT_S_P20BW_8_ED_TIME = 103,\n+\tUNI_CMD_MIB_CNT_S_P20BW_9_ED_TIME = 104,\n+\tUNI_CMD_MIB_CNT_S_P20BW_10_ED_TIME = 105,\n+\tUNI_CMD_MIB_CNT_S_P20BW_11_ED_TIME = 106,\n+\tUNI_CMD_MIB_CNT_S_P20BW_12_ED_TIME = 107,\n+\tUNI_CMD_MIB_CNT_S_P20BW_13_ED_TIME = 108,\n+\tUNI_CMD_MIB_CNT_S_P20BW_14_ED_TIME = 109,\n+\tUNI_CMD_MIB_CNT_S_P20BW_15_ED_TIME = 110,\n+\tUNI_CMD_MIB_CNT_TX_BW_320MHZ = 111,\n+\tUNI_CMD_MIB_CNT_TX_DDLMT_RNG0 = 112, /* Rng0~Rng4 112~116 */\n+\tUNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_3 = 117, /* 117~120 */\n+\tUNI_CMD_MIB_CNT_BSS0_TX = 121, /* 121~124 */\n+\tUNI_CMD_MIB_CNT_BSS0_TX_DATA = 125, /* 125~128 */\n+\tUNI_CMD_MIB_CNT_BSS0_TX_BYTE = 129, /* 129~132 */\n+\tUNI_CMD_MIB_CNT_RX_OK_BSS0 = 133, /* 133~136 */\n+\tUNI_CMD_MIB_CNT_RX_BYTE_BSS0 = 137, /* 137~140 */\n+\tUNI_CMD_MIB_CNT_RX_DATA_BSS0 = 141, /* 141~144 */\n+\tUNI_CMD_MIB_CNT_MBSS0_TX_OK = 145, /* 145~160 */\n+\tUNI_CMD_MIB_CNT_MBSS0_TX_BYTE = 161, /* 161~176 */\n+\tUNI_CMD_MIB_CNT_RX_OK_MBSS0 = 177, /* 177~192 */\n+\tUNI_CMD_MIB_CNT_RX_BYTE_MBSS0 = 193, /* 193~208 */\n+\tUNI_CMD_MIB_CNT_AMPDU = 209,\n+\tUNI_CMD_MIB_CNT_AMPDU_MPDU = 210,\n+\tUNI_CMD_MIB_CNT_AMPDU_ACKED = 211,\n+\tUNI_CMD_MIB_CNT_MAX_NUM\n+};\n+\n+enum ENUM_UNI_CMD_GET_STATISTICS_TAG {\n+\tUNI_CMD_GET_STATISTICS_TAG_BASIC = 0,\n+\tUNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1,\n+\tUNI_CMD_GET_STATISTICS_TAG_STA = 2,\n+\tUNI_CMD_GET_STATISTICS_TAG_BUG_REPORT = 3,\n+\tUNI_CMD_GET_STATISTICS_TAG_EML_STATS = 6,\n+\tUNI_CMD_GET_STATISTICS_TAG_REGULAR_STATS = 7,\n+\tUNI_CMD_GET_STATISTICS_TAG_BSS_LINK_QUALITY = 8,\n+\tUNI_CMD_GET_STATISTICS_TAG_GET_ML_CHNL_COND = 9,\n+\tUNI_CMD_GET_STATISTICS_TAG_STAT_WTBL = 10,\n+\tUNI_CMD_GET_STATISTICS_TAG_LINK_LAYER_STATS = 0x80,\n+\tUNI_CMD_GET_STATISTICS_TAG_PPDU_LATENCY,\n+\tUNI_CMD_GET_STATISTICS_TAG_CURRENT_TX_RATE,\n+\tUNI_CMD_GET_STATISTICS_TAG_BEACON_REPORT = 0x86,\n+\tUNI_CMD_GET_STATISTICS_TAG_BSS_CURRENT_TX_RATE = 0x87,\n+\tUNI_CMD_GET_STATISTICS_TAG_BSS_PRED_TX_BITRATE = 0x88,\n+};\n+\n #define UNI_WOW_DETECT_TYPE_MAGIC\t\tBIT(0)\n #define UNI_WOW_DETECT_TYPE_ANY\t\t\tBIT(1)\n #define UNI_WOW_DETECT_TYPE_DISCONNECT\t\tBIT(2)\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile\nindex f9dcc0bba393c..3863a89b89742 100644\n--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile\n+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile\n@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o\n obj-$(CONFIG_MT7925E) += mt7925e.o\n obj-$(CONFIG_MT7925U) += mt7925u.o\n \n-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o\n+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o stats.o\n mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o\n mt7925e-y := pci.o pci_mac.o pci_mcu.o\n mt7925u-y := usb.o\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c\nindex d01ff49de47af..7a40d5ac47dac 100644\n--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c\n+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c\n@@ -1,8 +1,11 @@\n // SPDX-License-Identifier: BSD-3-Clause-Clear\n /* Copyright (C) 2023 MediaTek Inc. */\n \n+#include \u003clinux/math64.h\u003e\n+\n #include \"mt7925.h\"\n #include \"mcu.h\"\n+#include \"stats.h\"\n \n static int\n mt7925_reg_set(void *data, u64 val)\n@@ -286,6 +289,346 @@ static int mt7925_chip_reset(void *data, u64 val)\n \n DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, \"%lld\\n\");\n \n+static const u32 stats_query_mib_counters[] = {\n+\tUNI_CMD_MIB_CNT_RX_FCS_ERR,\n+\tUNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW,\n+\tUNI_CMD_MIB_CNT_RX_MPDU,\n+\tUNI_CMD_MIB_CNT_AMPDU_RX_COUNT,\n+\tUNI_CMD_MIB_CNT_PF_DROP,\n+\tUNI_CMD_MIB_CNT_LEN_MISMATCH,\n+\tUNI_CMD_MIB_CNT_CHANNEL_IDLE,\n+\tUNI_CMD_MIB_CNT_CCA_NAV_TX_TIME,\n+\tUNI_CMD_MIB_CNT_MDRDY,\n+\tUNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME,\n+\tUNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,\n+\tUNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,\n+\tUNI_CMD_MIB_CNT_P_CCA_TIME,\n+\tUNI_CMD_MIB_CNT_S_CCA_TIME,\n+\tUNI_CMD_MIB_CNT_P_ED_TIME,\n+\tUNI_CMD_MIB_CNT_BCN_TX,\n+\tUNI_CMD_MIB_CNT_TX_BW_40MHZ,\n+\tUNI_CMD_MIB_CNT_TX_BW_80MHZ,\n+\tUNI_CMD_MIB_CNT_TX_BW_160MHZ,\n+\tUNI_CMD_MIB_CNT_BSS0_BA_MISS,\n+\tUNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT,\n+\tUNI_CMD_MIB_CNT_BSS0_FRAME_RETRY,\n+\tUNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2,\n+\tUNI_CMD_MIB_CNT_BSS0_RTS_RETRY,\n+\tUNI_CMD_MIB_CNT_BSS0_ACK_FAIL,\n+\tUNI_CMD_MIB_CNT_AMPDU,\n+\tUNI_CMD_MIB_CNT_AMPDU_MPDU,\n+\tUNI_CMD_MIB_CNT_AMPDU_ACKED,\n+};\n+\n+/* Dump the whole firmware auto-rate table and mark the entry currently in use\n+ * with \"--\u003e\"\n+ */\n+static void\n+mt7925_dump_auto_rate_table(struct seq_file *s,\n+\t\t\t    const struct mt7925_wtbl_rate *wr)\n+{\n+\tu8 fcap = wr-\u003efcap;\n+\tu8 rate_idx = wr-\u003erate_idx;\n+\tu8 cbrn = wr-\u003ecbrn;\n+\tint i;\n+\n+\tseq_printf(s, \"%-22s  Cur Rate Index = %u\\n\", \" \", rate_idx);\n+\n+\tfor (i = 0; i \u003c MT7925_AUTO_RATE_NUM; i++) {\n+\t\tu16 rate_code = le16_to_cpu(wr-\u003erate_code[i]);\n+\t\tu8 txmode = MT7925_HW_RATE_TO_MODE(rate_code);\n+\t\tu8 rate = MT7925_HW_RATE_TO_MCS(rate_code);\n+\t\tu8 nsts = MT7925_HW_RATE_TO_NSS(rate_code) + 1;\n+\t\tu8 stbc = MT7925_HW_RATE_TO_STBC(rate_code);\n+\t\tu8 dcm = MT7925_HW_RATE_TO_DCM(rate_code);\n+\t\tu8 ersu106t = MT7925_HW_RATE_TO_106T(rate_code);\n+\t\tu8 sgi = mt7925_wtbl_get_sgi(wr, txmode, fcap);\n+\t\tu8 ldpc = mt7925_wtbl_get_ldpc(wr, txmode);\n+\t\tconst char *mode_str = \"N/A\";\n+\n+\t\tif (dcm)\n+\t\t\trate = MT7925_HW_RATE_UNMASK_DCM(rate);\n+\t\tif (ersu106t)\n+\t\t\trate = MT7925_HW_RATE_UNMASK_106T(rate);\n+\n+\t\tif (txmode \u003c ARRAY_SIZE(mt7925_tx_mode_str))\n+\t\t\tmode_str = mt7925_tx_mode_str[txmode];\n+\n+\t\tseq_printf(s, \"Rate index[%d]    %s\", i,\n+\t\t\t   rate_idx == i ? \"--\u003e \" : \"    \");\n+\n+\t\t/* rate/MCS field */\n+\t\tif (txmode == MT7925_TX_RATE_MODE_CCK)\n+\t\t\tseq_printf(s, \"%s, \",\n+\t\t\t\t   mt7925_tx_rate_cck_str[rate \u0026 0x3]);\n+\t\telse if (txmode == MT7925_TX_RATE_MODE_OFDM)\n+\t\t\tseq_printf(s, \"%s, \", mt7925_hw_rate_ofdm_str(rate));\n+\t\telse if (txmode == MT7925_TX_RATE_MODE_HTMIX ||\n+\t\t\t txmode == MT7925_TX_RATE_MODE_HTGF)\n+\t\t\tseq_printf(s, \"MCS%d, \", rate);\n+\t\telse\n+\t\t\tseq_printf(s, \"%s%d_MCS%d, \",\n+\t\t\t\t   stbc ? \"NSTS\" : \"NSS\", nsts, rate);\n+\n+\t\t/* bandwidth: mirror the CCK/OFDM -\u003e BW20 and cbrn handling */\n+\t\tif (txmode == MT7925_TX_RATE_MODE_CCK ||\n+\t\t    txmode == MT7925_TX_RATE_MODE_OFDM)\n+\t\t\tseq_printf(s, \"%s, \", mt7925_tx_rate_bw_str[0]);\n+\t\telse if (i \u003e cbrn)\n+\t\t\tseq_printf(s, \"%s, \",\n+\t\t\t\t   fcap \u003c 5 ?\n+\t\t\t\t   (fcap \u003e MT7925_BW_20 ?\n+\t\t\t\t    mt7925_tx_rate_bw_str[fcap - 1] :\n+\t\t\t\t    mt7925_tx_rate_bw_str[fcap]) :\n+\t\t\t\t   mt7925_tx_rate_bw_str[5]);\n+\t\telse\n+\t\t\tseq_printf(s, \"%s, \",\n+\t\t\t\t   fcap \u003c 5 ? mt7925_tx_rate_bw_str[fcap] :\n+\t\t\t\t   mt7925_tx_rate_bw_str[5]);\n+\n+\t\t/* GI / preamble */\n+\t\tif (txmode == MT7925_TX_RATE_MODE_CCK)\n+\t\t\tseq_printf(s, \"%s, \", rate \u003c 4 ? \"LP\" : \"SP\");\n+\t\telse if (txmode == MT7925_TX_RATE_MODE_OFDM)\n+\t\t\t; /* OFDM has no GI/preamble to print */\n+\t\telse if (txmode == MT7925_TX_RATE_MODE_HTMIX ||\n+\t\t\t txmode == MT7925_TX_RATE_MODE_HTGF ||\n+\t\t\t txmode == MT7925_TX_RATE_MODE_VHT ||\n+\t\t\t txmode == MT7925_TX_RATE_MODE_PLR)\n+\t\t\tseq_printf(s, \"%s, \", sgi == 0 ? \"LGI\" : \"SGI\");\n+\t\telse\n+\t\t\tseq_printf(s, \"%s, \",\n+\t\t\t\t   sgi == 0 ? \"SGI\" :\n+\t\t\t\t   (sgi == 1 ? \"MGI\" : \"LGI\"));\n+\n+\t\t/* mode, DCM, 106t, STBC, coding */\n+\t\tseq_printf(s, \"%s%s%s%s%s\\n\",\n+\t\t\t   mode_str,\n+\t\t\t   dcm ? \", DCM\" : \"\", ersu106t ? \", 106t\" : \"\",\n+\t\t\t   stbc ? \", STBC, \" : \", \",\n+\t\t\t   (ldpc == 0 ||\n+\t\t\t    txmode == MT7925_TX_RATE_MODE_CCK ||\n+\t\t\t    txmode == MT7925_TX_RATE_MODE_OFDM) ?\n+\t\t\t   \"BCC\" : \"LDPC\");\n+\t}\n+}\n+\n+static int mt792x_stats(struct seq_file *s, void *data)\n+{\n+\ts8 rssi = 0;\n+\tu8 band_idx = 0;\n+\tbool wtbl_rate_valid = false;\n+\tint ret = 0;\n+\tint i = 0;\n+\tu32 stats_value = 0;\n+\tu32 tx_link_speed_kbps = 0;\n+\tu64 tx_total = 0;\n+\tu64 tx_fail = 0;\n+\tu64 tx_per = 0;\n+\tu32 tx_per_rem = 0;\n+\t/* UNI_CMD_MIB_DATA */\n+\tu64 mib_values[ARRAY_SIZE(stats_query_mib_counters)];\n+\tstruct mt792x_dev *dev = dev_get_drvdata(s-\u003eprivate);\n+\tstruct mt7925_wtbl_rate wtbl_rate;\n+\tstruct mt7925_sta_stats *stats = NULL;\n+\n+\tband_idx = dev-\u003emphy.band_idx;\n+\n+\t stats = kzalloc(sizeof(*stats), GFP_KERNEL);\n+\tif (!stats)\n+\t\treturn -ENOMEM;\n+\n+\t/* Get STA_STATS */\n+\tmt792x_mutex_acquire(dev);\n+\tret = mt792x_mcu_get_stat(dev, stats);\n+\tmt792x_mutex_release(dev);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\t/* Get MIB_STATS */\n+\tmt792x_mutex_acquire(dev);\n+\tret = mt7925_mcu_get_mib_info(dev, band_idx, stats_query_mib_counters,\n+\t\t\t\t      ARRAY_SIZE(stats_query_mib_counters), mib_values);\n+\tmt792x_mutex_release(dev);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\t/* Get Link_Speed and RSSI */\n+\tmt792x_mutex_acquire(dev);\n+\tret = mt7925_mcu_get_link_quality(dev, stats-\u003ebss_idx, \u0026rssi, \u0026tx_link_speed_kbps);\n+\tmt792x_mutex_release(dev);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\t/* Get the firmware auto-rate table (best effort; do not abort the\n+\t * whole stats dump if the WTBL query is not available).\n+\t */\n+\tmt792x_mutex_acquire(dev);\n+\tif (!mt792x_mcu_get_wtbl_rate(dev, stats-\u003ewtbl_idx, \u0026wtbl_rate))\n+\t\twtbl_rate_valid = true;\n+\tmt792x_mutex_release(dev);\n+\n+\t/* Print STA_STATS info */\n+\tseq_printf(s, \"(STA) connected AP MAC Address = %pM\\n\", stats-\u003emac_addr);\n+\tseq_printf(s, \"%-22s  BssIdx = [%u]\\n\", \" \", stats-\u003ebss_idx);\n+\tseq_printf(s, \"%-22s  StaRecIdx = [%u]\\n\",\n+\t\t   \" \", stats-\u003esta_idx);\n+\tseq_printf(s, \"%-22s  RSSI = %d\\n\", \" \", rssi);\n+\tseq_printf(s, \"%-22s  Link_Speed = %u (kbit/s)\\n\", \" \", tx_link_speed_kbps);\n+\n+\tseq_printf(s, \"%-22s  temperature = %u\\n\",\n+\t\t   \" \", stats-\u003etemperature);\n+\n+\tstats_value = le32_to_cpu(stats-\u003etransmit_count) - le32_to_cpu(stats-\u003etransmit_fail_count);\n+\tseq_printf(s, \"%-22s  Tx success = %u\\n\",\n+\t\t   \" \", stats_value);\n+\n+\tstats_value = le32_to_cpu(stats-\u003etx_fail_count) - le32_to_cpu(stats-\u003etx_life_timeout_count);\n+\tseq_printf(s, \"%-22s  Tx fail to Rcv ACK after retry = %u\\n\",\n+\t\t   \" \", stats_value);\n+\n+\tseq_printf(s, \"%-22s  Rx Mpdu = %u\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003emib[band_idx].rx_mpdu_cnt));\n+\n+\tseq_printf(s, \"%-22s  Rx Fcs Error = %u (MPDU)\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003emib[band_idx].fcs_error));\n+\tseq_printf(s, \"%-22s  Rx FIFO full = %u (MPDU)\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003emib[band_idx].rx_fifo_full));\n+\n+\tfor (i = 0; i \u003c STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM; i++)\n+\t\tseq_printf(s, \"%-22s  TRX_AGG_RANGE[%d] = %u (PPDU)\\n\",\n+\t\t\t   \" \", i, le32_to_cpu(stats-\u003emib[band_idx].tx_range_ampdu_cnt[i]));\n+\n+\tseq_printf(s, \"%-22s  MPDUs in AMPDUs transmitted = %u (MPDU)\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003emib[band_idx].ampdu_tx_sf_cnt));\n+\tseq_printf(s, \"%-22s  MPDUs in AMPDUs transmitted with ACK reply = %u (MPDU)\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003emib[band_idx].ampdu_tx_ack_sf_cnt));\n+\tseq_printf(s, \"%-22s  rate1_tx_cnt = %u\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003erate1_tx_cnt));\n+\tseq_printf(s, \"%-22s  rate1_fail_cnt = %u\\n\",\n+\t\t   \" \", le32_to_cpu(stats-\u003erate1_fail_cnt));\n+\tseq_printf(s, \"%-22s  train_up = %u\\n\",\n+\t\t   \" \", le16_to_cpu(stats-\u003etrain_up));\n+\tseq_printf(s, \"%-22s  train_down = %u\\n\",\n+\t\t   \" \", le16_to_cpu(stats-\u003etrain_down));\n+\tseq_printf(s, \"%-22s  is_force_tx_stream = %u\\n\",\n+\t\t   \" \", stats-\u003eis_force_tx_stream);\n+\tseq_printf(s, \"%-22s  is_force_se_off = %u\\n\",\n+\t\t   \" \", stats-\u003eis_force_se_off);\n+\tseq_printf(s, \"%-22s  max_ampdu_factor = %u\\n\",\n+\t\t   \" \", stats-\u003emax_ampdu_factor);\n+\tseq_printf(s, \"%-22s  tx_rate_up_penalty = %u\\n\",\n+\t\t   \" \", stats-\u003etx_rate_up_penalty);\n+\tseq_printf(s, \"%-22s  low_traffic_mode = %u\\n\",\n+\t\t   \" \", stats-\u003elow_traffic_mode);\n+\tseq_printf(s, \"%-22s  low_traffic_count = %u\\n\",\n+\t\t   \" \", stats-\u003elow_traffic_count);\n+\tseq_printf(s, \"%-22s  low_traffic_dashboard = %u\\n\",\n+\t\t   \" \", stats-\u003elow_traffic_dashboard);\n+\tseq_printf(s, \"%-22s  dynamic_sgi_state = %u\\n\",\n+\t\t   \" \", stats-\u003edynamic_sgi_state);\n+\tseq_printf(s, \"%-22s  dynamic_sgi_score = %u\\n\",\n+\t\t   \" \", stats-\u003edynamic_sgi_score);\n+\tseq_printf(s, \"%-22s  dynamic_bw_state = %u\\n\",\n+\t\t   \" \", stats-\u003edynamic_bw_state);\n+\tseq_printf(s, \"%-22s  dynamic_gband_256qam_state = %u\\n\",\n+\t\t   \" \", stats-\u003edynamic_gband_256qam_state);\n+\tseq_printf(s, \"%-22s  vht_non_sp_rate_state = %u\\n\",\n+\t\t   \" \", stats-\u003evht_non_sp_rate_state);\n+\n+\t/* Decode the TX Vector BBP latch to show the current TX MCS rate */\n+\tif (band_idx \u003c MT7925_STA_STATS_BAND_NUM)\n+\t\tmt7925_print_last_tx_rate(s, stats-\u003etx_vector[band_idx].txv);\n+\n+\t/* Dump the whole firmware auto-rate table and mark the entry currently */\n+\tif (wtbl_rate_valid)\n+\t\tmt7925_dump_auto_rate_table(s, \u0026wtbl_rate);\n+\n+\tseq_puts(s, \"\\nmib state:\\n\");\n+\t/* ===Rx Related Counters=== */\n+\tseq_puts(s, \"=== Rx Related Counters ===\\n\");\n+\tseq_printf(s, \"%-22s  Rx with CRC = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_FCS_ERR]);\n+\tseq_printf(s, \"%-22s  Rx drop due to out of resource = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_FIFO_OVERFLOW]);\n+\tseq_printf(s, \"%-22s  Rx Mpdu = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_MPDU]);\n+\tseq_printf(s, \"%-22s  Rx AMpdu = %llu (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_AMPDU_RX_COUNT]);\n+\tseq_printf(s, \"%-22s  Rx PF Drop = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_PF_DROP]);\n+\tseq_printf(s, \"%-22s  Rx Len Mismatch = %llu (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_LEN_MISMATCH]);\n+\n+\t/* ===Phy/Timing Related Counters=== */\n+\tseq_puts(s, \"\\n=== Phy/Timing Related Counters ===\\n\");\n+\tseq_printf(s, \"%-22s  ChannelIdleCnt = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_CHANNEL_IDLE]);\n+\tseq_printf(s, \"%-22s  CCA_NAV_Tx_Time = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_CCA_NAV_TX_TIME]);\n+\tseq_printf(s, \"%-22s  Rx_MDRDY_CNT = %llu (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_MDRDY]);\n+\tseq_printf(s, \"%-22s  CCK_MDRDY = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_CCK_MDRDY_TIME]);\n+\tseq_printf(s, \"%-22s  OFDM_MDRDY = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME]);\n+\tseq_printf(s, \"%-22s  OFDM_GREEN_MDRDY = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME]);\n+\tseq_printf(s, \"%-22s  Prim CCA Time = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_P_CCA_TIME]);\n+\tseq_printf(s, \"%-22s  Sec CCA Time = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_S_CCA_TIME]);\n+\tseq_printf(s, \"%-22s  Prim ED Time = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_P_ED_TIME]);\n+\n+\t/* ===Tx Related Counters(Generic)=== */\n+\tseq_puts(s, \"\\n=== Tx Related Counters(Generic) ===\\n\");\n+\tseq_printf(s, \"%-22s  BeaconTxCnt = %llu\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BCN_TX]);\n+\tseq_printf(s, \"%-22s  Tx 40MHz Cnt = %llu  (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_TX_BW_40MHZ]);\n+\tseq_printf(s, \"%-22s  Tx 80MHz Cnt = %llu  (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_TX_BW_80MHZ]);\n+\tseq_printf(s, \"%-22s  Tx 160MHz Cnt = %llu  (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_TX_BW_160MHZ]);\n+\n+\t/* ===BSSID[0] Related Counters=== */\n+\tseq_puts(s, \"\\n=== BSSID[0] Related Counters ===\\n\");\n+\tseq_printf(s, \"%-22s  BA Miss Cnt = %llu  (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_BA_MISS]);\n+\tseq_printf(s, \"%-22s  RTS Tx Cnt = %llu  (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_RTS_TX_CNT]);\n+\tseq_printf(s, \"%-22s  Frame Retry Cnt = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY]);\n+\tseq_printf(s, \"%-22s  Frame Retry 2 Cnt = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY_2]);\n+\tseq_printf(s, \"%-22s  RTS Retry Cnt = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_RTS_RETRY]);\n+\tseq_printf(s, \"%-22s  Ack Failed Cnt = %llu (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_BSS0_ACK_FAIL]);\n+\n+\t/* ===AMPDU Related Counters=== */\n+\tseq_puts(s, \"\\n=== AMPDU Related Counters ===\\n\");\n+\tseq_printf(s, \"%-22s  Tx AMPDU_Pkt_Cnt = %llu  (PPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_AMPDU]);\n+\tseq_printf(s, \"%-22s  Tx AMPDU_MPDU_Pkt_Cnt = %llu  (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_AMPDU_MPDU]);\n+\tseq_printf(s, \"%-22s  AMPDU Tx success = %llu (MPDU)\\n\",\n+\t\t   \" \", mib_values[QUERY_MIB_CNT_AMPDU_ACKED]);\n+\n+\ttx_total = mib_values[QUERY_MIB_CNT_AMPDU_MPDU];\n+\ttx_fail =  mib_values[QUERY_MIB_CNT_AMPDU_MPDU] - mib_values[QUERY_MIB_CNT_AMPDU_ACKED];\n+\ttx_per = tx_total == 0 ? 0 : div64_u64(1000 * tx_fail, tx_total);\n+\tseq_printf(s, \"%-22s  AMPDU Tx fail count   = %llu  (MPDU), PER=%llu.%1llu%%\\n\",\n+\t\t   \" \",\n+\t\t   tx_fail,\n+\t\t   div_u64_rem(tx_per, 10, \u0026tx_per_rem), (u64)tx_per_rem);\n+\n+out:\n+\tkfree(stats);\n+\treturn ret;\n+}\n+\n int mt7925_init_debugfs(struct mt792x_dev *dev)\n {\n \tstruct dentry *dir;\n@@ -313,5 +656,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)\n \t\t\t\t    mt792x_pm_stats);\n \tdebugfs_create_file(\"deep-sleep\", 0600, dir, dev, \u0026fops_ds);\n \n+\tdebugfs_create_devm_seqfile(dev-\u003emt76.dev, \"stats\", dir,\n+\t\t\t\t    mt792x_stats);\n+\n \treturn 0;\n }\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.c b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c\nnew file mode 100644\nindex 0000000000000..9a47953062bc3\n--- /dev/null\n+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c\n@@ -0,0 +1,585 @@\n+// SPDX-License-Identifier: BSD-3-Clause-Clear\n+/* Copyright (C) 2026 MediaTek Inc. */\n+\n+#include \u003clinux/firmware.h\u003e\n+#include \u003clinux/fs.h\u003e\n+#include \"mt7925.h\"\n+#include \"mcu.h\"\n+#include \"mac.h\"\n+#include \"stats.h\"\n+\n+#define MT7925_STA_STATS_VERSION 1\n+#define MT7925_STA_STATS_VALID 1U\n+\n+#define MT7925_MIB_MAX_CNT\t\t\t32\n+#define MT7925_MIB_CNT_MAX_NUM\t\t512  /* firmware UNI_CMD_MIB_CNT_MAX_NUM upper bound */\n+\n+#define MT7925_LQ_BAND_NUM\t4\t\t\t/* Link quality band */\n+\n+const char * const mt7925_tx_mode_str[] = {\n+\t\"CCK\", \"OFDM\", \"MM\", \"GF\", \"VHT\", \"PLR\",\n+\t\"PLRP\", \"ALR\", \"HE_SU\", \"HE_ER\", \"HE_TRIG\", \"HE_MU\",\n+\t\"EHT_ER\", \"TOF\", \"EHT_TRIG\", \"EHT_MU\"\n+};\n+\n+const char * const mt7925_tx_rate_cck_str[] = {\n+\t\"1M\", \"2M\", \"5.5M\", \"11M\", \"N/A\"\n+};\n+\n+const char * const mt7925_tx_rate_bw_str[] = {\n+\t\"BW20\", \"BW40\", \"BW80\", \"BW160/BW8080\", \"BW320\", \"N/A\"\n+};\n+\n+struct mt7925_sta_stats_req {\n+\tu8 rsv[4];\n+\t__le16 tag;\n+\t__le16 len;\n+\tu8 sta_rec_idx;\n+\tu8 rsv1;\n+\tu8 lls_read_clear;\n+\tu8 reset_counter;\n+} __packed;\n+\n+struct mt7925_sta_stats_event {\n+\tu8 rsv[4];\n+\t__le16 tag;\n+\t__le16 len;\n+\tstruct mt7925_sta_stats stats;\n+} __packed;\n+\n+/* WTBL auto-rate request TLV, mirror Gen4m struct UNI_CMD_STAT_WTBL.\n+ * The RA tag reuses UNI_CMD_ID_STATISTICS_WTBL (10).\n+ */\n+struct mt7925_wtbl_rate_req {\n+\tu8 rsv[4];\n+\t__le16 tag;\n+\t__le16 len;\n+\t__le16 wlan_idx;\n+\t__le16 rsv1;\n+} __packed;\n+\n+/* WTBL auto-rate event TLV. Firmware answers cmd tag 10 with event tag 12. */\n+struct mt7925_wtbl_rate_event {\n+\tu8 rsv[4];\n+\tstruct mt7925_wtbl_rate rate;\n+} __packed;\n+\n+/* fixed field, mirror UNI_(CMD|EVENT)_MIB_INFO */\n+struct mt7925_mib_hdr {\n+\tu8 band_idx;\n+\tu8 rsv[3];\n+} __packed;\n+\n+/* One requested/returned MIB counter, mirror UNI_(CMD|EVENT)_MIB_DATA_T. */\n+struct mt7925_mib_data {\n+\t__le16 tag;\t/* UNI_(CMD|EVENT)_MIB_DATA = 0 */\n+\t__le16 len;\n+\t__le32 counter;\t/* MIB counter ID (enum mt7925_mib_counter) */\n+\t__le64 data;\t/* only valid on event; ignored on cmd */\n+} __packed;\n+\n+/* per-band link quality entry, mirror UNI_LINK_QUALITY */\n+struct mt7925_link_quality {\n+\ts8 rssi;\t\t/* moving-average beacon RSSI of connected AP */\n+\ts8 link_quality;\t/* always 0 */\n+\t__le16 link_speed;\t/* TX rate1, unit 500 Kb/s */\n+\tu8 medium_busy_pct;\t/* always 0 */\n+\tu8 is_ready;\t\t/* 0: fields invalid, 1: valid */\n+\tu8 rsv[2];\n+} __packed;\n+\n+/* request TLV, mirror UNI_CMD_LINK_QUALITY (header + tag/len only) */\n+struct mt7925_lq_req {\n+\tu8 rsv[4];\t\t/* UNI_CMD_GET_STATISTICS fixed field */\n+\t__le16 tag;\t\t/* UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1 */\n+\t__le16 len;\n+} __packed;\n+\n+/* event TLV, mirror UNI_EVENT_LINK_QUALITY */\n+struct mt7925_lq_event {\n+\tu8 rsv[4];\t\t/* UNI_EVENT_STATISTICS fixed field */\n+\t__le16 tag;\t\t/* UNI_EVENT_ID_STATISTICS_LINK_QUALITY = 1 */\n+\t__le16 len;\n+\tstruct mt7925_link_quality lq[MT7925_LQ_BAND_NUM];\n+} __packed;\n+\n+static void\n+mt7925_get_ap_sta_rec_idx_iter(void *data, u8 *mac,\n+\t\t\t       struct ieee80211_vif *vif)\n+{\n+\tstruct mt7925_sta_rec_lookup *lookup = data;\n+\tstruct mt792x_vif *mvif;\n+\tstruct mt792x_sta *msta;\n+\n+\tif (lookup-\u003esta_rec_idx \u003e= 0)\n+\t\treturn;\n+\n+\tif (vif-\u003etype != NL80211_IFTYPE_STATION || !vif-\u003ecfg.assoc)\n+\t\treturn;\n+\n+\tmvif = (struct mt792x_vif *)vif-\u003edrv_priv;\n+\tmsta = mvif-\u003ewep_sta;\n+\tif (!msta)\n+\t\treturn;\n+\n+\tif (!msta-\u003edeflink.wcid.sta)\n+\t\treturn;\n+\n+\tlookup-\u003esta_rec_idx = msta-\u003edeflink.wcid.idx;\n+}\n+\n+int\n+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx)\n+{\n+\tstruct mt7925_sta_rec_lookup lookup = {\n+\t\t.sta_rec_idx = -1,\n+\t};\n+\n+\tlockdep_assert_held(\u0026dev-\u003emt76.mutex);\n+\n+\tieee80211_iterate_active_interfaces(dev-\u003emphy.hw,\n+\t\t\t\t\t    IEEE80211_IFACE_ITER_RESUME_ALL,\n+\t\t\t\t\t    mt7925_get_ap_sta_rec_idx_iter,\n+\t\t\t\t\t    \u0026lookup);\n+\n+\tif (lookup.sta_rec_idx \u003c 0)\n+\t\treturn -ENOTCONN;\n+\n+\tif (lookup.sta_rec_idx \u003e U8_MAX)\n+\t\treturn -ERANGE;\n+\n+\t*sta_rec_idx = lookup.sta_rec_idx;\n+\n+\treturn 0;\n+}\n+\n+/* Decode the TX Vector BBP latch and print the last TX rate */\n+void\n+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3])\n+{\n+\tconst char *mode_str = \"N/A\";\n+\tu8 rate, txmode, frmode, sgi, ldpc, nsts, stbc, spe, dcm, ersu106t;\n+\ts8 txpwr, pos_txpwr;\n+\tu32 txv[3];\n+\n+\ttxv[0] = le32_to_cpu(le_txv[0]);\n+\ttxv[1] = le32_to_cpu(le_txv[1]);\n+\ttxv[2] = le32_to_cpu(le_txv[2]);\n+\n+\tif (txv[0] == 0xffffffff) {\n+\t\tseq_printf(s, \"%-22s  %s = %s\\n\", \" \", \"Last TX Rate\", \"N/A\");\n+\t\tseq_printf(s, \"%-22s  %s = %s\\n\",\n+\t\t\t   \" \", \"Chip Out TX Power\", \"N/A\");\n+\t\treturn;\n+\t}\n+\n+\trate = MT7925_TXV_GET_TX_RATE(txv);\n+\ttxmode = MT7925_TXV_GET_TX_MODE(txv);\n+\tfrmode = MT7925_TXV_GET_TX_FRMODE(txv);\n+\tnsts = MT7925_TXV_GET_TX_NSTS(txv) + 1;\n+\tsgi = MT7925_TXV_GET_TX_SGI(txv);\n+\tldpc = MT7925_TXV_GET_TX_LDPC(txv);\n+\tstbc = MT7925_TXV_GET_TX_STBC(txv);\n+\ttxpwr = MT7925_TXV_GET_TX_PWR(txv);\n+\tspe = MT7925_TXV_GET_TX_SPE_IDX(txv);\n+\tdcm = MT7925_TXV_GET_TX_DCM(txv);\n+\tersu106t = MT7925_TXV_GET_TX_106T(txv);\n+\n+\tif (dcm)\n+\t\trate = MT7925_TXV_RATE_UNMASK_DCM(rate);\n+\tif (ersu106t)\n+\t\trate = MT7925_TXV_RATE_UNMASK_106T(rate);\n+\n+\tif (txmode \u003c ARRAY_SIZE(mt7925_tx_mode_str))\n+\t\tmode_str = mt7925_tx_mode_str[txmode];\n+\n+\tseq_printf(s, \"%-22s  %s = \", \" \", \"Last TX Rate\");\n+\n+\t/* rate/MCS field */\n+\tif (txmode == MT7925_TX_RATE_MODE_CCK)\n+\t\tseq_printf(s, \"%s, \",\n+\t\t\t   rate \u003c 4 ? mt7925_tx_rate_cck_str[rate] :\n+\t\t\t   (((rate \u003e= 5) \u0026\u0026 (rate \u003c= 7)) ?\n+\t\t\t    mt7925_tx_rate_cck_str[rate - 4] :\n+\t\t\t    mt7925_tx_rate_cck_str[4]));\n+\telse if (txmode == MT7925_TX_RATE_MODE_OFDM)\n+\t\tseq_printf(s, \"%s, \", mt7925_hw_rate_ofdm_str(rate));\n+\telse if (txmode == MT7925_TX_RATE_MODE_HTMIX ||\n+\t\t txmode == MT7925_TX_RATE_MODE_HTGF)\n+\t\tseq_printf(s, \"MCS%d, \", rate);\n+\telse\n+\t\tseq_printf(s, \"%s%d_MCS%d, \",\n+\t\t\t   stbc ? \"NSTS\" : \"NSS\", nsts, rate);\n+\n+\t/* bandwidth */\n+\tseq_printf(s, \"%s, \",\n+\t\t   frmode \u003c 5 ? mt7925_tx_rate_bw_str[frmode] :\n+\t\t   mt7925_tx_rate_bw_str[5]);\n+\n+\t/* GI / preamble */\n+\tif (txmode == MT7925_TX_RATE_MODE_CCK)\n+\t\tseq_printf(s, \"%s, \", rate \u003c 4 ? \"LP\" : \"SP\");\n+\telse if (txmode == MT7925_TX_RATE_MODE_OFDM)\n+\t\t; /* OFDM has no GI/preamble to print */\n+\telse if (txmode == MT7925_TX_RATE_MODE_HTMIX ||\n+\t\t txmode == MT7925_TX_RATE_MODE_HTGF ||\n+\t\t txmode == MT7925_TX_RATE_MODE_VHT ||\n+\t\t txmode == MT7925_TX_RATE_MODE_PLR)\n+\t\tseq_printf(s, \"%s, \", sgi == 0 ? \"LGI\" : \"SGI\");\n+\telse\n+\t\tseq_printf(s, \"%s, \",\n+\t\t\t   sgi == 0 ? \"SGI\" : (sgi == 1 ? \"MGI\" : \"LGI\"));\n+\n+\t/* mode, DCM, 106t, STBC, coding, SPE */\n+\tseq_printf(s, \"%s%s%s%s%s%s%d\\n\",\n+\t\t   mode_str,\n+\t\t   dcm ? \", DCM\" : \"\", ersu106t ? \", 106t\" : \"\",\n+\t\t   stbc ? \", STBC, \" : \", \", ldpc == 0 ? \"BCC\" : \"LDPC\",\n+\t\t   \", SPE\", spe);\n+\n+\t/* chip out TX power (unit: 0.5 dBm) */\n+\tpos_txpwr = (txpwr \u003c 0) ? (~txpwr + 1) : txpwr;\n+\tseq_printf(s, \"%-22s  %s = %c%d.%1d dBm\\n\",\n+\t\t   \" \", \"Chip Out TX Power\",\n+\t\t   (txpwr \u003c 0) ? '-' : '+',\n+\t\t   (pos_txpwr / 2), 5 * (pos_txpwr % 2));\n+}\n+\n+/* Guard interval used for the auto-rate entries, mirror Gen4m\n+ * connac3x_get_sgi_info_from_fw(): pick the per-mode/per-BW flag.\n+ */\n+u8\n+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap)\n+{\n+\tbool he = (txmode == MT7925_TX_RATE_MODE_HE_SU ||\n+\t\t   txmode == MT7925_TX_RATE_MODE_HE_ER ||\n+\t\t   txmode == MT7925_TX_RATE_MODE_HE_TRIG ||\n+\t\t   txmode == MT7925_TX_RATE_MODE_HE_MU);\n+\tbool eht = (txmode == MT7925_TX_RATE_MODE_EHT_ER ||\n+\t\t    txmode == MT7925_TX_RATE_MODE_EHT_TRIG ||\n+\t\t    txmode == MT7925_TX_RATE_MODE_EHT_MU);\n+\n+\tif (eht)\n+\t\treturn wr-\u003egi_eht;\n+\n+\tswitch (fcap) {\n+\tcase MT7925_BW_20:\n+\t\treturn he ? wr-\u003eg2_he : wr-\u003eg2;\n+\tcase MT7925_BW_40:\n+\t\treturn he ? wr-\u003eg4_he : wr-\u003eg4;\n+\tcase MT7925_BW_80:\n+\t\treturn he ? wr-\u003eg8_he : wr-\u003eg8;\n+\tcase MT7925_BW_160:\n+\t\treturn he ? wr-\u003eg16_he : wr-\u003eg16;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+/* Coding used for the auto-rate entries, mirror Gen4m\n+ * connac3x_wtbl_get_ldpc_info_from_fw(): 0 = BCC, 1 = LDPC.\n+ */\n+u8\n+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode)\n+{\n+\tswitch (txmode) {\n+\tcase MT7925_TX_RATE_MODE_HTMIX:\n+\tcase MT7925_TX_RATE_MODE_HTGF:\n+\t\treturn wr-\u003eht_ldpc;\n+\tcase MT7925_TX_RATE_MODE_VHT:\n+\t\treturn wr-\u003evht_ldpc;\n+\tcase MT7925_TX_RATE_MODE_HE_SU:\n+\tcase MT7925_TX_RATE_MODE_HE_ER:\n+\tcase MT7925_TX_RATE_MODE_HE_TRIG:\n+\tcase MT7925_TX_RATE_MODE_HE_MU:\n+\t\treturn wr-\u003ehe_ldpc;\n+\tcase MT7925_TX_RATE_MODE_EHT_ER:\n+\tcase MT7925_TX_RATE_MODE_EHT_TRIG:\n+\tcase MT7925_TX_RATE_MODE_EHT_MU:\n+\t\treturn wr-\u003eeht_ldpc;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+int mt792x_mcu_get_stat(struct mt792x_dev *dev,\n+\t\t\tstruct mt7925_sta_stats *stats)\n+{\n+\tu16 len = 0;\n+\tint ret = 0;\n+\tstruct mt7925_sta_stats_event *event = NULL;\n+\tstruct sk_buff *skb = NULL;\n+\tstruct mt7925_sta_stats_req req = {\n+\t\t.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_STA),\n+\t\t.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),\n+\t\t.lls_read_clear = false,\n+\t\t.reset_counter = false,\n+\t};\n+\n+\tret = mt7925_get_ap_sta_rec_idx(dev, \u0026req.sta_rec_idx);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!stats)\n+\t\treturn -EINVAL;\n+\n+\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\n+\t\t\t\t\tMCU_UNI_QUERY(GET_STAT_INFO),\n+\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tevent = (struct mt7925_sta_stats_event *)skb-\u003edata;\n+\n+\tif (le16_to_cpu(event-\u003etag) != UNI_CMD_ID_STATISTICS_STA) {\n+\t\tret = -EPROTO;\n+\t\tgoto out;\n+\t}\n+\n+\tlen = le16_to_cpu(event-\u003elen);\n+\tif (len \u003c sizeof(*event) - sizeof(event-\u003ersv) ||\n+\t    len \u003e skb-\u003elen - sizeof(event-\u003ersv) || !IS_ALIGNED(len, 4)) {\n+\t\tret = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\tif (event-\u003estats.version != MT7925_STA_STATS_VERSION) {\n+\t\tret = -EPROTO;\n+\t\tgoto out;\n+\t}\n+\n+\tif (!(le32_to_cpu(event-\u003estats.flags) \u0026 MT7925_STA_STATS_VALID)) {\n+\t\tret = -ENOENT;\n+\t\tgoto out;\n+\t}\n+\n+\tmemcpy(stats, \u0026event-\u003estats, sizeof(*stats));\n+\n+out:\n+\tdev_kfree_skb(skb);\n+\treturn ret;\n+}\n+\n+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,\n+\t\t\t    const u32 *counters, u8 counter_num,\n+\t\t\t    u64 *values)\n+{\n+\tu8 *req;\n+\tu8 *tlv, *tlv_end;\n+\tu16 req_len;\n+\tint i, ret;\n+\tstruct mt7925_mib_hdr *req_hdr;\n+\tstruct mt7925_mib_data *req_tlv;\n+\tstruct sk_buff *skb;\n+\n+\tif (!counters || !values || !counter_num ||\n+\t    counter_num \u003e MT7925_MIB_MAX_CNT)\n+\t\treturn -EINVAL;\n+\n+\t/* clear output; counters not returned by firmware stay 0 */\n+\tmemset(values, 0, counter_num * sizeof(*values));\n+\n+\treq_len = sizeof(struct mt7925_mib_hdr) +\n+\t\t  counter_num * sizeof(struct mt7925_mib_data);\n+\n+\treq = kzalloc(req_len, GFP_KERNEL);\n+\tif (!req)\n+\t\treturn -ENOMEM;\n+\n+\treq_hdr = (struct mt7925_mib_hdr *)req;\n+\treq_hdr-\u003eband_idx = band_idx;\n+\n+\treq_tlv = (struct mt7925_mib_data *)(req + sizeof(*req_hdr));\n+\tfor (i = 0; i \u003c counter_num; i++) {\n+\t\treq_tlv[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);\n+\t\treq_tlv[i].len = cpu_to_le16(sizeof(struct mt7925_mib_data));\n+\t\treq_tlv[i].counter = cpu_to_le32(counters[i]);\n+\t}\n+\n+\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\n+\t\t\t\t\tMCU_UNI_QUERY(GET_MIB_INFO), req,\n+\t\t\t\t\treq_len, true, \u0026skb);\n+\tkfree(req);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (skb-\u003elen \u003c sizeof(struct mt7925_mib_hdr)) {\n+\t\tret = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\t/* walk the returned TLVs, match each counter ID to the request */\n+\ttlv = skb-\u003edata + sizeof(struct mt7925_mib_hdr);\n+\ttlv_end = skb-\u003edata + skb-\u003elen;\n+\n+\twhile (tlv + sizeof(struct mt7925_mib_data) \u003c= tlv_end) {\n+\t\tstruct mt7925_mib_data *evt_tlv =\n+\t\t\t(struct mt7925_mib_data *)tlv;\n+\t\tu16 tag = le16_to_cpu(evt_tlv-\u003etag);\n+\t\tu16 len = le16_to_cpu(evt_tlv-\u003elen);\n+\t\tu32 counter;\n+\n+\t\tif (len \u003c sizeof(struct mt7925_mib_data) ||\n+\t\t    tlv + len \u003e tlv_end)\n+\t\t\tbreak;\n+\n+\t\tif (tag == UNI_CMD_MIB_DATA) {\n+\t\t\tcounter = le32_to_cpu(evt_tlv-\u003ecounter);\n+\n+\t\t\tif (counter \u003c MT7925_MIB_CNT_MAX_NUM) {\n+\t\t\t\tfor (i = 0; i \u003c counter_num; i++) {\n+\t\t\t\t\tif (counters[i] == counter) {\n+\t\t\t\t\t\tvalues[i] =\n+\t\t\t\t\t\t  le64_to_cpu(evt_tlv-\u003edata);\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\ttlv += len;\n+\t}\n+\n+\tret = 0;\n+\n+out:\n+\tdev_kfree_skb(skb);\n+\n+\treturn ret;\n+}\n+\n+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx)\n+{\n+\tchar * const ofdm_str[] = {\n+\t\t\"6M\", \"9M\", \"12M\", \"18M\", \"24M\", \"36M\", \"48M\", \"54M\", \"N/A\"\n+\t};\n+\n+\tswitch (ofdm_idx) {\n+\tcase 11: return ofdm_str[0];\t/* 6M */\n+\tcase 15: return ofdm_str[1];\t/* 9M */\n+\tcase 10: return ofdm_str[2];\t/* 12M */\n+\tcase 14: return ofdm_str[3];\t/* 18M */\n+\tcase 9:  return ofdm_str[4];\t/* 24M */\n+\tcase 13: return ofdm_str[5];\t/* 36M */\n+\tcase 8:  return ofdm_str[6];\t/* 48M */\n+\tcase 12: return ofdm_str[7];\t/* 54M */\n+\tdefault: return ofdm_str[8];\n+\t}\n+}\n+\n+/* mt792x_mcu_get_wtbl_rate - fetch the firmware auto-rate table for a WCID\n+ * @dev:\tdevice\n+ * @wlan_idx:\tHW WTBL / WCID index (see mt7925_sta_stats.wtbl_idx)\n+ * @rate:\tout, filled with the auto-rate table snapshot\n+ *\n+ * Mirror of Gen4m wlanoidGetStatWtbl(): send UNI_CMD_GET_STAT_INFO with the\n+ * WTBL rate TLV (cmd tag 10) and parse the reply (event tag 12) into @rate.\n+ *\n+ * Return: 0 on success, negative errno otherwise.\n+ */\n+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,\n+\t\t\t     struct mt7925_wtbl_rate *rate)\n+{\n+\tint ret;\n+\tstruct mt7925_wtbl_rate_event *event = NULL;\n+\tstruct sk_buff *skb = NULL;\n+\tstruct mt7925_wtbl_rate_req req = {\n+\t\t.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_WTBL),\n+\t\t.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),\n+\t\t.wlan_idx = cpu_to_le16(wlan_idx),\n+\t};\n+\n+\tif (!rate)\n+\t\treturn -EINVAL;\n+\n+\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\n+\t\t\t\t\tMCU_UNI_QUERY(GET_STAT_INFO),\n+\t\t\t\t\t\u0026req, sizeof(req), true, \u0026skb);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (skb-\u003elen \u003c sizeof(*event)) {\n+\t\tret = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\tevent = (struct mt7925_wtbl_rate_event *)skb-\u003edata;\n+\n+\tif (le16_to_cpu(event-\u003erate.tag) != MT7925_UNI_EVENT_STATISTICS_WTBL) {\n+\t\tret = -EPROTO;\n+\t\tgoto out;\n+\t}\n+\n+\tmemcpy(rate, \u0026event-\u003erate, sizeof(*rate));\n+\tret = 0;\n+\n+out:\n+\tdev_kfree_skb(skb);\n+\treturn ret;\n+}\n+\n+/**\n+ * mt7925_mcu_get_link_quality - query RSSI and TX link speed for a band\n+ * @dev:\tdevice\n+ * @band_idx:\tband/BSS index (0..3)\n+ * @rssi:\tout, moving-average beacon RSSI in dBm (may be NULL)\n+ * @tx_link_speed_kbps: out, TX rate in kbit/s (may be NULL)\n+ *\n+ * Return: 0 on success, -ENOENT if the band's link quality is not ready.\n+ *\n+ * Only RSSI and TX rate come from firmware here; for RX rate read wcid-\u003erate.\n+ */\n+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,\n+\t\t\t\ts8 *rssi, u32 *tx_link_speed_kbps)\n+{\n+\tint ret;\n+\tstruct mt7925_lq_event *event;\n+\tstruct mt7925_link_quality *lq;\n+\tstruct sk_buff *skb;\n+\tstruct mt7925_lq_req req = {\n+\t\t.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_LINK_QUALITY),\n+\t\t.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),\n+\t};\n+\n+\tif (band_idx \u003e= MT7925_LQ_BAND_NUM)\n+\t\treturn -EINVAL;\n+\n+\tret = mt76_mcu_send_and_get_msg(\u0026dev-\u003emt76,\n+\t\t\t\t\tMCU_UNI_QUERY(GET_STAT_INFO), \u0026req,\n+\t\t\t\t\tsizeof(req), true, \u0026skb);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (skb-\u003elen \u003c sizeof(*event)) {\n+\t\tret = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\tevent = (struct mt7925_lq_event *)skb-\u003edata;\n+\n+\tif (le16_to_cpu(event-\u003etag) != UNI_CMD_ID_STATISTICS_LINK_QUALITY) {\n+\t\tret = -EPROTO;\n+\t\tgoto out;\n+\t}\n+\n+\tlq = \u0026event-\u003elq[band_idx];\n+\tif (!lq-\u003eis_ready) {\n+\t\tret = -ENOENT;\n+\t\tgoto out;\n+\t}\n+\n+\tif (rssi)\n+\t\t*rssi = lq-\u003erssi;\n+\n+\t/* firmware reports TX rate in 500 Kb/s units -\u003e kbit/s */\n+\tif (tx_link_speed_kbps)\n+\t\t*tx_link_speed_kbps = le16_to_cpu(lq-\u003elink_speed) * 500;\n+\n+\tret = 0;\n+\n+out:\n+\tdev_kfree_skb(skb);\n+\n+\treturn ret;\n+}\ndiff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.h b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h\nnew file mode 100644\nindex 0000000000000..a70b9eb3aa806\n--- /dev/null\n+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h\n@@ -0,0 +1,309 @@\n+/* SPDX-License-Identifier: BSD-3-Clause-Clear */\n+/* Copyright (C) 2026 MediaTek Inc. */\n+\n+#ifndef __MT7925_STATS_H\n+#define __MT7925_STATS_H\n+\n+#include \u003clinux/if_ether.h\u003e\n+#include \u003clinux/types.h\u003e\n+#include \"../mt76_connac_mcu.h\"\n+\n+#define MT7925_STA_STATS_AC_NUM 4\n+#define MT7925_STA_STATS_BAND_NUM 2\n+#define MT7925_STA_STATS_AGG_RANGE_NUM 16\n+#define MT7925_STA_STATS_TX_QUALITY_NUM 4\n+#define MT7925_TX_MODE_NUM      16\n+#define MT7925_TX_RATE_CCK_NUM  5\n+#define MT7925_TX_RATE_BW_NUM   6\n+\n+/* HW rate code (16-bit) field extract, mirror Gen4m CONNAC3X_HW_TX_RATE_TO_*.\n+ * The auto-rate table stores one rate code per entry.\n+ */\n+#define MT7925_HW_RATE_TO_MCS(_x)\t(((_x) \u0026 GENMASK(5, 0)) \u003e\u003e 0)\n+#define MT7925_HW_RATE_TO_MODE(_x)\t(((_x) \u0026 GENMASK(9, 6)) \u003e\u003e 6)\n+#define MT7925_HW_RATE_TO_NSS(_x)\t(((_x) \u0026 GENMASK(13, 10)) \u003e\u003e 10)\n+#define MT7925_HW_RATE_TO_STBC(_x)\t(((_x) \u0026 BIT(14)) \u003e\u003e 14)\n+#define MT7925_HW_RATE_TO_DCM(_x)\t(((_x) \u0026 BIT(4)) \u003e\u003e 4)\n+#define MT7925_HW_RATE_TO_106T(_x)\t(((_x) \u0026 BIT(5)) \u003e\u003e 5)\n+#define MT7925_HW_RATE_UNMASK_DCM(_r)\t((u8)(_r) \u0026 0xef)\n+#define MT7925_HW_RATE_UNMASK_106T(_r)\t((u8)(_r) \u0026 0xdf)\n+\n+/* Number of entries in the firmware auto-rate table (mirror Gen4m\n+ * AUTO_RATE_NUM). RA cycles through these and reports which one is in use\n+ * via wtbl-\u003erate_idx.\n+ */\n+#define MT7925_AUTO_RATE_NUM 8\n+\n+/* Firmware EVENT_STATISTICS sub-event tags. The WTBL rate request uses cmd tag\n+ * UNI_CMD_ID_STATISTICS_WTBL (10) but the firmware replies with event tag 12.\n+ */\n+#define MT7925_UNI_EVENT_STATISTICS_WTBL 12\n+\n+/* CONNAC3X TX Vector BBP latch bit fields (mirror cmm_asic_connac3x.h).\n+ * These operate on the 3-DWORD tx_vector[band].txv[3] array.\n+ */\n+#define MT7925_TXV_GET_TX_RATE(_txv)\t((_txv)[2] \u0026 0x7f)\n+#define MT7925_TXV_GET_TX_LDPC(_txv)\t(((_txv)[2] \u0026 (0x1 \u003c\u003c 7)) \u003e\u003e 7)\n+#define MT7925_TXV_GET_TX_STBC(_txv)\t(((_txv)[0] \u0026 (0x3 \u003c\u003c 6)) \u003e\u003e 6)\n+#define MT7925_TXV_GET_TX_FRMODE(_txv)\t(((_txv)[0] \u0026 (0x7 \u003c\u003c 8)) \u003e\u003e 8)\n+#define MT7925_TXV_GET_TX_MODE(_txv)\t(((_txv)[0] \u0026 (0xf \u003c\u003c 12)) \u003e\u003e 12)\n+#define MT7925_TXV_GET_TX_NSTS(_txv)\t(((_txv)[2] \u0026 (0xfU \u003c\u003c 28)) \u003e\u003e 28)\n+#define MT7925_TXV_GET_TX_PWR(_txv)\t(((_txv)[0] \u0026 (0xff \u003c\u003c 16)) \u003e\u003e 16)\n+#define MT7925_TXV_GET_TX_SGI(_txv)\t(((_txv)[1] \u0026 (0x3 \u003c\u003c 26)) \u003e\u003e 26)\n+#define MT7925_TXV_GET_TX_SPE_IDX(_txv)\t(((_txv)[0] \u0026 (0x1f \u003c\u003c 0)) \u003e\u003e 0)\n+#define MT7925_TXV_GET_TX_DCM(_txv)\t(((_txv)[2] \u0026 (0x1 \u003c\u003c 4)) \u003e\u003e 4)\n+#define MT7925_TXV_GET_TX_106T(_txv)\t(((_txv)[2] \u0026 (0x1 \u003c\u003c 5)) \u003e\u003e 5)\n+#define MT7925_TXV_RATE_UNMASK_DCM(_r)\t((u8)(_r) \u0026 0xef)\n+#define MT7925_TXV_RATE_UNMASK_106T(_r)\t((u8)(_r) \u0026 0xdf)\n+\n+extern const char * const mt7925_tx_mode_str[MT7925_TX_MODE_NUM];\n+extern const char * const mt7925_tx_rate_cck_str[MT7925_TX_RATE_CCK_NUM];\n+extern const char * const mt7925_tx_rate_bw_str[MT7925_TX_RATE_BW_NUM];\n+\n+/* TX rate mode, mirror Gen4m TX_RATE_MODE_* (see nic_rate.h). Used to decode\n+ * both the TX Vector BBP latch and the firmware auto-rate table rate codes.\n+ */\n+enum mt7925_tx_rate_mode {\n+\tMT7925_TX_RATE_MODE_CCK = 0,\n+\tMT7925_TX_RATE_MODE_OFDM = 1,\n+\tMT7925_TX_RATE_MODE_HTMIX = 2,\n+\tMT7925_TX_RATE_MODE_HTGF = 3,\n+\tMT7925_TX_RATE_MODE_VHT = 4,\n+\tMT7925_TX_RATE_MODE_PLR = 5,\n+\tMT7925_TX_RATE_MODE_HE_SU = 8,\n+\tMT7925_TX_RATE_MODE_HE_ER = 9,\n+\tMT7925_TX_RATE_MODE_HE_TRIG = 10,\n+\tMT7925_TX_RATE_MODE_HE_MU = 11,\n+\tMT7925_TX_RATE_MODE_EHT_ER = 13,\n+\tMT7925_TX_RATE_MODE_EHT_TRIG = 14,\n+\tMT7925_TX_RATE_MODE_EHT_MU = 15,\n+};\n+\n+enum {\n+\tUNI_CMD_MIB_DATA,\n+};\n+\n+enum stats_query_mib_array_index {\n+\tQUERY_MIB_CNT_RX_FCS_ERR = 0,\n+\tQUERY_MIB_CNT_RX_FIFO_OVERFLOW,\n+\tQUERY_MIB_CNT_RX_MPDU,\n+\tQUERY_MIB_CNT_AMPDU_RX_COUNT,\n+\tQUERY_MIB_CNT_PF_DROP,\n+\tQUERY_MIB_CNT_LEN_MISMATCH,\n+\tQUERY_MIB_CNT_CHANNEL_IDLE,\n+\tQUERY_MIB_CNT_CCA_NAV_TX_TIME,\n+\tQUERY_MIB_CNT_MDRDY,\n+\tQUERY_MIB_CNT_RX_CCK_MDRDY_TIME,\n+\tQUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,\n+\tQUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,\n+\tQUERY_MIB_CNT_P_CCA_TIME,\n+\tQUERY_MIB_CNT_S_CCA_TIME,\n+\tQUERY_MIB_CNT_P_ED_TIME,\n+\tQUERY_MIB_CNT_BCN_TX,\n+\tQUERY_MIB_CNT_TX_BW_40MHZ,\n+\tQUERY_MIB_CNT_TX_BW_80MHZ,\n+\tQUERY_MIB_CNT_TX_BW_160MHZ,\n+\tQUERY_MIB_CNT_BSS0_BA_MISS,\n+\tQUERY_MIB_CNT_BSS0_RTS_TX_CNT,\n+\tQUERY_MIB_CNT_BSS0_FRAME_RETRY,\n+\tQUERY_MIB_CNT_BSS0_FRAME_RETRY_2,\n+\tQUERY_MIB_CNT_BSS0_RTS_RETRY,\n+\tQUERY_MIB_CNT_BSS0_ACK_FAIL,\n+\tQUERY_MIB_CNT_AMPDU,\n+\tQUERY_MIB_CNT_AMPDU_MPDU,\n+\tQUERY_MIB_CNT_AMPDU_ACKED,\n+\tQUERY_MIB_CNT_MAX_NUM,\n+};\n+\n+enum stats_trx_agg_range {\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE0_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE1_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE2_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE3_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE4_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE5_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE6_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE7_IDX,\n+\tSTAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM\n+};\n+\n+enum {\n+\tMT7925_BW_20 = 0,\n+\tMT7925_BW_40,\n+\tMT7925_BW_80,\n+\tMT7925_BW_160,\n+};\n+\n+struct mt7925_sta_stats_ac {\n+\t__le32 tx_fail;\n+\t__le32 tx_retry;\n+} __packed;\n+\n+struct mt7925_sta_stats_tx_vector {\n+\t__le32 txv[3];\n+} __packed;\n+\n+struct mt7925_sta_stats_mib {\n+\t__le32 rx_mpdu_cnt;\n+\t__le32 fcs_error;\n+\t__le32 rx_fifo_full;\n+\t__le32 ampdu_tx_sf_cnt;\n+\t__le32 ampdu_tx_ack_sf_cnt;\n+\t__le32 tx_range_ampdu_cnt[MT7925_STA_STATS_AGG_RANGE_NUM];\n+} __packed;\n+\n+/* Firmware EVENT_STA_STATISTICS body. */\n+struct mt7925_sta_stats {\n+\tu8 version;\n+\tu8 rsv[3];\n+\t__le32 flags;\n+\n+\tu8 sta_idx;\n+\tu8 bss_idx;\n+\tu8 wtbl_idx;\n+\tu8 rsv1;\n+\n+\tu8 mac_addr[ETH_ALEN];\n+\tu8 per;\n+\tu8 rcpi;\n+\n+\t__le32 phy_mode;\n+\t__le16 link_speed; /* 0.5 Mbit/s units */\n+\tu8 link_quality;\n+\tu8 link_reserved;\n+\n+\t__le32 tx_count;\n+\t__le32 tx_fail_count;\n+\t__le32 tx_life_timeout_count;\n+\t__le32 tx_done_air_time;\n+\t__le32 transmit_count;\n+\t__le32 transmit_fail_count;\n+\n+\tstruct mt7925_sta_stats_ac ac[MT7925_STA_STATS_AC_NUM];\n+\n+\tu8 temperature;\n+\tu8 skip_ar;\n+\tu8 ar_table_idx;\n+\tu8 rate_entry_idx;\n+\tu8 rate_entry_idx_prev;\n+\tu8 tx_sgi_detect_pass_cnt;\n+\tu8 ave_per;\n+\tu8 rsv2;\n+\n+\t__le32 agg_range_ctrl[2];\n+\tu8 range_mode;\n+\tu8 rsv3[3];\n+\t__le32 agg_range_ctrl_ext[6];\n+\n+\tu8 ar_state_curr;\n+\tu8 ar_state_prev;\n+\tu8 ar_action_type;\n+\tu8 highest_rate_cnt;\n+\tu8 lowest_rate_cnt;\n+\tu8 rsv4;\n+\t__le16 train_up;\n+\t__le16 train_down;\n+\tu8 rsv5[2];\n+\t__le32 rate1_tx_cnt;\n+\t__le32 rate1_fail_cnt;\n+\n+\tstruct mt7925_sta_stats_tx_vector tx_vector[MT7925_STA_STATS_BAND_NUM];\n+\tstruct mt7925_sta_stats_mib mib[MT7925_STA_STATS_BAND_NUM];\n+\n+\tu8 is_force_tx_stream;\n+\tu8 is_force_se_off;\n+\t__le16 ra_running_cnt;\n+\tu8 ra_status;\n+\tu8 max_ampdu_factor;\n+\tu8 tx_quality[MT7925_STA_STATS_TX_QUALITY_NUM];\n+\tu8 tx_rate_up_penalty;\n+\tu8 low_traffic_mode;\n+\tu8 low_traffic_count;\n+\tu8 low_traffic_dashboard;\n+\tu8 dynamic_sgi_state;\n+\tu8 dynamic_sgi_score;\n+\tu8 dynamic_bw_state;\n+\tu8 dynamic_gband_256qam_state;\n+\tu8 vht_non_sp_rate_state;\n+\tu8 rsv6[3];\n+\tu8 rsv7[2];\n+} __packed;\n+\n+struct mt7925_sta_rec_lookup {\n+\tint sta_rec_idx;\n+};\n+\n+/* Firmware auto-rate table snapshot, mirror Gen4m struct UNI_EVENT_STAT_WTBL.\n+ *\n+ * The firmware RA maintains up to MT7925_AUTO_RATE_NUM candidate rates in\n+ * rate_code[]. rate_idx tells which entry is currently in use, so a dump can\n+ * point at it with \"--\u003e\". fcap is the bandwidth capability and the ldpc/sgi\n+ * flags describe the per-mode coding/guard-interval used for those rates.\n+ */\n+struct mt7925_wtbl_rate {\n+\t__le16 tag;\n+\t__le16 len;\n+\tu8 rx_rcpi0;\n+\tu8 rx_rcpi1;\n+\tu8 cbrn;\n+\tu8 fcap;\n+\tu8 spe_idx;\n+\tu8 g2;\t\t\t/* SGI enabled, non-HE, BW20 */\n+\tu8 g4;\t\t\t/* SGI enabled, non-HE, BW40 */\n+\tu8 g8;\t\t\t/* SGI enabled, non-HE, BW80 */\n+\tu8 g16;\t\t\t/* SGI enabled, non-HE, BW160 */\n+\tu8 g2_he;\t\t/* GI, HE, BW20 */\n+\tu8 g4_he;\t\t/* GI, HE, BW40 */\n+\tu8 g8_he;\t\t/* GI, HE, BW80 */\n+\tu8 g16_he;\t\t/* GI, HE, BW160 */\n+\tu8 gi_eht;\t\t/* GI, EHT */\n+\tu8 ht_ldpc;\n+\tu8 vht_ldpc;\n+\tu8 he_ldpc;\n+\tu8 eht_ldpc;\n+\tu8 af;\n+\tu8 rate_idx;\t\t/* index into rate_code[] currently in use */\n+\t__le16 rate_code[MT7925_AUTO_RATE_NUM];\n+\t__le32 rsv[4];\n+} __packed;\n+\n+int\n+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx);\n+\n+void\n+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3]);\n+\n+u8\n+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap);\n+\n+u8\n+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode);\n+\n+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx);\n+\n+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,\n+\t\t\t     struct mt7925_wtbl_rate *rate);\n+\n+int mt792x_mcu_get_stat(struct mt792x_dev *dev,\n+\t\t\tstruct mt7925_sta_stats *stats);\n+\n+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,\n+\t\t\t    const u32 *counters, u8 counter_num,\n+\t\t\t    u64 *values);\n+\n+/* GET_STAT_INFO (UNI_CMD_ID_GET_STATISTICS = 0x23),\n+ * sub-command UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1.\n+ *\n+ * @band_idx:            band/BSS index (0..3)\n+ * @rssi:                out, moving-average beacon RSSI (dBm), may be NULL\n+ * @tx_link_speed_kbps:  out, TX link speed in kbit/s, may be NULL\n+ *\n+ * Returns 0 on success, -ENOENT if the band's link quality is not ready.\n+ *\n+ * NOTE: firmware provides only RSSI and TX rate here. RX rate/bandwidth are\n+ * not in this event; read wcid-\u003erate for the RX rate.\n+ */\n+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,\n+\t\t\t\ts8 *rssi, u32 *tx_link_speed_kbps);\n+\n+#endif\n"
}

2/1 2026/08/27 09:12 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a debugfs entry to query and print statistics from the firmware. It is a diagnostic/logging change that does not functionally alter the driver's execution flow.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 5e5db5bd1d3ccec498ba228472e71b468aab4db5
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Aug 27 09:12:29 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a5..bf0f105c786e6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1483,6 +1483,179 @@ enum {
 	MT_NIC_CAP_EML_CAP = 0x22,
 };
 
+enum {
+	UNI_CMD_ID_STATISTICS_BASIC = 0,
+	UNI_CMD_ID_STATISTICS_LINK_QUALITY = 1,
+	UNI_CMD_ID_STATISTICS_STA = 2,
+	UNI_CMD_ID_STATISTICS_BUG_REPORT = 3,
+	UNI_CMD_ID_STATISTICS_TX_STATS = 4,
+	UNI_CMD_ID_STATISTICS_ALL_STATS = 5,
+	UNI_CMD_ID_STATISTICS_EML_STATS = 6,
+	UNI_CMD_ID_STATISTICS_REGULAR_STATS = 7,
+	UNI_CMD_ID_STATISTICS_BSS_LINK_QUALITY = 8,
+	UNI_CMD_ID_STATISTICS_ML_CHNL_COND = 9,
+	UNI_CMD_ID_STATISTICS_WTBL = 10,
+	UNI_CMD_ID_STATISTICS_LINK_LAYER_STATS = 128,
+	UNI_CMD_ID_STATISTICS_PPDU_LATENCY = 129,
+	UNI_CMD_ID_STATISTICS_CURRENT_TX_RATE = 130,
+	UNI_CMD_ID_STATISTICS_SET_TX_BITRATE_MONITOR = 131,
+	UNI_CMD_ID_STATISTICS_GET_PREDICT_TX_BITRATE = 132,
+	UNI_CMD_ID_STATISTICS_SET_ATPS_TYPE_RATE_MODE = 133,
+	UNI_CMD_ID_STATISTICS_SET_BEACON_REPORT = 134,
+	UNI_CMD_ID_STATISTICS_BSS_CURRENT_TX_RATE = 135,
+	UNI_CMD_ID_STATISTICS_GET_BSS_PREDICT_TX_BITRATE = 136,
+	UNI_CMD_ID_STATISTICS_MAX_NUM
+};
+
+enum mt7925_mib_counter {
+	UNI_CMD_MIB_CNT_RX_FCS_ERR = 0,
+	UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW = 1,
+	UNI_CMD_MIB_CNT_RX_MPDU = 2,
+	UNI_CMD_MIB_CNT_AMPDU_RX_COUNT = 3,
+	UNI_CMD_MIB_CNT_RX_TOTAL_BYTE = 4,
+	UNI_CMD_MIB_CNT_RX_VALID_AMPDU_SF = 5,
+	UNI_CMD_MIB_CNT_RX_VALID_BYTE = 6,
+	UNI_CMD_MIB_CNT_CHANNEL_IDLE = 7,
+	UNI_CMD_MIB_CNT_VEC_DROP = 8,
+	UNI_CMD_MIB_CNT_DELIMITER_FAIL = 9,
+	UNI_CMD_MIB_CNT_VEC_MISMATCH = 10,
+	UNI_CMD_MIB_CNT_MDRDY = 11,
+	UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME = 12,
+	UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME = 13,
+	UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME = 14,
+	UNI_CMD_MIB_CNT_PF_DROP = 15,
+	UNI_CMD_MIB_CNT_LEN_MISMATCH = 16,
+	UNI_CMD_MIB_CNT_P_CCA_TIME = 17,
+	UNI_CMD_MIB_CNT_S_CCA_TIME = 18,
+	UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME = 19,
+	UNI_CMD_MIB_CNT_P_ED_TIME = 20,
+	UNI_CMD_MIB_CNT_BCN_TX = 21,
+	UNI_CMD_MIB_CNT_TX_BW_20MHZ = 22,
+	UNI_CMD_MIB_CNT_TX_BW_40MHZ = 23,
+	UNI_CMD_MIB_CNT_TX_BW_80MHZ = 24,
+	UNI_CMD_MIB_CNT_TX_BW_160MHZ = 25,
+	UNI_CMD_RMAC_CNT_OBSS_AIRTIME = 26,
+	UNI_CMD_RMAC_CNT_NONWIFI_AIRTIME = 27,
+	UNI_CMD_MIB_CNT_TX_DUR_CNT = 28,
+	UNI_CMD_MIB_CNT_RX_DUR_CNT = 29,
+	UNI_CMD_MIB_CNT_BA_CNT = 30,
+	UNI_CMD_MIB_CNT_MAC2PHY_TX_TIME = 31,
+	UNI_CMD_MIB_CNT_RX_OUT_OF_RANGE_COUNT = 32,
+	UNI_CMD_MIB_CNT_IBF_TX = 33,
+	UNI_CMD_MIB_CNT_EBF_TX = 34,
+	UNI_CMD_MIB_CNT_MUBF_TX = 35,
+	UNI_CMD_MIB_CNT_RX_BF_VHTFBK = 36,
+	UNI_CMD_MIB_CNT_RX_BF_HTFBK = 37,
+	UNI_CMD_MIB_CNT_RX_BF_HEFBK = 38,
+	UNI_CMD_MIB_CNT_BFEE_TXFBK_BFPOLL_TRI = 39,
+	UNI_CMD_MIB_CNT_BFEE_TXFBK_TRI = 40,
+	UNI_CMD_MIB_CNT_BFEE_RX_FBKCQI = 41,
+	UNI_CMD_MIB_CNT_BFEE_RX_NDP = 42,
+	UNI_CMD_MIB_CNT_RX_BF_IBF_UPT = 43,
+	UNI_CMD_MIB_CNT_RX_BF_EBF_UPT = 44,
+	UNI_CMD_MIB_CNT_BFEE_SP_ABORT = 45,
+	UNI_CMD_MIB_CNT_BFEE_TB_LEN_ERR = 46,
+	UNI_CMD_MIB_CNT_BFEE_TXFBK_MUTE = 47,
+	UNI_CMD_MIB_CNT_BFEE_TMAC_ABORT = 48,
+	UNI_CMD_MIB_CNT_BFEE_TXFBK_CPL = 49,
+	UNI_CMD_MIB_CNT_BFEE_COANT_BLKTX = 50,
+	UNI_CMD_MIB_CNT_BFEE_FBK_SEG = 51,
+	UNI_CMD_MIB_CNT_NAV_TIME = 52,
+	UNI_CMD_MIB_CNT_TX_ABORT_CNT1 = 53,
+	UNI_CMD_MIB_CNT_TX_ABORT_CNT0 = 54,
+	UNI_CMD_MIB_CNT_TX_ABORT_CNT3 = 55,
+	UNI_CMD_MIB_CNT_TX_ABORT_CNT2 = 56,
+	UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT = 57,
+	UNI_CMD_MIB_CNT_BSS1_RTS_TX_CNT = 58,
+	UNI_CMD_MIB_CNT_BSS2_RTS_TX_CNT = 59,
+	UNI_CMD_MIB_CNT_BSS3_RTS_TX_CNT = 60,
+	UNI_CMD_MIB_CNT_BSS0_RTS_RETRY = 61,
+	UNI_CMD_MIB_CNT_BSS1_RTS_RETRY = 62,
+	UNI_CMD_MIB_CNT_BSS2_RTS_RETRY = 63,
+	UNI_CMD_MIB_CNT_BSS3_RTS_RETRY = 64,
+	UNI_CMD_MIB_CNT_BSS0_BA_MISS = 65,
+	UNI_CMD_MIB_CNT_BSS1_BA_MISS = 66,
+	UNI_CMD_MIB_CNT_BSS2_BA_MISS = 67,
+	UNI_CMD_MIB_CNT_BSS3_BA_MISS = 68,
+	UNI_CMD_MIB_CNT_BSS0_ACK_FAIL = 69,
+	UNI_CMD_MIB_CNT_BSS1_ACK_FAIL = 70,
+	UNI_CMD_MIB_CNT_BSS2_ACK_FAIL = 71,
+	UNI_CMD_MIB_CNT_BSS3_ACK_FAIL = 72,
+	UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY = 73,
+	UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY = 74,
+	UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY = 75,
+	UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY = 76,
+	UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2 = 77,
+	UNI_CMD_MIB_CNT_BSS1_FRAME_RETRY_2 = 78,
+	UNI_CMD_MIB_CNT_BSS2_FRAME_RETRY_2 = 79,
+	UNI_CMD_MIB_CNT_BSS3_FRAME_RETRY_2 = 80,
+	UNI_CMD_MIB_CNT_RX_A1_SEARCH = 81,
+	UNI_CMD_MIB_CNT_RX_DROP_MPDU = 82,
+	UNI_CMD_MIB_CNT_RX_UNWANTED = 83,
+	UNI_CMD_MIB_CNT_RX_FCS_OK = 84,
+	UNI_CMD_MIB_CNT_SU_TX_OK = 85,
+	UNI_CMD_MIB_CNT_TX_FULL_BW = 86,
+	UNI_CMD_MIB_CNT_TX_AUTO_BW = 87,
+	UNI_CMD_MIB_CNT_CCA_SAMPLE_IDLE = 88,
+	UNI_CMD_MIB_CNT_CCA_SAMPLE_ACTIVE = 89,
+	UNI_CMD_MIB_CNT_CCA_TIME = 90,
+	UNI_CMD_MIB_CNT_S_20BW_CCA_TIME = 91,
+	UNI_CMD_MIB_CNT_S_40BW_CCA_TIME = 92,
+	UNI_CMD_MIB_CNT_S_80BW_CCA_TIME = 93,
+	UNI_CMD_MIB_CNT_S_160BW_CCA_TIME = 94,
+	UNI_CMD_MIB_CNT_S_P20BW_0_ED_TIME = 95,
+	UNI_CMD_MIB_CNT_S_P20BW_1_ED_TIME = 96,
+	UNI_CMD_MIB_CNT_S_P20BW_2_ED_TIME = 97,
+	UNI_CMD_MIB_CNT_S_P20BW_3_ED_TIME = 98,
+	UNI_CMD_MIB_CNT_S_P20BW_4_ED_TIME = 99,
+	UNI_CMD_MIB_CNT_S_P20BW_5_ED_TIME = 100,
+	UNI_CMD_MIB_CNT_S_P20BW_6_ED_TIME = 101,
+	UNI_CMD_MIB_CNT_S_P20BW_7_ED_TIME = 102,
+	UNI_CMD_MIB_CNT_S_P20BW_8_ED_TIME = 103,
+	UNI_CMD_MIB_CNT_S_P20BW_9_ED_TIME = 104,
+	UNI_CMD_MIB_CNT_S_P20BW_10_ED_TIME = 105,
+	UNI_CMD_MIB_CNT_S_P20BW_11_ED_TIME = 106,
+	UNI_CMD_MIB_CNT_S_P20BW_12_ED_TIME = 107,
+	UNI_CMD_MIB_CNT_S_P20BW_13_ED_TIME = 108,
+	UNI_CMD_MIB_CNT_S_P20BW_14_ED_TIME = 109,
+	UNI_CMD_MIB_CNT_S_P20BW_15_ED_TIME = 110,
+	UNI_CMD_MIB_CNT_TX_BW_320MHZ = 111,
+	UNI_CMD_MIB_CNT_TX_DDLMT_RNG0 = 112, /* Rng0~Rng4 112~116 */
+	UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_3 = 117, /* 117~120 */
+	UNI_CMD_MIB_CNT_BSS0_TX = 121, /* 121~124 */
+	UNI_CMD_MIB_CNT_BSS0_TX_DATA = 125, /* 125~128 */
+	UNI_CMD_MIB_CNT_BSS0_TX_BYTE = 129, /* 129~132 */
+	UNI_CMD_MIB_CNT_RX_OK_BSS0 = 133, /* 133~136 */
+	UNI_CMD_MIB_CNT_RX_BYTE_BSS0 = 137, /* 137~140 */
+	UNI_CMD_MIB_CNT_RX_DATA_BSS0 = 141, /* 141~144 */
+	UNI_CMD_MIB_CNT_MBSS0_TX_OK = 145, /* 145~160 */
+	UNI_CMD_MIB_CNT_MBSS0_TX_BYTE = 161, /* 161~176 */
+	UNI_CMD_MIB_CNT_RX_OK_MBSS0 = 177, /* 177~192 */
+	UNI_CMD_MIB_CNT_RX_BYTE_MBSS0 = 193, /* 193~208 */
+	UNI_CMD_MIB_CNT_AMPDU = 209,
+	UNI_CMD_MIB_CNT_AMPDU_MPDU = 210,
+	UNI_CMD_MIB_CNT_AMPDU_ACKED = 211,
+	UNI_CMD_MIB_CNT_MAX_NUM
+};
+
+enum ENUM_UNI_CMD_GET_STATISTICS_TAG {
+	UNI_CMD_GET_STATISTICS_TAG_BASIC = 0,
+	UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1,
+	UNI_CMD_GET_STATISTICS_TAG_STA = 2,
+	UNI_CMD_GET_STATISTICS_TAG_BUG_REPORT = 3,
+	UNI_CMD_GET_STATISTICS_TAG_EML_STATS = 6,
+	UNI_CMD_GET_STATISTICS_TAG_REGULAR_STATS = 7,
+	UNI_CMD_GET_STATISTICS_TAG_BSS_LINK_QUALITY = 8,
+	UNI_CMD_GET_STATISTICS_TAG_GET_ML_CHNL_COND = 9,
+	UNI_CMD_GET_STATISTICS_TAG_STAT_WTBL = 10,
+	UNI_CMD_GET_STATISTICS_TAG_LINK_LAYER_STATS = 0x80,
+	UNI_CMD_GET_STATISTICS_TAG_PPDU_LATENCY,
+	UNI_CMD_GET_STATISTICS_TAG_CURRENT_TX_RATE,
+	UNI_CMD_GET_STATISTICS_TAG_BEACON_REPORT = 0x86,
+	UNI_CMD_GET_STATISTICS_TAG_BSS_CURRENT_TX_RATE = 0x87,
+	UNI_CMD_GET_STATISTICS_TAG_BSS_PRED_TX_BITRATE = 0x88,
+};
+
 #define UNI_WOW_DETECT_TYPE_MAGIC		BIT(0)
 #define UNI_WOW_DETECT_TYPE_ANY			BIT(1)
 #define UNI_WOW_DETECT_TYPE_DISCONNECT		BIT(2)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393c..3863a89b89742 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
 obj-$(CONFIG_MT7925E) += mt7925e.o
 obj-$(CONFIG_MT7925U) += mt7925u.o
 
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o stats.o
 mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
 mt7925e-y := pci.o pci_mac.o pci_mcu.o
 mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47af..7a40d5ac47dac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -1,8 +1,11 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /* Copyright (C) 2023 MediaTek Inc. */
 
+#include <linux/math64.h>
+
 #include "mt7925.h"
 #include "mcu.h"
+#include "stats.h"
 
 static int
 mt7925_reg_set(void *data, u64 val)
@@ -286,6 +289,346 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
 
+static const u32 stats_query_mib_counters[] = {
+	UNI_CMD_MIB_CNT_RX_FCS_ERR,
+	UNI_CMD_MIB_CNT_RX_FIFO_OVERFLOW,
+	UNI_CMD_MIB_CNT_RX_MPDU,
+	UNI_CMD_MIB_CNT_AMPDU_RX_COUNT,
+	UNI_CMD_MIB_CNT_PF_DROP,
+	UNI_CMD_MIB_CNT_LEN_MISMATCH,
+	UNI_CMD_MIB_CNT_CHANNEL_IDLE,
+	UNI_CMD_MIB_CNT_CCA_NAV_TX_TIME,
+	UNI_CMD_MIB_CNT_MDRDY,
+	UNI_CMD_MIB_CNT_RX_CCK_MDRDY_TIME,
+	UNI_CMD_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+	UNI_CMD_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+	UNI_CMD_MIB_CNT_P_CCA_TIME,
+	UNI_CMD_MIB_CNT_S_CCA_TIME,
+	UNI_CMD_MIB_CNT_P_ED_TIME,
+	UNI_CMD_MIB_CNT_BCN_TX,
+	UNI_CMD_MIB_CNT_TX_BW_40MHZ,
+	UNI_CMD_MIB_CNT_TX_BW_80MHZ,
+	UNI_CMD_MIB_CNT_TX_BW_160MHZ,
+	UNI_CMD_MIB_CNT_BSS0_BA_MISS,
+	UNI_CMD_MIB_CNT_BSS0_RTS_TX_CNT,
+	UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY,
+	UNI_CMD_MIB_CNT_BSS0_FRAME_RETRY_2,
+	UNI_CMD_MIB_CNT_BSS0_RTS_RETRY,
+	UNI_CMD_MIB_CNT_BSS0_ACK_FAIL,
+	UNI_CMD_MIB_CNT_AMPDU,
+	UNI_CMD_MIB_CNT_AMPDU_MPDU,
+	UNI_CMD_MIB_CNT_AMPDU_ACKED,
+};
+
+/* Dump the whole firmware auto-rate table and mark the entry currently in use
+ * with "-->"
+ */
+static void
+mt7925_dump_auto_rate_table(struct seq_file *s,
+			    const struct mt7925_wtbl_rate *wr)
+{
+	u8 fcap = wr->fcap;
+	u8 rate_idx = wr->rate_idx;
+	u8 cbrn = wr->cbrn;
+	int i;
+
+	seq_printf(s, "%-22s  Cur Rate Index = %u\n", " ", rate_idx);
+
+	for (i = 0; i < MT7925_AUTO_RATE_NUM; i++) {
+		u16 rate_code = le16_to_cpu(wr->rate_code[i]);
+		u8 txmode = MT7925_HW_RATE_TO_MODE(rate_code);
+		u8 rate = MT7925_HW_RATE_TO_MCS(rate_code);
+		u8 nsts = MT7925_HW_RATE_TO_NSS(rate_code) + 1;
+		u8 stbc = MT7925_HW_RATE_TO_STBC(rate_code);
+		u8 dcm = MT7925_HW_RATE_TO_DCM(rate_code);
+		u8 ersu106t = MT7925_HW_RATE_TO_106T(rate_code);
+		u8 sgi = mt7925_wtbl_get_sgi(wr, txmode, fcap);
+		u8 ldpc = mt7925_wtbl_get_ldpc(wr, txmode);
+		const char *mode_str = "N/A";
+
+		if (dcm)
+			rate = MT7925_HW_RATE_UNMASK_DCM(rate);
+		if (ersu106t)
+			rate = MT7925_HW_RATE_UNMASK_106T(rate);
+
+		if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+			mode_str = mt7925_tx_mode_str[txmode];
+
+		seq_printf(s, "Rate index[%d]    %s", i,
+			   rate_idx == i ? "--> " : "    ");
+
+		/* rate/MCS field */
+		if (txmode == MT7925_TX_RATE_MODE_CCK)
+			seq_printf(s, "%s, ",
+				   mt7925_tx_rate_cck_str[rate & 0x3]);
+		else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+			seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+		else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+			 txmode == MT7925_TX_RATE_MODE_HTGF)
+			seq_printf(s, "MCS%d, ", rate);
+		else
+			seq_printf(s, "%s%d_MCS%d, ",
+				   stbc ? "NSTS" : "NSS", nsts, rate);
+
+		/* bandwidth: mirror the CCK/OFDM -> BW20 and cbrn handling */
+		if (txmode == MT7925_TX_RATE_MODE_CCK ||
+		    txmode == MT7925_TX_RATE_MODE_OFDM)
+			seq_printf(s, "%s, ", mt7925_tx_rate_bw_str[0]);
+		else if (i > cbrn)
+			seq_printf(s, "%s, ",
+				   fcap < 5 ?
+				   (fcap > MT7925_BW_20 ?
+				    mt7925_tx_rate_bw_str[fcap - 1] :
+				    mt7925_tx_rate_bw_str[fcap]) :
+				   mt7925_tx_rate_bw_str[5]);
+		else
+			seq_printf(s, "%s, ",
+				   fcap < 5 ? mt7925_tx_rate_bw_str[fcap] :
+				   mt7925_tx_rate_bw_str[5]);
+
+		/* GI / preamble */
+		if (txmode == MT7925_TX_RATE_MODE_CCK)
+			seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+		else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+			; /* OFDM has no GI/preamble to print */
+		else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+			 txmode == MT7925_TX_RATE_MODE_HTGF ||
+			 txmode == MT7925_TX_RATE_MODE_VHT ||
+			 txmode == MT7925_TX_RATE_MODE_PLR)
+			seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+		else
+			seq_printf(s, "%s, ",
+				   sgi == 0 ? "SGI" :
+				   (sgi == 1 ? "MGI" : "LGI"));
+
+		/* mode, DCM, 106t, STBC, coding */
+		seq_printf(s, "%s%s%s%s%s\n",
+			   mode_str,
+			   dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+			   stbc ? ", STBC, " : ", ",
+			   (ldpc == 0 ||
+			    txmode == MT7925_TX_RATE_MODE_CCK ||
+			    txmode == MT7925_TX_RATE_MODE_OFDM) ?
+			   "BCC" : "LDPC");
+	}
+}
+
+static int mt792x_stats(struct seq_file *s, void *data)
+{
+	s8 rssi = 0;
+	u8 band_idx = 0;
+	bool wtbl_rate_valid = false;
+	int ret = 0;
+	int i = 0;
+	u32 stats_value = 0;
+	u32 tx_link_speed_kbps = 0;
+	u64 tx_total = 0;
+	u64 tx_fail = 0;
+	u64 tx_per = 0;
+	u32 tx_per_rem = 0;
+	/* UNI_CMD_MIB_DATA */
+	u64 mib_values[ARRAY_SIZE(stats_query_mib_counters)];
+	struct mt792x_dev *dev = dev_get_drvdata(s->private);
+	struct mt7925_wtbl_rate wtbl_rate;
+	struct mt7925_sta_stats *stats = NULL;
+
+	band_idx = dev->mphy.band_idx;
+
+	 stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+	if (!stats)
+		return -ENOMEM;
+
+	/* Get STA_STATS */
+	mt792x_mutex_acquire(dev);
+	ret = mt792x_mcu_get_stat(dev, stats);
+	mt792x_mutex_release(dev);
+	if (ret)
+		goto out;
+
+	/* Get MIB_STATS */
+	mt792x_mutex_acquire(dev);
+	ret = mt7925_mcu_get_mib_info(dev, band_idx, stats_query_mib_counters,
+				      ARRAY_SIZE(stats_query_mib_counters), mib_values);
+	mt792x_mutex_release(dev);
+	if (ret)
+		goto out;
+
+	/* Get Link_Speed and RSSI */
+	mt792x_mutex_acquire(dev);
+	ret = mt7925_mcu_get_link_quality(dev, stats->bss_idx, &rssi, &tx_link_speed_kbps);
+	mt792x_mutex_release(dev);
+	if (ret)
+		goto out;
+
+	/* Get the firmware auto-rate table (best effort; do not abort the
+	 * whole stats dump if the WTBL query is not available).
+	 */
+	mt792x_mutex_acquire(dev);
+	if (!mt792x_mcu_get_wtbl_rate(dev, stats->wtbl_idx, &wtbl_rate))
+		wtbl_rate_valid = true;
+	mt792x_mutex_release(dev);
+
+	/* Print STA_STATS info */
+	seq_printf(s, "(STA) connected AP MAC Address = %pM\n", stats->mac_addr);
+	seq_printf(s, "%-22s  BssIdx = [%u]\n", " ", stats->bss_idx);
+	seq_printf(s, "%-22s  StaRecIdx = [%u]\n",
+		   " ", stats->sta_idx);
+	seq_printf(s, "%-22s  RSSI = %d\n", " ", rssi);
+	seq_printf(s, "%-22s  Link_Speed = %u (kbit/s)\n", " ", tx_link_speed_kbps);
+
+	seq_printf(s, "%-22s  temperature = %u\n",
+		   " ", stats->temperature);
+
+	stats_value = le32_to_cpu(stats->transmit_count) - le32_to_cpu(stats->transmit_fail_count);
+	seq_printf(s, "%-22s  Tx success = %u\n",
+		   " ", stats_value);
+
+	stats_value = le32_to_cpu(stats->tx_fail_count) - le32_to_cpu(stats->tx_life_timeout_count);
+	seq_printf(s, "%-22s  Tx fail to Rcv ACK after retry = %u\n",
+		   " ", stats_value);
+
+	seq_printf(s, "%-22s  Rx Mpdu = %u\n",
+		   " ", le32_to_cpu(stats->mib[band_idx].rx_mpdu_cnt));
+
+	seq_printf(s, "%-22s  Rx Fcs Error = %u (MPDU)\n",
+		   " ", le32_to_cpu(stats->mib[band_idx].fcs_error));
+	seq_printf(s, "%-22s  Rx FIFO full = %u (MPDU)\n",
+		   " ", le32_to_cpu(stats->mib[band_idx].rx_fifo_full));
+
+	for (i = 0; i < STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM; i++)
+		seq_printf(s, "%-22s  TRX_AGG_RANGE[%d] = %u (PPDU)\n",
+			   " ", i, le32_to_cpu(stats->mib[band_idx].tx_range_ampdu_cnt[i]));
+
+	seq_printf(s, "%-22s  MPDUs in AMPDUs transmitted = %u (MPDU)\n",
+		   " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_sf_cnt));
+	seq_printf(s, "%-22s  MPDUs in AMPDUs transmitted with ACK reply = %u (MPDU)\n",
+		   " ", le32_to_cpu(stats->mib[band_idx].ampdu_tx_ack_sf_cnt));
+	seq_printf(s, "%-22s  rate1_tx_cnt = %u\n",
+		   " ", le32_to_cpu(stats->rate1_tx_cnt));
+	seq_printf(s, "%-22s  rate1_fail_cnt = %u\n",
+		   " ", le32_to_cpu(stats->rate1_fail_cnt));
+	seq_printf(s, "%-22s  train_up = %u\n",
+		   " ", le16_to_cpu(stats->train_up));
+	seq_printf(s, "%-22s  train_down = %u\n",
+		   " ", le16_to_cpu(stats->train_down));
+	seq_printf(s, "%-22s  is_force_tx_stream = %u\n",
+		   " ", stats->is_force_tx_stream);
+	seq_printf(s, "%-22s  is_force_se_off = %u\n",
+		   " ", stats->is_force_se_off);
+	seq_printf(s, "%-22s  max_ampdu_factor = %u\n",
+		   " ", stats->max_ampdu_factor);
+	seq_printf(s, "%-22s  tx_rate_up_penalty = %u\n",
+		   " ", stats->tx_rate_up_penalty);
+	seq_printf(s, "%-22s  low_traffic_mode = %u\n",
+		   " ", stats->low_traffic_mode);
+	seq_printf(s, "%-22s  low_traffic_count = %u\n",
+		   " ", stats->low_traffic_count);
+	seq_printf(s, "%-22s  low_traffic_dashboard = %u\n",
+		   " ", stats->low_traffic_dashboard);
+	seq_printf(s, "%-22s  dynamic_sgi_state = %u\n",
+		   " ", stats->dynamic_sgi_state);
+	seq_printf(s, "%-22s  dynamic_sgi_score = %u\n",
+		   " ", stats->dynamic_sgi_score);
+	seq_printf(s, "%-22s  dynamic_bw_state = %u\n",
+		   " ", stats->dynamic_bw_state);
+	seq_printf(s, "%-22s  dynamic_gband_256qam_state = %u\n",
+		   " ", stats->dynamic_gband_256qam_state);
+	seq_printf(s, "%-22s  vht_non_sp_rate_state = %u\n",
+		   " ", stats->vht_non_sp_rate_state);
+
+	/* Decode the TX Vector BBP latch to show the current TX MCS rate */
+	if (band_idx < MT7925_STA_STATS_BAND_NUM)
+		mt7925_print_last_tx_rate(s, stats->tx_vector[band_idx].txv);
+
+	/* Dump the whole firmware auto-rate table and mark the entry currently */
+	if (wtbl_rate_valid)
+		mt7925_dump_auto_rate_table(s, &wtbl_rate);
+
+	seq_puts(s, "\nmib state:\n");
+	/* ===Rx Related Counters=== */
+	seq_puts(s, "=== Rx Related Counters ===\n");
+	seq_printf(s, "%-22s  Rx with CRC = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_FCS_ERR]);
+	seq_printf(s, "%-22s  Rx drop due to out of resource = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_FIFO_OVERFLOW]);
+	seq_printf(s, "%-22s  Rx Mpdu = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_MPDU]);
+	seq_printf(s, "%-22s  Rx AMpdu = %llu (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_AMPDU_RX_COUNT]);
+	seq_printf(s, "%-22s  Rx PF Drop = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_PF_DROP]);
+	seq_printf(s, "%-22s  Rx Len Mismatch = %llu (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_LEN_MISMATCH]);
+
+	/* ===Phy/Timing Related Counters=== */
+	seq_puts(s, "\n=== Phy/Timing Related Counters ===\n");
+	seq_printf(s, "%-22s  ChannelIdleCnt = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_CHANNEL_IDLE]);
+	seq_printf(s, "%-22s  CCA_NAV_Tx_Time = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_CCA_NAV_TX_TIME]);
+	seq_printf(s, "%-22s  Rx_MDRDY_CNT = %llu (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_MDRDY]);
+	seq_printf(s, "%-22s  CCK_MDRDY = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_CCK_MDRDY_TIME]);
+	seq_printf(s, "%-22s  OFDM_MDRDY = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME]);
+	seq_printf(s, "%-22s  OFDM_GREEN_MDRDY = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME]);
+	seq_printf(s, "%-22s  Prim CCA Time = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_P_CCA_TIME]);
+	seq_printf(s, "%-22s  Sec CCA Time = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_S_CCA_TIME]);
+	seq_printf(s, "%-22s  Prim ED Time = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_P_ED_TIME]);
+
+	/* ===Tx Related Counters(Generic)=== */
+	seq_puts(s, "\n=== Tx Related Counters(Generic) ===\n");
+	seq_printf(s, "%-22s  BeaconTxCnt = %llu\n",
+		   " ", mib_values[QUERY_MIB_CNT_BCN_TX]);
+	seq_printf(s, "%-22s  Tx 40MHz Cnt = %llu  (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_TX_BW_40MHZ]);
+	seq_printf(s, "%-22s  Tx 80MHz Cnt = %llu  (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_TX_BW_80MHZ]);
+	seq_printf(s, "%-22s  Tx 160MHz Cnt = %llu  (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_TX_BW_160MHZ]);
+
+	/* ===BSSID[0] Related Counters=== */
+	seq_puts(s, "\n=== BSSID[0] Related Counters ===\n");
+	seq_printf(s, "%-22s  BA Miss Cnt = %llu  (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_BA_MISS]);
+	seq_printf(s, "%-22s  RTS Tx Cnt = %llu  (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_TX_CNT]);
+	seq_printf(s, "%-22s  Frame Retry Cnt = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY]);
+	seq_printf(s, "%-22s  Frame Retry 2 Cnt = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_FRAME_RETRY_2]);
+	seq_printf(s, "%-22s  RTS Retry Cnt = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_RTS_RETRY]);
+	seq_printf(s, "%-22s  Ack Failed Cnt = %llu (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_BSS0_ACK_FAIL]);
+
+	/* ===AMPDU Related Counters=== */
+	seq_puts(s, "\n=== AMPDU Related Counters ===\n");
+	seq_printf(s, "%-22s  Tx AMPDU_Pkt_Cnt = %llu  (PPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_AMPDU]);
+	seq_printf(s, "%-22s  Tx AMPDU_MPDU_Pkt_Cnt = %llu  (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_AMPDU_MPDU]);
+	seq_printf(s, "%-22s  AMPDU Tx success = %llu (MPDU)\n",
+		   " ", mib_values[QUERY_MIB_CNT_AMPDU_ACKED]);
+
+	tx_total = mib_values[QUERY_MIB_CNT_AMPDU_MPDU];
+	tx_fail =  mib_values[QUERY_MIB_CNT_AMPDU_MPDU] - mib_values[QUERY_MIB_CNT_AMPDU_ACKED];
+	tx_per = tx_total == 0 ? 0 : div64_u64(1000 * tx_fail, tx_total);
+	seq_printf(s, "%-22s  AMPDU Tx fail count   = %llu  (MPDU), PER=%llu.%1llu%%\n",
+		   " ",
+		   tx_fail,
+		   div_u64_rem(tx_per, 10, &tx_per_rem), (u64)tx_per_rem);
+
+out:
+	kfree(stats);
+	return ret;
+}
+
 int mt7925_init_debugfs(struct mt792x_dev *dev)
 {
 	struct dentry *dir;
@@ -313,5 +656,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
 
+	debugfs_create_devm_seqfile(dev->mt76.dev, "stats", dir,
+				    mt792x_stats);
+
 	return 0;
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.c b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
new file mode 100644
index 0000000000000..9a47953062bc3
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include "mt7925.h"
+#include "mcu.h"
+#include "mac.h"
+#include "stats.h"
+
+#define MT7925_STA_STATS_VERSION 1
+#define MT7925_STA_STATS_VALID 1U
+
+#define MT7925_MIB_MAX_CNT			32
+#define MT7925_MIB_CNT_MAX_NUM		512  /* firmware UNI_CMD_MIB_CNT_MAX_NUM upper bound */
+
+#define MT7925_LQ_BAND_NUM	4			/* Link quality band */
+
+const char * const mt7925_tx_mode_str[] = {
+	"CCK", "OFDM", "MM", "GF", "VHT", "PLR",
+	"PLRP", "ALR", "HE_SU", "HE_ER", "HE_TRIG", "HE_MU",
+	"EHT_ER", "TOF", "EHT_TRIG", "EHT_MU"
+};
+
+const char * const mt7925_tx_rate_cck_str[] = {
+	"1M", "2M", "5.5M", "11M", "N/A"
+};
+
+const char * const mt7925_tx_rate_bw_str[] = {
+	"BW20", "BW40", "BW80", "BW160/BW8080", "BW320", "N/A"
+};
+
+struct mt7925_sta_stats_req {
+	u8 rsv[4];
+	__le16 tag;
+	__le16 len;
+	u8 sta_rec_idx;
+	u8 rsv1;
+	u8 lls_read_clear;
+	u8 reset_counter;
+} __packed;
+
+struct mt7925_sta_stats_event {
+	u8 rsv[4];
+	__le16 tag;
+	__le16 len;
+	struct mt7925_sta_stats stats;
+} __packed;
+
+/* WTBL auto-rate request TLV, mirror Gen4m struct UNI_CMD_STAT_WTBL.
+ * The RA tag reuses UNI_CMD_ID_STATISTICS_WTBL (10).
+ */
+struct mt7925_wtbl_rate_req {
+	u8 rsv[4];
+	__le16 tag;
+	__le16 len;
+	__le16 wlan_idx;
+	__le16 rsv1;
+} __packed;
+
+/* WTBL auto-rate event TLV. Firmware answers cmd tag 10 with event tag 12. */
+struct mt7925_wtbl_rate_event {
+	u8 rsv[4];
+	struct mt7925_wtbl_rate rate;
+} __packed;
+
+/* fixed field, mirror UNI_(CMD|EVENT)_MIB_INFO */
+struct mt7925_mib_hdr {
+	u8 band_idx;
+	u8 rsv[3];
+} __packed;
+
+/* One requested/returned MIB counter, mirror UNI_(CMD|EVENT)_MIB_DATA_T. */
+struct mt7925_mib_data {
+	__le16 tag;	/* UNI_(CMD|EVENT)_MIB_DATA = 0 */
+	__le16 len;
+	__le32 counter;	/* MIB counter ID (enum mt7925_mib_counter) */
+	__le64 data;	/* only valid on event; ignored on cmd */
+} __packed;
+
+/* per-band link quality entry, mirror UNI_LINK_QUALITY */
+struct mt7925_link_quality {
+	s8 rssi;		/* moving-average beacon RSSI of connected AP */
+	s8 link_quality;	/* always 0 */
+	__le16 link_speed;	/* TX rate1, unit 500 Kb/s */
+	u8 medium_busy_pct;	/* always 0 */
+	u8 is_ready;		/* 0: fields invalid, 1: valid */
+	u8 rsv[2];
+} __packed;
+
+/* request TLV, mirror UNI_CMD_LINK_QUALITY (header + tag/len only) */
+struct mt7925_lq_req {
+	u8 rsv[4];		/* UNI_CMD_GET_STATISTICS fixed field */
+	__le16 tag;		/* UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1 */
+	__le16 len;
+} __packed;
+
+/* event TLV, mirror UNI_EVENT_LINK_QUALITY */
+struct mt7925_lq_event {
+	u8 rsv[4];		/* UNI_EVENT_STATISTICS fixed field */
+	__le16 tag;		/* UNI_EVENT_ID_STATISTICS_LINK_QUALITY = 1 */
+	__le16 len;
+	struct mt7925_link_quality lq[MT7925_LQ_BAND_NUM];
+} __packed;
+
+static void
+mt7925_get_ap_sta_rec_idx_iter(void *data, u8 *mac,
+			       struct ieee80211_vif *vif)
+{
+	struct mt7925_sta_rec_lookup *lookup = data;
+	struct mt792x_vif *mvif;
+	struct mt792x_sta *msta;
+
+	if (lookup->sta_rec_idx >= 0)
+		return;
+
+	if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+		return;
+
+	mvif = (struct mt792x_vif *)vif->drv_priv;
+	msta = mvif->wep_sta;
+	if (!msta)
+		return;
+
+	if (!msta->deflink.wcid.sta)
+		return;
+
+	lookup->sta_rec_idx = msta->deflink.wcid.idx;
+}
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx)
+{
+	struct mt7925_sta_rec_lookup lookup = {
+		.sta_rec_idx = -1,
+	};
+
+	lockdep_assert_held(&dev->mt76.mutex);
+
+	ieee80211_iterate_active_interfaces(dev->mphy.hw,
+					    IEEE80211_IFACE_ITER_RESUME_ALL,
+					    mt7925_get_ap_sta_rec_idx_iter,
+					    &lookup);
+
+	if (lookup.sta_rec_idx < 0)
+		return -ENOTCONN;
+
+	if (lookup.sta_rec_idx > U8_MAX)
+		return -ERANGE;
+
+	*sta_rec_idx = lookup.sta_rec_idx;
+
+	return 0;
+}
+
+/* Decode the TX Vector BBP latch and print the last TX rate */
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3])
+{
+	const char *mode_str = "N/A";
+	u8 rate, txmode, frmode, sgi, ldpc, nsts, stbc, spe, dcm, ersu106t;
+	s8 txpwr, pos_txpwr;
+	u32 txv[3];
+
+	txv[0] = le32_to_cpu(le_txv[0]);
+	txv[1] = le32_to_cpu(le_txv[1]);
+	txv[2] = le32_to_cpu(le_txv[2]);
+
+	if (txv[0] == 0xffffffff) {
+		seq_printf(s, "%-22s  %s = %s\n", " ", "Last TX Rate", "N/A");
+		seq_printf(s, "%-22s  %s = %s\n",
+			   " ", "Chip Out TX Power", "N/A");
+		return;
+	}
+
+	rate = MT7925_TXV_GET_TX_RATE(txv);
+	txmode = MT7925_TXV_GET_TX_MODE(txv);
+	frmode = MT7925_TXV_GET_TX_FRMODE(txv);
+	nsts = MT7925_TXV_GET_TX_NSTS(txv) + 1;
+	sgi = MT7925_TXV_GET_TX_SGI(txv);
+	ldpc = MT7925_TXV_GET_TX_LDPC(txv);
+	stbc = MT7925_TXV_GET_TX_STBC(txv);
+	txpwr = MT7925_TXV_GET_TX_PWR(txv);
+	spe = MT7925_TXV_GET_TX_SPE_IDX(txv);
+	dcm = MT7925_TXV_GET_TX_DCM(txv);
+	ersu106t = MT7925_TXV_GET_TX_106T(txv);
+
+	if (dcm)
+		rate = MT7925_TXV_RATE_UNMASK_DCM(rate);
+	if (ersu106t)
+		rate = MT7925_TXV_RATE_UNMASK_106T(rate);
+
+	if (txmode < ARRAY_SIZE(mt7925_tx_mode_str))
+		mode_str = mt7925_tx_mode_str[txmode];
+
+	seq_printf(s, "%-22s  %s = ", " ", "Last TX Rate");
+
+	/* rate/MCS field */
+	if (txmode == MT7925_TX_RATE_MODE_CCK)
+		seq_printf(s, "%s, ",
+			   rate < 4 ? mt7925_tx_rate_cck_str[rate] :
+			   (((rate >= 5) && (rate <= 7)) ?
+			    mt7925_tx_rate_cck_str[rate - 4] :
+			    mt7925_tx_rate_cck_str[4]));
+	else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+		seq_printf(s, "%s, ", mt7925_hw_rate_ofdm_str(rate));
+	else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+		 txmode == MT7925_TX_RATE_MODE_HTGF)
+		seq_printf(s, "MCS%d, ", rate);
+	else
+		seq_printf(s, "%s%d_MCS%d, ",
+			   stbc ? "NSTS" : "NSS", nsts, rate);
+
+	/* bandwidth */
+	seq_printf(s, "%s, ",
+		   frmode < 5 ? mt7925_tx_rate_bw_str[frmode] :
+		   mt7925_tx_rate_bw_str[5]);
+
+	/* GI / preamble */
+	if (txmode == MT7925_TX_RATE_MODE_CCK)
+		seq_printf(s, "%s, ", rate < 4 ? "LP" : "SP");
+	else if (txmode == MT7925_TX_RATE_MODE_OFDM)
+		; /* OFDM has no GI/preamble to print */
+	else if (txmode == MT7925_TX_RATE_MODE_HTMIX ||
+		 txmode == MT7925_TX_RATE_MODE_HTGF ||
+		 txmode == MT7925_TX_RATE_MODE_VHT ||
+		 txmode == MT7925_TX_RATE_MODE_PLR)
+		seq_printf(s, "%s, ", sgi == 0 ? "LGI" : "SGI");
+	else
+		seq_printf(s, "%s, ",
+			   sgi == 0 ? "SGI" : (sgi == 1 ? "MGI" : "LGI"));
+
+	/* mode, DCM, 106t, STBC, coding, SPE */
+	seq_printf(s, "%s%s%s%s%s%s%d\n",
+		   mode_str,
+		   dcm ? ", DCM" : "", ersu106t ? ", 106t" : "",
+		   stbc ? ", STBC, " : ", ", ldpc == 0 ? "BCC" : "LDPC",
+		   ", SPE", spe);
+
+	/* chip out TX power (unit: 0.5 dBm) */
+	pos_txpwr = (txpwr < 0) ? (~txpwr + 1) : txpwr;
+	seq_printf(s, "%-22s  %s = %c%d.%1d dBm\n",
+		   " ", "Chip Out TX Power",
+		   (txpwr < 0) ? '-' : '+',
+		   (pos_txpwr / 2), 5 * (pos_txpwr % 2));
+}
+
+/* Guard interval used for the auto-rate entries, mirror Gen4m
+ * connac3x_get_sgi_info_from_fw(): pick the per-mode/per-BW flag.
+ */
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap)
+{
+	bool he = (txmode == MT7925_TX_RATE_MODE_HE_SU ||
+		   txmode == MT7925_TX_RATE_MODE_HE_ER ||
+		   txmode == MT7925_TX_RATE_MODE_HE_TRIG ||
+		   txmode == MT7925_TX_RATE_MODE_HE_MU);
+	bool eht = (txmode == MT7925_TX_RATE_MODE_EHT_ER ||
+		    txmode == MT7925_TX_RATE_MODE_EHT_TRIG ||
+		    txmode == MT7925_TX_RATE_MODE_EHT_MU);
+
+	if (eht)
+		return wr->gi_eht;
+
+	switch (fcap) {
+	case MT7925_BW_20:
+		return he ? wr->g2_he : wr->g2;
+	case MT7925_BW_40:
+		return he ? wr->g4_he : wr->g4;
+	case MT7925_BW_80:
+		return he ? wr->g8_he : wr->g8;
+	case MT7925_BW_160:
+		return he ? wr->g16_he : wr->g16;
+	default:
+		return 0;
+	}
+}
+
+/* Coding used for the auto-rate entries, mirror Gen4m
+ * connac3x_wtbl_get_ldpc_info_from_fw(): 0 = BCC, 1 = LDPC.
+ */
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode)
+{
+	switch (txmode) {
+	case MT7925_TX_RATE_MODE_HTMIX:
+	case MT7925_TX_RATE_MODE_HTGF:
+		return wr->ht_ldpc;
+	case MT7925_TX_RATE_MODE_VHT:
+		return wr->vht_ldpc;
+	case MT7925_TX_RATE_MODE_HE_SU:
+	case MT7925_TX_RATE_MODE_HE_ER:
+	case MT7925_TX_RATE_MODE_HE_TRIG:
+	case MT7925_TX_RATE_MODE_HE_MU:
+		return wr->he_ldpc;
+	case MT7925_TX_RATE_MODE_EHT_ER:
+	case MT7925_TX_RATE_MODE_EHT_TRIG:
+	case MT7925_TX_RATE_MODE_EHT_MU:
+		return wr->eht_ldpc;
+	default:
+		return 0;
+	}
+}
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+			struct mt7925_sta_stats *stats)
+{
+	u16 len = 0;
+	int ret = 0;
+	struct mt7925_sta_stats_event *event = NULL;
+	struct sk_buff *skb = NULL;
+	struct mt7925_sta_stats_req req = {
+		.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_STA),
+		.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+		.lls_read_clear = false,
+		.reset_counter = false,
+	};
+
+	ret = mt7925_get_ap_sta_rec_idx(dev, &req.sta_rec_idx);
+	if (ret)
+		return ret;
+
+	if (!stats)
+		return -EINVAL;
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+					MCU_UNI_QUERY(GET_STAT_INFO),
+					&req, sizeof(req), true, &skb);
+
+	if (ret)
+		return ret;
+
+	event = (struct mt7925_sta_stats_event *)skb->data;
+
+	if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_STA) {
+		ret = -EPROTO;
+		goto out;
+	}
+
+	len = le16_to_cpu(event->len);
+	if (len < sizeof(*event) - sizeof(event->rsv) ||
+	    len > skb->len - sizeof(event->rsv) || !IS_ALIGNED(len, 4)) {
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	if (event->stats.version != MT7925_STA_STATS_VERSION) {
+		ret = -EPROTO;
+		goto out;
+	}
+
+	if (!(le32_to_cpu(event->stats.flags) & MT7925_STA_STATS_VALID)) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	memcpy(stats, &event->stats, sizeof(*stats));
+
+out:
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+			    const u32 *counters, u8 counter_num,
+			    u64 *values)
+{
+	u8 *req;
+	u8 *tlv, *tlv_end;
+	u16 req_len;
+	int i, ret;
+	struct mt7925_mib_hdr *req_hdr;
+	struct mt7925_mib_data *req_tlv;
+	struct sk_buff *skb;
+
+	if (!counters || !values || !counter_num ||
+	    counter_num > MT7925_MIB_MAX_CNT)
+		return -EINVAL;
+
+	/* clear output; counters not returned by firmware stay 0 */
+	memset(values, 0, counter_num * sizeof(*values));
+
+	req_len = sizeof(struct mt7925_mib_hdr) +
+		  counter_num * sizeof(struct mt7925_mib_data);
+
+	req = kzalloc(req_len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req_hdr = (struct mt7925_mib_hdr *)req;
+	req_hdr->band_idx = band_idx;
+
+	req_tlv = (struct mt7925_mib_data *)(req + sizeof(*req_hdr));
+	for (i = 0; i < counter_num; i++) {
+		req_tlv[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+		req_tlv[i].len = cpu_to_le16(sizeof(struct mt7925_mib_data));
+		req_tlv[i].counter = cpu_to_le32(counters[i]);
+	}
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+					MCU_UNI_QUERY(GET_MIB_INFO), req,
+					req_len, true, &skb);
+	kfree(req);
+	if (ret)
+		return ret;
+
+	if (skb->len < sizeof(struct mt7925_mib_hdr)) {
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	/* walk the returned TLVs, match each counter ID to the request */
+	tlv = skb->data + sizeof(struct mt7925_mib_hdr);
+	tlv_end = skb->data + skb->len;
+
+	while (tlv + sizeof(struct mt7925_mib_data) <= tlv_end) {
+		struct mt7925_mib_data *evt_tlv =
+			(struct mt7925_mib_data *)tlv;
+		u16 tag = le16_to_cpu(evt_tlv->tag);
+		u16 len = le16_to_cpu(evt_tlv->len);
+		u32 counter;
+
+		if (len < sizeof(struct mt7925_mib_data) ||
+		    tlv + len > tlv_end)
+			break;
+
+		if (tag == UNI_CMD_MIB_DATA) {
+			counter = le32_to_cpu(evt_tlv->counter);
+
+			if (counter < MT7925_MIB_CNT_MAX_NUM) {
+				for (i = 0; i < counter_num; i++) {
+					if (counters[i] == counter) {
+						values[i] =
+						  le64_to_cpu(evt_tlv->data);
+						break;
+					}
+				}
+			}
+		}
+
+		tlv += len;
+	}
+
+	ret = 0;
+
+out:
+	dev_kfree_skb(skb);
+
+	return ret;
+}
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx)
+{
+	char * const ofdm_str[] = {
+		"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M", "N/A"
+	};
+
+	switch (ofdm_idx) {
+	case 11: return ofdm_str[0];	/* 6M */
+	case 15: return ofdm_str[1];	/* 9M */
+	case 10: return ofdm_str[2];	/* 12M */
+	case 14: return ofdm_str[3];	/* 18M */
+	case 9:  return ofdm_str[4];	/* 24M */
+	case 13: return ofdm_str[5];	/* 36M */
+	case 8:  return ofdm_str[6];	/* 48M */
+	case 12: return ofdm_str[7];	/* 54M */
+	default: return ofdm_str[8];
+	}
+}
+
+/* mt792x_mcu_get_wtbl_rate - fetch the firmware auto-rate table for a WCID
+ * @dev:	device
+ * @wlan_idx:	HW WTBL / WCID index (see mt7925_sta_stats.wtbl_idx)
+ * @rate:	out, filled with the auto-rate table snapshot
+ *
+ * Mirror of Gen4m wlanoidGetStatWtbl(): send UNI_CMD_GET_STAT_INFO with the
+ * WTBL rate TLV (cmd tag 10) and parse the reply (event tag 12) into @rate.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+			     struct mt7925_wtbl_rate *rate)
+{
+	int ret;
+	struct mt7925_wtbl_rate_event *event = NULL;
+	struct sk_buff *skb = NULL;
+	struct mt7925_wtbl_rate_req req = {
+		.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_WTBL),
+		.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+		.wlan_idx = cpu_to_le16(wlan_idx),
+	};
+
+	if (!rate)
+		return -EINVAL;
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+					MCU_UNI_QUERY(GET_STAT_INFO),
+					&req, sizeof(req), true, &skb);
+	if (ret)
+		return ret;
+
+	if (skb->len < sizeof(*event)) {
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	event = (struct mt7925_wtbl_rate_event *)skb->data;
+
+	if (le16_to_cpu(event->rate.tag) != MT7925_UNI_EVENT_STATISTICS_WTBL) {
+		ret = -EPROTO;
+		goto out;
+	}
+
+	memcpy(rate, &event->rate, sizeof(*rate));
+	ret = 0;
+
+out:
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+/**
+ * mt7925_mcu_get_link_quality - query RSSI and TX link speed for a band
+ * @dev:	device
+ * @band_idx:	band/BSS index (0..3)
+ * @rssi:	out, moving-average beacon RSSI in dBm (may be NULL)
+ * @tx_link_speed_kbps: out, TX rate in kbit/s (may be NULL)
+ *
+ * Return: 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * Only RSSI and TX rate come from firmware here; for RX rate read wcid->rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+				s8 *rssi, u32 *tx_link_speed_kbps)
+{
+	int ret;
+	struct mt7925_lq_event *event;
+	struct mt7925_link_quality *lq;
+	struct sk_buff *skb;
+	struct mt7925_lq_req req = {
+		.tag = cpu_to_le16(UNI_CMD_ID_STATISTICS_LINK_QUALITY),
+		.len = cpu_to_le16(sizeof(req) - sizeof(req.rsv)),
+	};
+
+	if (band_idx >= MT7925_LQ_BAND_NUM)
+		return -EINVAL;
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+					MCU_UNI_QUERY(GET_STAT_INFO), &req,
+					sizeof(req), true, &skb);
+	if (ret)
+		return ret;
+
+	if (skb->len < sizeof(*event)) {
+		ret = -EMSGSIZE;
+		goto out;
+	}
+
+	event = (struct mt7925_lq_event *)skb->data;
+
+	if (le16_to_cpu(event->tag) != UNI_CMD_ID_STATISTICS_LINK_QUALITY) {
+		ret = -EPROTO;
+		goto out;
+	}
+
+	lq = &event->lq[band_idx];
+	if (!lq->is_ready) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (rssi)
+		*rssi = lq->rssi;
+
+	/* firmware reports TX rate in 500 Kb/s units -> kbit/s */
+	if (tx_link_speed_kbps)
+		*tx_link_speed_kbps = le16_to_cpu(lq->link_speed) * 500;
+
+	ret = 0;
+
+out:
+	dev_kfree_skb(skb);
+
+	return ret;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/stats.h b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
new file mode 100644
index 0000000000000..a70b9eb3aa806
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/stats.h
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __MT7925_STATS_H
+#define __MT7925_STATS_H
+
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include "../mt76_connac_mcu.h"
+
+#define MT7925_STA_STATS_AC_NUM 4
+#define MT7925_STA_STATS_BAND_NUM 2
+#define MT7925_STA_STATS_AGG_RANGE_NUM 16
+#define MT7925_STA_STATS_TX_QUALITY_NUM 4
+#define MT7925_TX_MODE_NUM      16
+#define MT7925_TX_RATE_CCK_NUM  5
+#define MT7925_TX_RATE_BW_NUM   6
+
+/* HW rate code (16-bit) field extract, mirror Gen4m CONNAC3X_HW_TX_RATE_TO_*.
+ * The auto-rate table stores one rate code per entry.
+ */
+#define MT7925_HW_RATE_TO_MCS(_x)	(((_x) & GENMASK(5, 0)) >> 0)
+#define MT7925_HW_RATE_TO_MODE(_x)	(((_x) & GENMASK(9, 6)) >> 6)
+#define MT7925_HW_RATE_TO_NSS(_x)	(((_x) & GENMASK(13, 10)) >> 10)
+#define MT7925_HW_RATE_TO_STBC(_x)	(((_x) & BIT(14)) >> 14)
+#define MT7925_HW_RATE_TO_DCM(_x)	(((_x) & BIT(4)) >> 4)
+#define MT7925_HW_RATE_TO_106T(_x)	(((_x) & BIT(5)) >> 5)
+#define MT7925_HW_RATE_UNMASK_DCM(_r)	((u8)(_r) & 0xef)
+#define MT7925_HW_RATE_UNMASK_106T(_r)	((u8)(_r) & 0xdf)
+
+/* Number of entries in the firmware auto-rate table (mirror Gen4m
+ * AUTO_RATE_NUM). RA cycles through these and reports which one is in use
+ * via wtbl->rate_idx.
+ */
+#define MT7925_AUTO_RATE_NUM 8
+
+/* Firmware EVENT_STATISTICS sub-event tags. The WTBL rate request uses cmd tag
+ * UNI_CMD_ID_STATISTICS_WTBL (10) but the firmware replies with event tag 12.
+ */
+#define MT7925_UNI_EVENT_STATISTICS_WTBL 12
+
+/* CONNAC3X TX Vector BBP latch bit fields (mirror cmm_asic_connac3x.h).
+ * These operate on the 3-DWORD tx_vector[band].txv[3] array.
+ */
+#define MT7925_TXV_GET_TX_RATE(_txv)	((_txv)[2] & 0x7f)
+#define MT7925_TXV_GET_TX_LDPC(_txv)	(((_txv)[2] & (0x1 << 7)) >> 7)
+#define MT7925_TXV_GET_TX_STBC(_txv)	(((_txv)[0] & (0x3 << 6)) >> 6)
+#define MT7925_TXV_GET_TX_FRMODE(_txv)	(((_txv)[0] & (0x7 << 8)) >> 8)
+#define MT7925_TXV_GET_TX_MODE(_txv)	(((_txv)[0] & (0xf << 12)) >> 12)
+#define MT7925_TXV_GET_TX_NSTS(_txv)	(((_txv)[2] & (0xfU << 28)) >> 28)
+#define MT7925_TXV_GET_TX_PWR(_txv)	(((_txv)[0] & (0xff << 16)) >> 16)
+#define MT7925_TXV_GET_TX_SGI(_txv)	(((_txv)[1] & (0x3 << 26)) >> 26)
+#define MT7925_TXV_GET_TX_SPE_IDX(_txv)	(((_txv)[0] & (0x1f << 0)) >> 0)
+#define MT7925_TXV_GET_TX_DCM(_txv)	(((_txv)[2] & (0x1 << 4)) >> 4)
+#define MT7925_TXV_GET_TX_106T(_txv)	(((_txv)[2] & (0x1 << 5)) >> 5)
+#define MT7925_TXV_RATE_UNMASK_DCM(_r)	((u8)(_r) & 0xef)
+#define MT7925_TXV_RATE_UNMASK_106T(_r)	((u8)(_r) & 0xdf)
+
+extern const char * const mt7925_tx_mode_str[MT7925_TX_MODE_NUM];
+extern const char * const mt7925_tx_rate_cck_str[MT7925_TX_RATE_CCK_NUM];
+extern const char * const mt7925_tx_rate_bw_str[MT7925_TX_RATE_BW_NUM];
+
+/* TX rate mode, mirror Gen4m TX_RATE_MODE_* (see nic_rate.h). Used to decode
+ * both the TX Vector BBP latch and the firmware auto-rate table rate codes.
+ */
+enum mt7925_tx_rate_mode {
+	MT7925_TX_RATE_MODE_CCK = 0,
+	MT7925_TX_RATE_MODE_OFDM = 1,
+	MT7925_TX_RATE_MODE_HTMIX = 2,
+	MT7925_TX_RATE_MODE_HTGF = 3,
+	MT7925_TX_RATE_MODE_VHT = 4,
+	MT7925_TX_RATE_MODE_PLR = 5,
+	MT7925_TX_RATE_MODE_HE_SU = 8,
+	MT7925_TX_RATE_MODE_HE_ER = 9,
+	MT7925_TX_RATE_MODE_HE_TRIG = 10,
+	MT7925_TX_RATE_MODE_HE_MU = 11,
+	MT7925_TX_RATE_MODE_EHT_ER = 13,
+	MT7925_TX_RATE_MODE_EHT_TRIG = 14,
+	MT7925_TX_RATE_MODE_EHT_MU = 15,
+};
+
+enum {
+	UNI_CMD_MIB_DATA,
+};
+
+enum stats_query_mib_array_index {
+	QUERY_MIB_CNT_RX_FCS_ERR = 0,
+	QUERY_MIB_CNT_RX_FIFO_OVERFLOW,
+	QUERY_MIB_CNT_RX_MPDU,
+	QUERY_MIB_CNT_AMPDU_RX_COUNT,
+	QUERY_MIB_CNT_PF_DROP,
+	QUERY_MIB_CNT_LEN_MISMATCH,
+	QUERY_MIB_CNT_CHANNEL_IDLE,
+	QUERY_MIB_CNT_CCA_NAV_TX_TIME,
+	QUERY_MIB_CNT_MDRDY,
+	QUERY_MIB_CNT_RX_CCK_MDRDY_TIME,
+	QUERY_MIB_CNT_RX_OFDM_LG_MIXED_MDRDY_TIME,
+	QUERY_MIB_CNT_RX_OFDM_GREEN_MDRDY_TIME,
+	QUERY_MIB_CNT_P_CCA_TIME,
+	QUERY_MIB_CNT_S_CCA_TIME,
+	QUERY_MIB_CNT_P_ED_TIME,
+	QUERY_MIB_CNT_BCN_TX,
+	QUERY_MIB_CNT_TX_BW_40MHZ,
+	QUERY_MIB_CNT_TX_BW_80MHZ,
+	QUERY_MIB_CNT_TX_BW_160MHZ,
+	QUERY_MIB_CNT_BSS0_BA_MISS,
+	QUERY_MIB_CNT_BSS0_RTS_TX_CNT,
+	QUERY_MIB_CNT_BSS0_FRAME_RETRY,
+	QUERY_MIB_CNT_BSS0_FRAME_RETRY_2,
+	QUERY_MIB_CNT_BSS0_RTS_RETRY,
+	QUERY_MIB_CNT_BSS0_ACK_FAIL,
+	QUERY_MIB_CNT_AMPDU,
+	QUERY_MIB_CNT_AMPDU_MPDU,
+	QUERY_MIB_CNT_AMPDU_ACKED,
+	QUERY_MIB_CNT_MAX_NUM,
+};
+
+enum stats_trx_agg_range {
+	STAT_MIB_CNT_TRX_AGG_RANGE0_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE1_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE2_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE3_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE4_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE5_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE6_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE7_IDX,
+	STAT_MIB_CNT_TRX_AGG_RANGE_MAX_NUM
+};
+
+enum {
+	MT7925_BW_20 = 0,
+	MT7925_BW_40,
+	MT7925_BW_80,
+	MT7925_BW_160,
+};
+
+struct mt7925_sta_stats_ac {
+	__le32 tx_fail;
+	__le32 tx_retry;
+} __packed;
+
+struct mt7925_sta_stats_tx_vector {
+	__le32 txv[3];
+} __packed;
+
+struct mt7925_sta_stats_mib {
+	__le32 rx_mpdu_cnt;
+	__le32 fcs_error;
+	__le32 rx_fifo_full;
+	__le32 ampdu_tx_sf_cnt;
+	__le32 ampdu_tx_ack_sf_cnt;
+	__le32 tx_range_ampdu_cnt[MT7925_STA_STATS_AGG_RANGE_NUM];
+} __packed;
+
+/* Firmware EVENT_STA_STATISTICS body. */
+struct mt7925_sta_stats {
+	u8 version;
+	u8 rsv[3];
+	__le32 flags;
+
+	u8 sta_idx;
+	u8 bss_idx;
+	u8 wtbl_idx;
+	u8 rsv1;
+
+	u8 mac_addr[ETH_ALEN];
+	u8 per;
+	u8 rcpi;
+
+	__le32 phy_mode;
+	__le16 link_speed; /* 0.5 Mbit/s units */
+	u8 link_quality;
+	u8 link_reserved;
+
+	__le32 tx_count;
+	__le32 tx_fail_count;
+	__le32 tx_life_timeout_count;
+	__le32 tx_done_air_time;
+	__le32 transmit_count;
+	__le32 transmit_fail_count;
+
+	struct mt7925_sta_stats_ac ac[MT7925_STA_STATS_AC_NUM];
+
+	u8 temperature;
+	u8 skip_ar;
+	u8 ar_table_idx;
+	u8 rate_entry_idx;
+	u8 rate_entry_idx_prev;
+	u8 tx_sgi_detect_pass_cnt;
+	u8 ave_per;
+	u8 rsv2;
+
+	__le32 agg_range_ctrl[2];
+	u8 range_mode;
+	u8 rsv3[3];
+	__le32 agg_range_ctrl_ext[6];
+
+	u8 ar_state_curr;
+	u8 ar_state_prev;
+	u8 ar_action_type;
+	u8 highest_rate_cnt;
+	u8 lowest_rate_cnt;
+	u8 rsv4;
+	__le16 train_up;
+	__le16 train_down;
+	u8 rsv5[2];
+	__le32 rate1_tx_cnt;
+	__le32 rate1_fail_cnt;
+
+	struct mt7925_sta_stats_tx_vector tx_vector[MT7925_STA_STATS_BAND_NUM];
+	struct mt7925_sta_stats_mib mib[MT7925_STA_STATS_BAND_NUM];
+
+	u8 is_force_tx_stream;
+	u8 is_force_se_off;
+	__le16 ra_running_cnt;
+	u8 ra_status;
+	u8 max_ampdu_factor;
+	u8 tx_quality[MT7925_STA_STATS_TX_QUALITY_NUM];
+	u8 tx_rate_up_penalty;
+	u8 low_traffic_mode;
+	u8 low_traffic_count;
+	u8 low_traffic_dashboard;
+	u8 dynamic_sgi_state;
+	u8 dynamic_sgi_score;
+	u8 dynamic_bw_state;
+	u8 dynamic_gband_256qam_state;
+	u8 vht_non_sp_rate_state;
+	u8 rsv6[3];
+	u8 rsv7[2];
+} __packed;
+
+struct mt7925_sta_rec_lookup {
+	int sta_rec_idx;
+};
+
+/* Firmware auto-rate table snapshot, mirror Gen4m struct UNI_EVENT_STAT_WTBL.
+ *
+ * The firmware RA maintains up to MT7925_AUTO_RATE_NUM candidate rates in
+ * rate_code[]. rate_idx tells which entry is currently in use, so a dump can
+ * point at it with "-->". fcap is the bandwidth capability and the ldpc/sgi
+ * flags describe the per-mode coding/guard-interval used for those rates.
+ */
+struct mt7925_wtbl_rate {
+	__le16 tag;
+	__le16 len;
+	u8 rx_rcpi0;
+	u8 rx_rcpi1;
+	u8 cbrn;
+	u8 fcap;
+	u8 spe_idx;
+	u8 g2;			/* SGI enabled, non-HE, BW20 */
+	u8 g4;			/* SGI enabled, non-HE, BW40 */
+	u8 g8;			/* SGI enabled, non-HE, BW80 */
+	u8 g16;			/* SGI enabled, non-HE, BW160 */
+	u8 g2_he;		/* GI, HE, BW20 */
+	u8 g4_he;		/* GI, HE, BW40 */
+	u8 g8_he;		/* GI, HE, BW80 */
+	u8 g16_he;		/* GI, HE, BW160 */
+	u8 gi_eht;		/* GI, EHT */
+	u8 ht_ldpc;
+	u8 vht_ldpc;
+	u8 he_ldpc;
+	u8 eht_ldpc;
+	u8 af;
+	u8 rate_idx;		/* index into rate_code[] currently in use */
+	__le16 rate_code[MT7925_AUTO_RATE_NUM];
+	__le32 rsv[4];
+} __packed;
+
+int
+mt7925_get_ap_sta_rec_idx(struct mt792x_dev *dev, u8 *sta_rec_idx);
+
+void
+mt7925_print_last_tx_rate(struct seq_file *s, const __le32 le_txv[3]);
+
+u8
+mt7925_wtbl_get_sgi(const struct mt7925_wtbl_rate *wr, u8 txmode, u8 fcap);
+
+u8
+mt7925_wtbl_get_ldpc(const struct mt7925_wtbl_rate *wr, u8 txmode);
+
+char *mt7925_hw_rate_ofdm_str(u8 ofdm_idx);
+
+int mt792x_mcu_get_wtbl_rate(struct mt792x_dev *dev, u16 wlan_idx,
+			     struct mt7925_wtbl_rate *rate);
+
+int mt792x_mcu_get_stat(struct mt792x_dev *dev,
+			struct mt7925_sta_stats *stats);
+
+int mt7925_mcu_get_mib_info(struct mt792x_dev *dev, u8 band_idx,
+			    const u32 *counters, u8 counter_num,
+			    u64 *values);
+
+/* GET_STAT_INFO (UNI_CMD_ID_GET_STATISTICS = 0x23),
+ * sub-command UNI_CMD_GET_STATISTICS_TAG_LINK_QUALITY = 1.
+ *
+ * @band_idx:            band/BSS index (0..3)
+ * @rssi:                out, moving-average beacon RSSI (dBm), may be NULL
+ * @tx_link_speed_kbps:  out, TX link speed in kbit/s, may be NULL
+ *
+ * Returns 0 on success, -ENOENT if the band's link quality is not ready.
+ *
+ * NOTE: firmware provides only RSSI and TX rate here. RX rate/bandwidth are
+ * not in this event; read wcid->rate for the RX rate.
+ */
+int mt7925_mcu_get_link_quality(struct mt792x_dev *dev, u8 band_idx,
+				s8 *rssi, u32 *tx_link_speed_kbps);
+
+#endif

3/2 2026/08/27 09:12 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26042
							output: 67
							thoughts: 1244
						

Thoughts:
**Analyzing the Debugfs Patch for Fuzzing Potential**

Okay, so I've got this patch for the `mt7925` wireless driver. My initial thought is to assess its impact on the driver's fuzzability. The patch adds a new debugfs file, `stats`, for the `mt7925` driver. It includes `stats.c`, `stats.h` and modifies `debugfs.c` to register the file and also adds some new enums in `mt76_connac_mcu.h`.

My first instinct is to see if this introduces any new functional code paths that could benefit from fuzzing. The `stats` debugfs file seems to be reading statistics from the firmware via MCU commands. Debugfs files are generally low-priority for fuzzing, unless the parsing or state change is complex, but here it's just a simple query-and-print situation.

Let's dig a little deeper. From inspecting `mt7925/debugfs.c`, the patch creates this debugfs file using `debugfs_create_devm_seqfile` and associates it with `mt792x_stats`. Right, so it's a simple debugfs file. All the actual work happens within `mt792x_stats`, which itself calls various functions defined in `stats.c`. Looking at those functions – `mt7925_get_ap_sta_rec_idx`, `mt7925_print_last_tx_rate`, and so on – it's clear they're all related to gathering and printing these stats.

Are any of these functions *exposed* or used outside of the debugfs entry? I need to make sure. Nope, they're all only called from within `mt792x_stats`. I need to ensure there are no changes to any of these routines in other patches in the series. But from the patch itself, it looks like `mt7925_mcu_get_link_quality` is not used anywhere else in this patch.

So, it's *just* adding a debugfs file. Even though it communicates with the firmware via MCU commands, the debugfs interface isn't changing how the core driver *functions*. Now I have to consider the prompt for my analysis. It says to return `WorthFuzzing=false` if the patch only contains things like:

*   Modifications to documentation, Kconfig, or comments
*   Logging (printk, pr_err) or tracepoints
*   Constant/macro changes that don't alter execution flow
*   Unreachable code paths in virtualized environments

Debugfs additions for statistics fall under the "purely decorative" category. They don't change how the driver *operates*. So from the prompt, I'm leaning toward `WorthFuzzing=false`.

Also, I have to consider that `mt7925` is a MediaTek Wi-Fi 7 driver (PCIe/USB). While mac80211_hwsim *simulates* Wi-Fi, it doesn't emulate the specific hardware. This driver needs real hardware or an emulator. But, wait, is `mt7925u` a USB driver? Since the prompt gives USB a pass, can syzkaller fuzz the `mt7925u` driver via `vhci`? Syzkaller has USB fuzzing capabilities via dummy\_hcd/vhci. I'm going to set aside the PCIe vs USB discussion, because the patch only adds a read function to debugfs.

This patch is essentially a diagnostic tool. It doesn't modify the driver's functional behavior in any way. I'm confident in returning `WorthFuzzing=false`. I can now use the `set-results` tool and provide that reasoning.



4/2 2026/08/27 09:12 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a debugfs entry to query and print statistics from the firmware. It is a diagnostic/logging change that does not functionally alter the driver's execution flow.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a debugfs entry to query and print statistics from the firmware. It is a diagnostic/logging change that does not functionally alter the driver's execution flow.",
  "WorthFuzzing": false
}

5/1 2026/08/27 09:12 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)