[PATCH] wifi: mt76: add set_chip/get_chip debugfs for chip_config

JB Tsai jb.tsai at mediatek.com
Mon Oct 26 20:32:37 PDT 2026


From: "allen.chien" <yung-lun.chien at mediatek.com>

Add set_chip/get_chip debugfs for chip_config in mt76 driver

Signed-off-by: allen.chien <yung-lun.chien at mediatek.com>
---
 .../wireless/mediatek/mt76/mt7925/debugfs.c   | 180 ++++++++++++++++--
 1 file changed, 169 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index 9a5ea12b76c7..060ab59f969d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /* Copyright (C) 2023 MediaTek Inc. */
 
+#include <linux/seq_buf.h>
+
 #include "mt7925.h"
 #include "mcu.h"
 
@@ -286,13 +288,172 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
 
+#define MT7925_CHIP_CONFIG_SIZE	sizeof_field(struct mt76_connac_config, data)
+#define MT7925_CHIP_CONFIG_COLS	16
+/* Max reply is MT7925_CHIP_CONFIG_SIZE bytes. A full binary dump is the
+ * worst form: 16 bytes/row as "%04x: %*ph" = 20 * (6 + 47 + 1) = 1080
+ * chars + NUL, well under the 4x budget below. seq_buf bounds writes.
+ */
+#define MT7925_CHIP_CONFIG_DUMP_SIZE	(MT7925_CHIP_CONFIG_SIZE * 4)
+
+struct mt7925_chip_config_dbg {
+	struct mt792x_dev *dev;
+	int len;
+	char resp[MT7925_CHIP_CONFIG_DUMP_SIZE];
+};
+
+static char *
+mt7925_chip_config_copy_cmd(const char __user *userbuf, size_t count,
+			    char *buf, size_t buf_size)
+{
+	if (!count || count >= buf_size)
+		return ERR_PTR(-EINVAL);
+
+	if (copy_from_user(buf, userbuf, count))
+		return ERR_PTR(-EFAULT);
+
+	buf[count] = '\0';
+	buf = strim(buf);
+
+	return buf[0] ? buf : ERR_PTR(-EINVAL);
+}
+
+/* Format a firmware chip_config reply into buf; returns the number of bytes
+ * written (not counting the NUL seq_buf appends). An empty reply prints
+ * "no reply", an ASCII reply is printed verbatim, and a binary reply is
+ * dumped in MT7925_CHIP_CONFIG_COLS-byte rows. seq_buf tracks the write
+ * offset and stops on overflow, so the caller never has to budget bytes.
+ * Shared by get_chip and coex_info so the formatting logic lives in one
+ * place.
+ */
+static int
+mt7925_chip_config_format(char *buf, size_t size, u8 resp_type,
+			  const u8 *resp, int ret)
+{
+	struct seq_buf s;
+	int i;
+
+	seq_buf_init(&s, buf, size);
+
+	if (!ret)
+		seq_buf_puts(&s, "no reply\n");
+	else if (resp_type == CHIP_CONFIG_TYPE_ASCII)
+		seq_buf_printf(&s, "%.*s\n", ret, resp);
+	else
+		for (i = 0; i < ret; i += MT7925_CHIP_CONFIG_COLS)
+			seq_buf_printf(&s, "%04x: %*ph\n", i,
+				       min_t(int, MT7925_CHIP_CONFIG_COLS,
+					     ret - i), resp + i);
+
+	return seq_buf_used(&s);
+}
+
+static ssize_t
+mt7925_set_chip_write(struct file *file, const char __user *userbuf,
+		      size_t count, loff_t *ppos)
+{
+	struct mt792x_dev *dev = file->private_data;
+	char buf[MT7925_CHIP_CONFIG_SIZE];
+	char *cmd;
+	int ret;
+
+	cmd = mt7925_chip_config_copy_cmd(userbuf, count, buf, sizeof(buf));
+	if (IS_ERR(cmd))
+		return PTR_ERR(cmd);
+
+	mt792x_mutex_acquire(dev);
+	ret = mt7925_mcu_chip_config(dev, cmd);
+	mt792x_mutex_release(dev);
+
+	return ret ? ret : count;
+}
+
+static ssize_t
+mt7925_get_chip_write(struct file *file, const char __user *userbuf,
+		      size_t count, loff_t *ppos)
+{
+	struct mt7925_chip_config_dbg *ctx = file->private_data;
+	u8 resp[MT7925_CHIP_CONFIG_SIZE], resp_type;
+	char buf[MT7925_CHIP_CONFIG_SIZE];
+	int ret;
+	char *cmd;
+
+	cmd = mt7925_chip_config_copy_cmd(userbuf, count, buf, sizeof(buf));
+	if (IS_ERR(cmd))
+		return PTR_ERR(cmd);
+
+	mt792x_mutex_acquire(ctx->dev);
+	ret = mt7925_mcu_chip_config_query(ctx->dev, cmd, &resp_type, resp,
+					   sizeof(resp));
+	mt792x_mutex_release(ctx->dev);
+
+	ctx->len = 0;
+	if (ret < 0)
+		return ret;
+
+	ctx->len = mt7925_chip_config_format(ctx->resp, sizeof(ctx->resp),
+					     resp_type, resp, ret);
+
+	return count;
+}
+
+static ssize_t
+mt7925_get_chip_read(struct file *file, char __user *userbuf, size_t count,
+		     loff_t *ppos)
+{
+	struct mt7925_chip_config_dbg *ctx = file->private_data;
+
+	return simple_read_from_buffer(userbuf, count, ppos, ctx->resp,
+				       ctx->len);
+}
+
+static const struct file_operations fops_set_chip = {
+	.write = mt7925_set_chip_write,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static int mt7925_get_chip_open(struct inode *inode, struct file *file)
+{
+	struct mt792x_dev *dev = inode->i_private;
+	struct mt7925_chip_config_dbg *ctx;
+
+	ctx = kzalloc_obj(*ctx, GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	file->private_data = ctx;
+
+	return 0;
+}
+
+static int mt7925_get_chip_release(struct inode *inode, struct file *file)
+{
+	struct mt7925_chip_config_dbg *ctx = file->private_data;
+
+	kfree(ctx);
+
+	return 0;
+}
+
+static const struct file_operations fops_get_chip = {
+	.read = mt7925_get_chip_read,
+	.write = mt7925_get_chip_write,
+	.open = mt7925_get_chip_open,
+	.release = mt7925_get_chip_release,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static int
 mt7925_coex_info(struct seq_file *s, void *data)
 {
-#define MT7925_CHIP_CONFIG_RESP_SIZE	320
 	struct mt792x_dev *dev = dev_get_drvdata(s->private);
-	u8 resp[MT7925_CHIP_CONFIG_RESP_SIZE], resp_type;
-	int i, ret;
+	char out[MT7925_CHIP_CONFIG_DUMP_SIZE];
+	u8 resp[MT7925_CHIP_CONFIG_SIZE], resp_type;
+	int ret;
 
 	mt792x_mutex_acquire(dev);
 	ret = mt7925_mcu_chip_config_query(dev, "coexBwcGetModeInfo 0",
@@ -302,14 +463,8 @@ mt7925_coex_info(struct seq_file *s, void *data)
 	if (ret < 0)
 		return ret;
 
-	if (!ret)
-		seq_puts(s, "no reply\n");
-	else if (resp_type == CHIP_CONFIG_TYPE_ASCII)
-		seq_printf(s, "%.*s\n", ret, resp);
-	else
-		for (i = 0; i < ret; i += 16)
-			seq_printf(s, "%04x: %*ph\n", i,
-				   min_t(int, 16, ret - i), resp + i);
+	ret = mt7925_chip_config_format(out, sizeof(out), resp_type, resp, ret);
+	seq_printf(s, "%.*s", ret, out);
 
 	return 0;
 }
@@ -339,6 +494,9 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 	debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset);
 	debugfs_create_devm_seqfile(dev->mt76.dev, "coex_info", dir,
 				    mt7925_coex_info);
+	debugfs_create_file("set_chip", 0200, dir, dev, &fops_set_chip);
+	/* pass dev as data: mt7925_get_chip_open() reads it from i_private */
+	debugfs_create_file("get_chip", 0600, dir, dev, &fops_get_chip);
 	debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
-- 
2.45.2




More information about the Linux-mediatek mailing list