[rdkb][common][bsp][Refactor and sync wifi from openwrt]

[Description]
1dacd97b [MAC80211][WiFi6][Misc][Fix patch fail]
7c02334a [MAC80211][WiFi7][Misc][Fix build fail because of mt76 version upgradation]
7a073097 [MAC80211][WiFi7][misc][ensure the first MLD bss is bss[0]]
27e2304c [MAC80211][WiFi6][mt76][Refactor due to atenl change]
1e1eb98e [MAC80211][WiFi6/7][app][Add single wiphy support for atenl & iwpriv wrapper]
d4101c33 [MAC80211][WiFi7][mt76][enable lftp for wifi7 r1 cert]
55f5732f [MAC80211][WiFi7][hostapd][set ctrl_interface for all bss]

[Release-log]

Change-Id: I9cad01561c310576a9e5bdc9f1b8eec3025e51d9
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
new file mode 100644
index 0000000..0748f93
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0081-mtk-wifi-mt76-mt7992-add-support-to-enable-index-FW-.patch
@@ -0,0 +1,628 @@
+From 4b13a975d69ad2c954791dbdfbcb3c7c7a5bf63f Mon Sep 17 00:00:00 2001
+From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+Date: Thu, 11 Jan 2024 08:55:13 +0800
+Subject: [PATCH 081/116] mtk: wifi: mt76: mt7992: add support to enable index
+ FW log for ConsysPlanet
+
+Add support to enable and record index FW log, which is the input for ConsysPlanet, via mt76-test command.
+
+Usage:
+1. Foreground logging
+	1) Start: mt76-test phy0 idxlog
+	2) Stop: Ctrl + C
+2. Background logging
+	1) Start: mt76-test phy0 idxlog &
+	2) Stop: killall mt76-test
+3. Logging with FW Parser
+	1) Start Ethernet recording of FW Parser.
+	2) Start: mt76-test phy0 idxlog <PC's IP Address>
+	3) Stop: Ctrl + C
+	4) Stop FW Parser.
+
+Log Files
+- FW Log: FW text message
+	- Location: /tmp/log/clog_(timestamp)/WIFI_FW_(timestamp).clog
+- Driver Log: log message printed at driver layer
+	- Location: /tmp/log/clog_(timestamp)/WIFI_KERNEL_(timestamp).clog
+
+Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
+---
+ mt7996/debugfs.c  |  90 +++++++++++++++++--
+ mt7996/mac.c      |  10 ++-
+ mt7996/mcu.c      |  34 +++++++-
+ mt7996/mcu.h      |   4 +-
+ mt7996/mt7996.h   |   3 +
+ tools/fwlog.c     | 218 ++++++++++++++++++++++++++++++++++------------
+ tools/main.c      |   2 +
+ tools/mt76-test.h |   3 +
+ 8 files changed, 295 insertions(+), 69 deletions(-)
+
+diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c
+index e26de48..8377586 100644
+--- a/mt7996/debugfs.c
++++ b/mt7996/debugfs.c
+@@ -430,8 +430,8 @@ create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
+ {
+ 	struct dentry *f;
+ 
+-	f = debugfs_create_file("fwlog_data", mode, parent, buf,
+-				&relay_file_operations);
++	f = debugfs_create_file(filename[0] == 'f' ? "fwlog_data" : "idxlog_data",
++	                        mode, parent, buf, &relay_file_operations);
+ 	if (IS_ERR(f))
+ 		return NULL;
+ 
+@@ -522,6 +522,53 @@ mt7996_fw_debug_bin_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
+ 			 mt7996_fw_debug_bin_set, "%lld\n");
+ 
++static int
++mt7996_idxlog_enable_get(void *data, u64 *val)
++{
++	struct mt7996_dev *dev = data;
++
++	*val = dev->idxlog_enable;
++
++	return 0;
++}
++
++static int
++mt7996_idxlog_enable_set(void *data, u64 val)
++{
++	static struct rchan_callbacks relay_cb = {
++		.create_buf_file = create_buf_file_cb,
++		.remove_buf_file = remove_buf_file_cb,
++	};
++	struct mt7996_dev *dev = data;
++
++	if (dev->idxlog_enable == !!val)
++		return 0;
++
++	if (!dev->relay_idxlog) {
++		dev->relay_idxlog = relay_open("idxlog_data", dev->debugfs_dir,
++		                               1500, 512, &relay_cb, NULL);
++		if (!dev->relay_idxlog)
++			return -ENOMEM;
++	}
++
++	dev->idxlog_enable = !!val;
++
++	if (val) {
++		int ret = mt7996_mcu_fw_time_sync(&dev->mt76);
++		if (ret)
++			return ret;
++
++		/* Reset relay channel only when it is not being written to. */
++		relay_reset(dev->relay_idxlog);
++	}
++
++	return mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM,
++	                                val ? MCU_FW_LOG_RELAY_IDX : 0);
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_idxlog_enable, mt7996_idxlog_enable_get,
++	                 mt7996_idxlog_enable_set, "%llu\n");
++
+ static int
+ mt7996_fw_util_wa_show(struct seq_file *file, void *data)
+ {
+@@ -1042,6 +1089,7 @@ int mt7996_init_debugfs(struct mt7996_phy *phy)
+ 	debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ 	debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ 	debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
++	debugfs_create_file("idxlog_enable", 0600, dir, dev, &fops_idxlog_enable);
+ 	/* TODO: wm fw cpu utilization */
+ 	debugfs_create_file("fw_util_wa", 0400, dir, dev,
+ 			    &mt7996_fw_util_wa_fops);
+@@ -1108,6 +1156,32 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ 	spin_unlock_irqrestore(&lock, flags);
+ }
+ 
++static void
++mt7996_debugfs_write_idxlog(struct mt7996_dev *dev, const void *data, int len)
++{
++	static DEFINE_SPINLOCK(lock);
++	unsigned long flags;
++	void *dest;
++
++	if (!dev->relay_idxlog)
++		return;
++
++	spin_lock_irqsave(&lock, flags);
++
++	dest = relay_reserve(dev->relay_idxlog, len + 4);
++	if (!dest)
++		dev_err(dev->mt76.dev, "Failed to reserve slot in %s\n",
++		        dev->relay_idxlog->base_filename);
++	else {
++		*(u32 *)dest = len;
++		dest += 4;
++		memcpy(dest, data, len);
++		relay_flush(dev->relay_idxlog);
++	}
++
++	spin_unlock_irqrestore(&lock, flags);
++}
++
+ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
+ {
+ 	struct {
+@@ -1132,11 +1206,15 @@ void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int
+ 
+ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+ {
+-	if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
+-		return false;
++	bool is_fwlog = get_unaligned_le32(data) == FW_BIN_LOG_MAGIC;
+ 
+-	if (dev->relay_fwlog)
+-		mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++	if (is_fwlog) {
++		if (dev->relay_fwlog)
++			mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
++	} else if (dev->relay_idxlog)
++		mt7996_debugfs_write_idxlog(dev, data, len);
++	else
++		return false;
+ 
+ 	return true;
+ }
+diff --git a/mt7996/mac.c b/mt7996/mac.c
+index e3758ff..8c44442 100644
+--- a/mt7996/mac.c
++++ b/mt7996/mac.c
+@@ -2279,11 +2279,9 @@ void mt7996_mac_work(struct work_struct *work)
+ 	mutex_lock(&mdev->mutex);
+ 
+ 	mt76_update_survey(mphy);
+-	if (++mphy->mac_work_count == 5) {
++	if (++mphy->mac_work_count % 5 == 0) {
+ 		int i;
+ 
+-		mphy->mac_work_count = 0;
+-
+ 		mt7996_mac_update_stats(phy);
+ 
+ 		/* Update DEV-wise information only in
+@@ -2302,6 +2300,12 @@ void mt7996_mac_work(struct work_struct *work)
+ 				if (mt7996_mcu_wa_cmd(phy->dev, MCU_WA_PARAM_CMD(QUERY), MCU_WA_PARAM_BSS_ACQ_PKT_CNT,
+ 				                      BSS_ACQ_PKT_CNT_BSS_BITMAP_ALL | BSS_ACQ_PKT_CNT_READ_CLR, 0))
+ 					dev_err(mdev->dev, "Failed to query per-AC-queue packet counts.\n");
++
++				if (mphy->mac_work_count == 100) {
++					if (phy->dev->idxlog_enable && mt7996_mcu_fw_time_sync(mdev))
++						dev_err(mdev->dev, "Failed to synchronize time with FW.\n");
++					mphy->mac_work_count = 0;
++				}
+ 			} else if (mt7996_band_valid(phy->dev, i) &&
+ 			           test_bit(MT76_STATE_RUNNING, &mdev->phys[i]->state))
+ 				break;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 82ed9e8..e38a507 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -400,6 +400,7 @@ static void
+ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+ #define UNI_EVENT_FW_LOG_FORMAT 0
++#define UNI_EVENT_FW_LOG_MEMORY	1
+ 	struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ 	const char *data = (char *)&rxd[1] + 4, *type;
+ 	struct tlv *tlv = (struct tlv *)data;
+@@ -411,7 +412,8 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+ 		goto out;
+ 	}
+ 
+-	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
++	if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT &&
++	    le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_MEMORY)
+ 		return;
+ 
+ 	data += sizeof(*tlv) + 4;
+@@ -3184,6 +3186,36 @@ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
+ 				 sizeof(data), false);
+ }
+ 
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev)
++{
++	struct {
++		u8 _rsv[4];
++
++		__le16 tag;
++		__le16 len;
++		__le32 sec;
++		__le32 usec;
++	} data = {
++		.tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_TIME_SYNC),
++		.len = cpu_to_le16(sizeof(data) - 4),
++	};
++	struct timespec64 ts;
++	struct tm tm;
++
++	ktime_get_real_ts64(&ts);
++	data.sec = cpu_to_le32((u32)ts.tv_sec);
++	data.usec = cpu_to_le32((u32)(ts.tv_nsec / 1000));
++
++	/* Dump synchronized time for ConsysPlanet to parse. */
++	time64_to_tm(ts.tv_sec, 0, &tm);
++	dev_info(dev->dev, "%ld-%02d-%02d %02d:%02d:%02d.%ld UTC\n",
++	        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++	        tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec / 1000);
++
++	return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
++	                         sizeof(data), true);
++}
++
+ static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
+ {
+ 	struct {
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index d24874a..814072e 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -357,7 +357,8 @@ enum {
+ 	MCU_FW_LOG_WM,
+ 	MCU_FW_LOG_WA,
+ 	MCU_FW_LOG_TO_HOST,
+-	MCU_FW_LOG_RELAY = 16
++	MCU_FW_LOG_RELAY = 16,
++	MCU_FW_LOG_RELAY_IDX = 40
+ };
+ 
+ enum {
+@@ -950,6 +951,7 @@ enum {
+ 	UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ 	UNI_WSYS_CONFIG_FW_DBG_CTRL,
+ 	UNI_CMD_CERT_CFG = 6,
++	UNI_WSYS_CONFIG_FW_TIME_SYNC, /* UNI_CMD_FW_TIME_SYNC in FW */
+ };
+ 
+ enum {
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 69bcf78..d03d3d9 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -591,9 +591,11 @@ struct mt7996_dev {
+ 	u8 fw_debug_bin;
+ 	u16 fw_debug_seq;
+ 	bool fw_debug_muru_disable;
++	bool idxlog_enable;
+ 
+ 	struct dentry *debugfs_dir;
+ 	struct rchan *relay_fwlog;
++	struct rchan *relay_idxlog;
+ 
+ 	void *cal;
+ 	u32 cur_prek_offset;
+@@ -845,6 +847,7 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
+ int mt7996_mcu_red_config(struct mt7996_dev *dev, bool enable);
+ int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+ int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
++int mt7996_mcu_fw_time_sync(struct mt76_dev *dev);
+ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
+ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+ void mt7996_mcu_exit(struct mt7996_dev *dev);
+diff --git a/tools/fwlog.c b/tools/fwlog.c
+index 3c6a61d..0e2de2d 100644
+--- a/tools/fwlog.c
++++ b/tools/fwlog.c
+@@ -29,10 +29,9 @@ static const char *debugfs_path(const char *phyname, const char *file)
+ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ {
+ 	FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
+-
+ 	if (!f) {
+-		fprintf(stderr, "Could not open fw_debug_bin file\n");
+-		return 1;
++		perror("fopen");
++		return -1;
+ 	}
+ 
+ 	if (en && val)
+@@ -47,6 +46,21 @@ static int mt76_set_fwlog_en(const char *phyname, bool en, char *val)
+ 	return 0;
+ }
+ 
++static int mt76_set_idxlog_enable(const char *phyname, bool enable)
++{
++	FILE *f = fopen(debugfs_path(phyname, "idxlog_enable"), "w");
++	if (!f) {
++		perror("fopen");
++		return -1;
++	}
++
++	fprintf(f, "%hhu", enable);
++
++	fclose(f);
++
++	return 0;
++}
++
+ int read_retry(int fd, void *buf, int len)
+ {
+ 	int out_len = 0;
+@@ -80,105 +94,193 @@ static void handle_signal(int sig)
+ 	done = true;
+ }
+ 
+-int mt76_fwlog(const char *phyname, int argc, char **argv)
++static int mt76_log_socket(struct sockaddr_in *remote, char *ip)
+ {
+-#define BUF_SIZE 1504
+ 	struct sockaddr_in local = {
+ 		.sin_family = AF_INET,
+ 		.sin_addr.s_addr = INADDR_ANY,
+ 	};
+-	struct sockaddr_in remote = {
+-		.sin_family = AF_INET,
+-		.sin_port = htons(55688),
+-	};
+-	char *buf = calloc(BUF_SIZE, sizeof(char));
+-	int ret = 0;
+-	/* int yes = 1; */
+-	int s, fd;
+-
+-	if (argc < 1) {
+-		fprintf(stderr, "need destination address\n");
+-		return 1;
+-	}
++	int s, ret;
+ 
+-	if (!inet_aton(argv[0], &remote.sin_addr)) {
+-		fprintf(stderr, "invalid destination address\n");
+-		return 1;
++	remote->sin_family = AF_INET;
++	remote->sin_port = htons(55688);
++	if (!inet_aton(ip, &remote->sin_addr)) {
++		fprintf(stderr, "Invalid destination IP address: %s\n", ip);
++		return -EINVAL;
+ 	}
+ 
+ 	s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ 	if (s < 0) {
+ 		perror("socket");
+-		return 1;
++		return s;
+ 	}
+ 
+-	/* setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); */
+-	if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
++	ret = bind(s, (struct sockaddr *)&local, sizeof(local));
++	if (ret) {
+ 		perror("bind");
+-		return 1;
++		close(s);
++		return ret;
+ 	}
+ 
+-	if (mt76_set_fwlog_en(phyname, true, argv[1]))
+-		return 1;
++	return s;
++}
++
++static int mt76_log_relay(int in_fd, int out_fd, struct sockaddr_in *remote)
++{
++	char *buf = malloc(FWLOG_BUF_SIZE);
++	int ret = 0;
+ 
+-	fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
+-	if (fd < 0) {
+-		fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
+-		ret = 1;
+-		goto out;
++	if (!buf) {
++		perror("malloc");
++		return -ENOMEM;
+ 	}
+ 
+ 	signal(SIGTERM, handle_signal);
+ 	signal(SIGINT, handle_signal);
+ 	signal(SIGQUIT, handle_signal);
+ 
+-	while (1) {
++	while (!done) {
+ 		struct pollfd pfd = {
+-			.fd = fd,
+-			.events = POLLIN | POLLHUP | POLLERR,
++			.fd = in_fd,
++			.events = POLLIN,
+ 		};
+ 		uint32_t len;
+-		int r;
+-
+-		if (done)
+-			break;
++		int rc;
+ 
+ 		poll(&pfd, 1, -1);
+ 
+-		r = read_retry(fd, &len, sizeof(len));
+-		if (r < 0)
++		rc = read_retry(in_fd, &len, sizeof(len));
++		if (rc < 0) {
++			if (!done) {
++				fprintf(stderr, "Failed to read relay file.\n");
++				ret = -1;
++			}
+ 			break;
+-
+-		if (!r)
++		}
++		if (!rc)
+ 			continue;
+ 
+-		if (len > BUF_SIZE) {
+-			fprintf(stderr, "Length error: %d > %d\n", len, BUF_SIZE);
+-			ret = 1;
++		if (len > FWLOG_BUF_SIZE) {
++			fprintf(stderr, "Log size was too large: %u bytes\n", len);
++			ret = -ENOMEM;
+ 			break;
+ 		}
+ 
+-		if (done)
++		rc = read_retry(in_fd, buf, len);
++		if (rc < 0) {
++			if (!done) {
++				fprintf(stderr, "Failed to read relay file.\n");
++				ret = -1;
++			}
+ 			break;
+-
+-		r = read_retry(fd, buf, len);
+-		if (done)
++		}
++		if (rc != len) {
++			fprintf(stderr, "Expected log size: %u bytes\n", len);
++			fprintf(stderr, "Read log size: %u bytes\n", rc);
++			ret = -EIO;
+ 			break;
++		}
+ 
+-		if (r != len) {
+-			fprintf(stderr, "Short read: %d < %d\n", r, len);
+-			ret = 1;
++		if (remote)
++			rc = sendto(out_fd, buf, len, 0, (struct sockaddr *)remote, sizeof(*remote));
++		else
++			rc = write(out_fd, buf, len);
++		if (rc < 0) {
++			perror("sendto/write");
++			ret = -1;
+ 			break;
+ 		}
++	}
++
++	free(buf);
++
++	return ret;
++}
++
++int mt76_fwlog(const char *phyname, int argc, char **argv)
++{
++	struct sockaddr_in remote;
++	int in_fd, out_fd, ret;
++
++	if (argc < 1) {
++		fprintf(stderr, "need destination address\n");
++		return -EINVAL;
++	}
++
++	out_fd = mt76_log_socket(&remote, argv[0]);
++	if (out_fd < 0)
++		return out_fd;
++
++	ret = mt76_set_fwlog_en(phyname, true, argv[1]);
++	if (ret)
++		goto close;
+ 
+-		/* send buf */
+-		sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
++	in_fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
++	if (in_fd < 0) {
++		perror("open");
++		goto disable;
+ 	}
+ 
+-	close(fd);
++	if (mt76_log_relay(in_fd, out_fd, &remote))
++		fprintf(stderr, "Failed to relay FW log.\n");
+ 
+-out:
+-	mt76_set_fwlog_en(phyname, false, NULL);
++	close(in_fd);
++disable:
++	ret = mt76_set_fwlog_en(phyname, false, NULL);
++close:
++	close(out_fd);
++
++	return ret;
++}
++
++int mt76_idxlog(const char *phyname, int argc, char **argv)
++{
++#define IDXLOG_FILE_PATH	"/tmp/log/WIFI_FW.clog"
++	struct sockaddr_in remote;
++	int in_fd, out_fd, ret;
++
++	if (argc) {
++		out_fd = mt76_log_socket(&remote, argv[0]);
++		if (out_fd < 0)
++			return out_fd;
++	} else {
++		out_fd = open(IDXLOG_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR);
++		if (out_fd < 0) {
++			perror("open");
++			return -1;
++		}
++	}
++
++	ret = mt76_set_idxlog_enable(phyname, true);
++	if (ret)
++		goto close;
++
++	in_fd = open(debugfs_path(phyname, "idxlog_data"), O_RDONLY);
++	if (in_fd < 0) {
++		perror("open");
++		goto disable;
++	}
++
++	if (mt76_log_relay(in_fd, out_fd, argc ? &remote : NULL))
++		fprintf(stderr, "Failed to relay index log.\n");
++
++	close(in_fd);
++disable:
++	ret = mt76_set_idxlog_enable(phyname, false);
++close:
++	close(out_fd);
++
++	if (argc)
++		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++		       "clog_dir=/tmp/log/clog_${timestamp};"
++		       "mkdir ${clog_dir};"
++		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
++	else
++		system("timestamp=$(date +\"%y%m%d_%H%M%S\");"
++		       "clog_dir=/tmp/log/clog_${timestamp};"
++		       "mkdir ${clog_dir};"
++		       "mv /tmp/log/WIFI_FW.clog ${clog_dir}/WIFI_FW_${timestamp}.clog;"
++		       "dmesg > ${clog_dir}/WIFI_KERNEL_${timestamp}.clog");
+ 
+ 	return ret;
+ }
+diff --git a/tools/main.c b/tools/main.c
+index 699a9ee..e9e2556 100644
+--- a/tools/main.c
++++ b/tools/main.c
+@@ -198,6 +198,8 @@ int main(int argc, char **argv)
+ 		ret = mt76_eeprom(phy, argc, argv);
+ 	else if (!strcmp(cmd, "fwlog"))
+ 		ret = mt76_fwlog(phyname, argc, argv);
++	else if (!strcmp(cmd, "idxlog"))
++		ret = mt76_idxlog(phyname, argc, argv);
+ 	else
+ 		usage();
+ 
+diff --git a/tools/mt76-test.h b/tools/mt76-test.h
+index d2fafa8..b9d508c 100644
+--- a/tools/mt76-test.h
++++ b/tools/mt76-test.h
+@@ -22,6 +22,8 @@
+ #define EEPROM_FILE_PATH_FMT	"/tmp/mt76-test-%s"
+ #define EEPROM_PART_SIZE	20480
+ 
++#define FWLOG_BUF_SIZE	1504
++
+ struct nl_msg;
+ struct nlattr;
+ 
+@@ -61,5 +63,6 @@ extern unsigned char *eeprom_data;
+ void usage(void);
+ int mt76_eeprom(int phy, int argc, char **argv);
+ int mt76_fwlog(const char *phyname, int argc, char **argv);
++int mt76_idxlog(const char *phyname, int argc, char **argv);
+ 
+ #endif
+-- 
+2.18.0
+