[PATCH] wifi: mt76: mt7925: add coex_info debugfs for WiFi/BT coex

JB Tsai <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.infradead.lists.linux-mediatek
Message-ID <[email protected]>
From: Vick Cheng <[email protected]>

This is FW MCU CMD API.
Add mt7925_mcu_chip_config_query() which sends the ASCII command as a
UNI query, walks the reply TLVs and copies the payload of the
UNI_CHIP_CONFIG_CHIP_CFG entry back to the caller along with its
response type. Expose it through a read-only 'coex_info' debugfs entry
that queries the firmware on every read, printing the reply as text or
as a hexdump depending on the reported type.

MCU_UNI_QUERY(CHIP_CONFIG) also needs the MCU_CMD_ACK option cleared in
mt7925_mcu_fill_message(), same as the set variant.

This gives a way to read the live WiFi/BT coex state on an upstream
kernel without a vendor tool. The entry is read-only and only issues the
command when read, so it costs nothing when unused, and the
mt7925_mcu_fill_message() change only matches the new query opcode, so
no existing command path changes behaviour.

Signed-off-by: Vick Cheng <[email protected]>
---
 .../wireless/mediatek/mt76/mt7925/debugfs.c   | 30 +++++++
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   | 78 ++++++++++++++++++-
 .../net/wireless/mediatek/mt76/mt7925/mcu.h   |  9 +++
 .../wireless/mediatek/mt76/mt7925/mt7925.h    |  2 +
 4 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index d01ff49de47a..9a5ea12b76c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -286,6 +286,34 @@ static int mt7925_chip_reset(void *data, u64 val)
 
 DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n");
 
+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;
+
+	mt792x_mutex_acquire(dev);
+	ret = mt7925_mcu_chip_config_query(dev, "coexBwcGetModeInfo 0",
+					   &resp_type, resp, sizeof(resp));
+	mt792x_mutex_release(dev);
+
+	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);
+
+	return 0;
+}
+
 int mt7925_init_debugfs(struct mt792x_dev *dev)
 {
 	struct dentry *dir;
@@ -309,6 +337,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 	debugfs_create_file("idle-timeout", 0600, dir, dev,
 			    &fops_pm_idle_timeout);
 	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_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
 				    mt792x_pm_stats);
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index fa29c486a455..e6316cea1865 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1160,6 +1160,81 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd)
 				 &req, sizeof(req), false);
 }
 
+int mt7925_mcu_chip_config_query(struct mt792x_dev *dev, const char *cmd,
+				 u8 *resp_type, void *resp, u16 resp_size)
+{
+	u16 len = strlen(cmd);
+	struct {
+		u8 _rsv[4];
+		__le16 tag;
+		__le16 len;
+		struct mt76_connac_config config;
+	} __packed req = {
+		.tag = cpu_to_le16(UNI_CHIP_CONFIG_CHIP_CFG),
+		.len = cpu_to_le16(sizeof(req) - 4),
+		.config = {
+			.resp_type = 0,
+			.type = CHIP_CONFIG_TYPE_ASCII,
+			.data_size = cpu_to_le16(len),
+		},
+	};
+	const u16 hdr_len = sizeof(struct tlv) +
+			    offsetof(struct mt76_connac_config, data);
+	struct mt76_connac_config *cfg;
+	struct sk_buff *skb;
+	int ret;
+
+	if (!len || len >= sizeof(req.config.data))
+		return -EINVAL;
+
+	memcpy(req.config.data, cmd, len);
+
+	ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(CHIP_CONFIG),
+					&req, sizeof(req), true, &skb);
+	if (ret)
+		return ret;
+
+	/* skip the fixed field of the event, the TLVs follow it */
+	if (skb->len < 4) {
+		ret = -EINVAL;
+		goto out;
+	}
+	skb_pull(skb, 4);
+
+	ret = -ENOENT;
+	while (skb->len >= sizeof(struct tlv)) {
+		struct tlv *tlv = (struct tlv *)skb->data;
+		u16 tlv_len = le16_to_cpu(tlv->len);
+		u16 data_size;
+
+		if (tlv_len < sizeof(*tlv) || tlv_len > skb->len)
+			break;
+
+		if (le16_to_cpu(tlv->tag) != UNI_CHIP_CONFIG_CHIP_CFG) {
+			skb_pull(skb, tlv_len);
+			continue;
+		}
+
+		if (tlv_len < hdr_len)
+			break;
+
+		cfg = (struct mt76_connac_config *)tlv->data;
+		data_size = le16_to_cpu(cfg->data_size);
+		if (data_size > tlv_len - hdr_len)
+			break;
+
+		*resp_type = cfg->resp_type;
+		ret = min_t(u16, data_size, resp_size);
+		memcpy(resp, cfg->data, ret);
+		break;
+	}
+
+out:
+	dev_kfree_skb(skb);
+
+	return ret;
+}
+
 int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable)
 {
 	char cmd[16];
@@ -3772,7 +3847,8 @@ int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
 			uni_txd->option = MCU_CMD_UNI_EXT_ACK;
 
 		if (cmd == MCU_UNI_CMD(HIF_CTRL) ||
-		    cmd == MCU_UNI_CMD(CHIP_CONFIG))
+		    cmd == MCU_UNI_CMD(CHIP_CONFIG) ||
+		    cmd == MCU_UNI_QUERY(CHIP_CONFIG))
 			uni_txd->option &= ~MCU_CMD_ACK;
 
 		if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL ||
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index 11f9eac13ffc..2cf3971d9853 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -119,6 +119,15 @@ enum {
 	UNI_CHIP_CONFIG_NIC_CAPA = 0x3,
 };
 
+/* type/resp_type of struct mt76_connac_config */
+enum {
+	CHIP_CONFIG_TYPE_WO_RESPONSE,
+	CHIP_CONFIG_TYPE_MEM8,
+	CHIP_CONFIG_TYPE_MEM32,
+	CHIP_CONFIG_TYPE_ASCII,
+	CHIP_CONFIG_TYPE_BINARY,
+};
+
 enum {
 	UNI_BAND_CONFIG_RADIO_ENABLE,
 	UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..d0db5d8dae48 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -295,6 +295,8 @@ int mt7925_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif,
 int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl);
 void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb);
 int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
+int mt7925_mcu_chip_config_query(struct mt792x_dev *dev, const char *cmd,
+				 u8 *resp_type, void *resp, u16 resp_size);
 int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
 			    u8 bit_op, u32 bit_map);
 
-- 
2.45.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.