From: Vick Cheng 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 --- .../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