[][MAC80211][app][atenl: support mt7986]

[Description]
Add support for mt7986 and rework commands.

[Release-log]
N/A

Change-Id: Ibeb9a0bf260dca17dfae249929af0eac2a6b51f4
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6050196
diff --git a/autobuild_mac80211_release/mt7986_mac80211/.config b/autobuild_mac80211_release/mt7986_mac80211/.config
index 6ac0349..d16cf7b 100644
--- a/autobuild_mac80211_release/mt7986_mac80211/.config
+++ b/autobuild_mac80211_release/mt7986_mac80211/.config
@@ -4525,7 +4525,7 @@
 # CONFIG_PACKAGE_1905daemon is not set
 # CONFIG_PACKAGE_8021xd is not set
 # CONFIG_PACKAGE_ated_ext is not set
-# CONFIG_PACKAGE_atenl is not set
+CONFIG_PACKAGE_atenl=y
 # CONFIG_PACKAGE_bluedroid is not set
 # CONFIG_PACKAGE_datconf is not set
 # CONFIG_PACKAGE_datconf-lua is not set
diff --git a/feed/atenl/files/ated.sh b/feed/atenl/files/ated.sh
index 71d139f..0fa8950 100755
--- a/feed/atenl/files/ated.sh
+++ b/feed/atenl/files/ated.sh
@@ -32,7 +32,7 @@
     killall atenl > /dev/null 2>&1
 fi
 
-eval "${cmd}" > /dev/null 2>&1 &
+eval "${cmd}"
 if [[ ! -z "${ori_inf}" ]]; then
     echo "if_name: ${ori_inf}"
 fi
diff --git a/feed/atenl/src/atenl.h b/feed/atenl/src/atenl.h
index a3ce244..4078825 100644
--- a/feed/atenl/src/atenl.h
+++ b/feed/atenl/src/atenl.h
@@ -15,9 +15,8 @@
 
 #include "nl.h"
 #include "util.h"
+#include "debug.h"
 
-/* #define CONFIG_ATENL_DEBUG     1 */
-/* #define CONFIG_ATENL_DEBUG_VERBOSE     1 */
 #define BRIDGE_NAME	"br-lan"
 #define ETH_P_RACFG	0x2880
 #define RACFG_PKT_MAX_SIZE	1600
@@ -28,14 +27,6 @@
 #define RACFG_CMD_TYPE_ETHREQ	BIT(3)
 #define RACFG_CMD_TYPE_PLATFORM_MODULE	GENMASK(4, 3)
 
-#define atenl_info(fmt, ...)	printf(fmt, __VA_ARGS__)
-#define atenl_err(fmt, ...)	fprintf(stderr, fmt, __VA_ARGS__)
-#ifdef CONFIG_ATENL_DEBUG
-#define atenl_dbg(fmt, ...)	atenl_info(fmt, __VA_ARGS__)
-#else
-#define atenl_dbg(fmt, ...)
-#endif
-
 #define set_band_val(_an, _band, _field, _val)	\
 	_an->anb[_band]._field = (_val)
 #define get_band_val(_an, _band, _field)	\
@@ -68,6 +59,8 @@
 	enum atenl_rf_mode rf_mode;
 
 	bool use_tx_time;
+	u32 tx_time;
+	u32 tx_mpdu_len;
 
 	bool reset_tx_cnt;
 	bool reset_rx_cnt;
@@ -76,19 +69,18 @@
 	struct atenl_rx_stat rx_stat;
 };
 
-#define MAX_BAND_NUM	4
+#define MAX_BAND_NUM	3
 
 struct atenl {
 	struct atenl_band anb[MAX_BAND_NUM];
 	u16 chip_id;
-
+	u16 adie_id;
+	u8 sub_chip_id;
 	u8 cur_band;
 
 	u8 mac_addr[ETH_ALEN];
 	bool unicast;
 	int sock_eth;
-	int pipefd[2];
-	int child_pid;
 
 	const char *mtd_part;
 	u32 mtd_offset;
@@ -97,6 +89,10 @@
 	u16 eeprom_size;
 
 	bool cmd_mode;
+
+	/* intermediate data */
+	u8 ibf_mcs;
+	u8 ibf_ant;
 };
 
 struct atenl_cmd_hdr {
@@ -131,7 +127,9 @@
 	HQA_CMD_SET_CFG,
 	HQA_CMD_SET_RU,
 	HQA_CMD_SET_BAND,
+	HQA_CMD_SET_EEPROM_TO_FW,
 	HQA_CMD_READ_MAC_BBP_REG,
+	HQA_CMD_READ_MAC_BBP_REG_QA,
 	HQA_CMD_READ_RF_REG,
 	HQA_CMD_READ_EEPROM_BULK,
 	HQA_CMD_READ_TEMPERATURE,
@@ -186,16 +184,24 @@
 struct atenl_data {
 	u8 buf[RACFG_PKT_MAX_SIZE];
 	int len;
+	u16 cmd_id;
+	u8 ext_id;
 	enum atenl_cmd cmd;
-	u32 ext_id;
 	enum atenl_ext_cmd ext_cmd;
 };
 
-struct atenl_cmd_ops {
-	u16 resp_len;
+struct atenl_ops {
 	int (*ops)(struct atenl *an, struct atenl_data *data);
+	u8 cmd;
+	u8 flags;
+	u16 cmd_id;
+	u16 resp_len;
 };
 
+#define ATENL_OPS_FLAG_EXT_CMD	BIT(0)
+#define ATENL_OPS_FLAG_LEGACY	BIT(1)
+#define ATENL_OPS_FLAG_SKIP	BIT(2)
+
 static inline struct atenl_cmd_hdr * atenl_hdr(struct atenl_data *data)
 {
 	u8 *hqa_data = (u8 *)data->buf + ETH_HLEN;
@@ -203,18 +209,6 @@
 	return (struct atenl_cmd_hdr *)hqa_data;
 }
 
-static inline void
-atenl_dbg_print_data(struct atenl_data *data, const char *func_name, u32 len)
-{
-#ifdef CONFIG_ATENL_DEBUG_VERBOSE
-	u32 *tmp = (u32 *)data->buf;
-	int i;
-
-	for (i = 0; i < DIV_ROUND_UP(len, 4); i++)
-		atenl_dbg("%s: [%d] = 0x%08x\n", func_name, i, tmp[i]);
-#endif
-}
-
 enum atenl_phy_type {
 	ATENL_PHY_TYPE_CCK,
 	ATENL_PHY_TYPE_OFDM,
@@ -374,8 +368,7 @@
 int atenl_eth_init(struct atenl *an);
 int atenl_eth_recv(struct atenl *an, struct atenl_data *data);
 int atenl_eth_send(struct atenl *an, struct atenl_data *data);
-int atenl_hqa_recv(struct atenl *an, struct atenl_data *data);
-int atenl_hqa_proc_cmd(struct atenl *an, struct atenl_data *data);
+int atenl_hqa_proc_cmd(struct atenl *an);
 int atenl_nl_process(struct atenl *an, struct atenl_data *data);
 int atenl_nl_process_many(struct atenl *an, struct atenl_data *data);
 int atenl_nl_check_mtd(struct atenl *an);
@@ -384,6 +377,7 @@
 int atenl_nl_update_buffer_mode(struct atenl *an);
 int atenl_nl_set_state(struct atenl *an, u8 band,
 		       enum mt76_testmode_state state);
+int atenl_nl_set_aid(struct atenl *an, u8 band, u8 aid);
 int atenl_eeprom_init(struct atenl *an, u8 phy_idx);
 void atenl_eeprom_close(struct atenl *an);
 int atenl_eeprom_write_mtd(struct atenl *an);
@@ -392,5 +386,7 @@
 u16 atenl_get_center_channel(u8 bw, u8 ch_band, u16 ctrl_ch);
 int atenl_reg_read(struct atenl *an, u32 offset, u32 *res);
 int atenl_reg_write(struct atenl *an, u32 offset, u32 val);
+int atenl_rf_read(struct atenl *an, u32 wf_sel, u32 offset, u32 *res);
+int atenl_rf_write(struct atenl *an, u32 wf_sel, u32 offset, u32 val);
 
 #endif
diff --git a/feed/atenl/src/debug.h b/feed/atenl/src/debug.h
new file mode 100644
index 0000000..7621887
--- /dev/null
+++ b/feed/atenl/src/debug.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+#ifndef __ATENL_DEBUG_H
+#define __ATENL_DEBUG_H
+
+/* #define CONFIG_ATENL_DEBUG     1 */
+/* #define CONFIG_ATENL_DEBUG_VERBOSE     1 */
+
+#define atenl_info(fmt, ...)	(void)fprintf(stdout, fmt, ##__VA_ARGS__)
+#define atenl_err(fmt, ...)	(void)fprintf(stderr, fmt, ##__VA_ARGS__)
+#ifdef CONFIG_ATENL_DEBUG
+#define atenl_dbg(fmt, ...)	atenl_info(fmt, ##__VA_ARGS__)
+#else
+#define atenl_dbg(fmt, ...)
+#endif
+
+static inline void
+atenl_dbg_print_data(const void *data, const char *func_name, u32 len)
+{
+#ifdef CONFIG_ATENL_DEBUG_VERBOSE
+	u32 *tmp = (u32 *)data;
+	int i;
+
+	for (i = 0; i < DIV_ROUND_UP(len, 4); i++)
+		atenl_dbg("%s: [%d] = 0x%08x\n", func_name, i, tmp[i]);
+#endif
+}
+
+/* #define debug_print(fmt, ...) \ */
+/* 	do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) */
+
+#endif
diff --git a/feed/atenl/src/eeprom.c b/feed/atenl/src/eeprom.c
index feec9ea..ee77ed7 100644
--- a/feed/atenl/src/eeprom.c
+++ b/feed/atenl/src/eeprom.c
@@ -36,14 +36,15 @@
 static int
 atenl_flash_create_file(struct atenl *an)
 {
+#define READ_LEN_LIMIT	20000
 	char buf[1024];
-	ssize_t len;
+	ssize_t len, limit = 0;
 	FILE *f;
 	int fd, ret;
 
 	f = mtd_open(an->mtd_part);
 	if (!f) {
-		fprintf(stderr, "Failed to open MTD device\n");
+		atenl_err("Failed to open MTD device\n");
 		return -1;
 	}
 
@@ -56,8 +57,13 @@
 
 retry:
 		w = write(fd, buf, len);
-		if (w > 0)
+		if (w > 0) {
+			limit += len;
+
+			if (limit >= READ_LEN_LIMIT)
+				break;
 			continue;
+		}
 
 		if (errno == EINTR)
 			goto retry;
@@ -143,9 +149,9 @@
 
 	if (!atenl_eeprom_file_exists()) {
 		if (flash_mode)
-			atenl_dbg("[%d]%s: init eeprom with flash mode\n", getpid(), __func__);
+			atenl_dbg("%s: init eeprom with flash mode\n", __func__);
 		else
-			atenl_dbg("[%d]%s: init eeprom with efuse mode\n", getpid(), __func__);
+			atenl_dbg("%s: init eeprom with efuse mode\n", __func__);
 
 		if (flash_mode)
 			return atenl_flash_create_file(an);
@@ -161,6 +167,46 @@
 }
 
 static void
+atenl_eeprom_init_chip_id(struct atenl *an)
+{
+	an->chip_id = *(u16 *)an->eeprom_data;
+
+	if (is_mt7915(an)) {
+		an->adie_id = 0x7975;
+	} else if (is_mt7916(an)) {
+		an->adie_id = 0x7976;
+	} else if (is_mt7986(an)) {
+		bool is_7975 = false;
+		u32 val;
+		u8 sub_id;
+
+		atenl_reg_read(an, 0x18050000, &val);
+
+		switch (val & 0xf) {
+		case MT7975_ONE_ADIE_SINGLE_BAND:
+			is_7975 = true;
+			/* fallthrough */
+		case MT7976_ONE_ADIE_SINGLE_BAND:
+			sub_id = 0xa;
+			break;
+		case MT7976_ONE_ADIE_DBDC:
+			sub_id = 0x7;
+			break;
+		case MT7975_DUAL_ADIE_DBDC:
+			is_7975 = true;
+			/* fallthrough */
+		case MT7976_DUAL_ADIE_DBDC:
+		default:
+			sub_id = 0xf;
+			break;
+		}
+
+		an->sub_chip_id = sub_id;
+		an->adie_id = is_7975 ? 0x7975 : 0x7976;
+	}
+}
+
+static void
 atenl_eeprom_init_max_size(struct atenl *an)
 {
 	switch (an->chip_id) {
@@ -284,7 +330,7 @@
 	}
 
 	an->eeprom_fd = eeprom_fd;
-	an->chip_id = *(u16 *)an->eeprom_data;
+	atenl_eeprom_init_chip_id(an);
 	atenl_eeprom_init_max_size(an);
 	atenl_eeprom_init_band_cap(an);
 	atenl_eeprom_init_antenna_cap(an);
@@ -301,7 +347,7 @@
 	munmap(an->eeprom_data, EEPROM_PART_SIZE);
 	close(an->eeprom_fd);
 
-	if (!an->cmd_mode && an->child_pid) {
+	if (!an->cmd_mode) {
 		if (remove(eeprom_file))
 			perror("remove");
 	}
@@ -328,7 +374,7 @@
 
 		ret = execvp("mtd", cmd);
 		if (ret < 0) {
-			fprintf(stderr, "%s: execl error\n", __func__);
+			atenl_err("%s: exec error\n", __func__);
 			exit(0);
 		}
 	} else {
@@ -338,7 +384,7 @@
 	return 0;
 }
 
-/* Directly read some value from driver's eeprom.
+/* Directly read values from driver's eeprom.
  * It's usally used to get calibrated data from driver.
  */
 int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
@@ -401,7 +447,7 @@
 		char *s = strchr(cmd, ' ');
 
 		if (!s) {
-			fprintf(stderr, "eeprom: please type a correct command\n");
+			atenl_err("eeprom: please type a correct command\n");
 			return;
 		}
 
diff --git a/feed/atenl/src/eth.c b/feed/atenl/src/eth.c
index a58add3..54a8d2c 100644
--- a/feed/atenl/src/eth.c
+++ b/feed/atenl/src/eth.c
@@ -43,25 +43,31 @@
 
 int atenl_eth_recv(struct atenl *an, struct atenl_data *data)
 {
-	char buf[RACFG_PKT_MAX_SIZE];
-	int len = recvfrom(an->sock_eth, buf, sizeof(buf), 0, NULL, NULL);
-	struct ethhdr *hdr = (struct ethhdr *)buf;
+	struct ethhdr *hdr;
+	int len;
 
-	atenl_dbg("[%d]%s: recv len = %d\n", getpid(), __func__, len);
+	len = recvfrom(an->sock_eth, data->buf, sizeof(data->buf), 0, NULL, NULL);
 
-	if (len >= ETH_HLEN + RACFG_HLEN) {
-		if (hdr->h_proto == htons(ETH_P_RACFG) &&
-		    (ether_addr_equal(an->mac_addr, hdr->h_dest) ||
-		     is_broadcast_ether_addr(hdr->h_dest))) {
-			data->len = len;
-			memcpy(data->buf, buf, len);
+	if (len < ETH_HLEN + RACFG_HLEN) {
+		atenl_err("packet len is too short: %d\n", len);
+		return -EINVAL;
+	}
+
+	hdr = (struct ethhdr *)data->buf;
+	if (hdr->h_proto != htons(ETH_P_RACFG)) {
+		atenl_err("invalid protocol type\n");
+		return -EINVAL;
+	}
 
-			return 0;
-		}
+	if (!ether_addr_equal(an->mac_addr, hdr->h_dest) &&
+	    !is_broadcast_ether_addr(hdr->h_dest)) {
+		atenl_err("invalid dest MAC\n");
+		return -EINVAL;
 	}
 
-	atenl_err("%s: packet len is too short\n", __func__);
-	return -EINVAL;
+	data->len = len;
+
+	return 0;
 }
 
 int atenl_eth_send(struct atenl *an, struct atenl_data *data)
@@ -81,11 +87,11 @@
 	if (len < 60)
 		len = 60;
 	else if (len > 1514) {
-		atenl_err("%s: response ethernet length is too long\n", __func__);
+		atenl_err("response ethernet length is too long\n");
 		return -1;
 	}
 
-	atenl_dbg_print_data(data, __func__, len);
+	atenl_dbg_print_data(data->buf, __func__, len);
 
 	addr.sll_family = PF_PACKET;
 	addr.sll_protocol = htons(ETH_P_RACFG);
@@ -103,7 +109,5 @@
 		return ret;
 	}
 	
-	atenl_dbg("[%d]%s: send length = %d\n", getpid(), __func__, len);
-
 	return 0;
 }
diff --git a/feed/atenl/src/hqa.c b/feed/atenl/src/hqa.c
index 2196fe2..eabfce5 100644
--- a/feed/atenl/src/hqa.c
+++ b/feed/atenl/src/hqa.c
@@ -200,31 +200,7 @@
 {
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
 
-	if (is_mt7986(an)) {
-		u32 sub_id, val;
-		int ret;
-
-		ret = atenl_reg_read(an, 0x18050000, &val);
-		if (ret)
-			return ret;
-
-		switch (val & 0xf) {
-		case MT7975_ONE_ADIE_SINGLE_BAND:
-		case MT7976_ONE_ADIE_SINGLE_BAND:
-			sub_id = htonl(0xa);
-			break;
-		case MT7976_ONE_ADIE_DBDC:
-			sub_id = htonl(0x7);
-			break;
-		case MT7975_DUAL_ADIE_DBDC:
-		case MT7976_DUAL_ADIE_DBDC:
-		default:
-			sub_id = htonl(0xf);
-			break;
-		}
-
-		memcpy(hdr->data + 2, &sub_id, 4);
-	}
+	*(u32 *)(hdr->data + 2) = htonl(an->sub_chip_id);
 
 	return 0;
 }
@@ -266,13 +242,12 @@
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
 	enum atenl_cmd cmd = data->cmd;
 	u32 *v = (u32 *)hdr->data;
-	u32 offset = ntohl(v[0]);
+	u32 offset = ntohl(v[0]), res;
 	int ret;
 
 	if (cmd == HQA_CMD_READ_MAC_BBP_REG) {
 		u16 num = ntohs(*(u16 *)(hdr->data + 4));
 		u32 *ptr = (u32 *)(hdr->data + 2);
-		u32 res;
 		int i;
 
 		if (num > SHRT_MAX) {
@@ -289,6 +264,13 @@
 			res = htonl(res);
 			memcpy(ptr + i, &res, 4);
 		}
+	} else if (cmd == HQA_CMD_READ_MAC_BBP_REG_QA) {
+		ret = atenl_reg_read(an, offset, &res);
+		if (ret)
+			goto out;
+
+		res = htonl(res);
+		memcpy(hdr->data + 2, &res, 4);
 	} else {
 		u32 val = ntohl(v[1]);
 
@@ -305,6 +287,49 @@
 }
 
 static int
+atenl_hqa_rf_reg(struct atenl *an, struct atenl_data *data)
+{
+	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
+	enum atenl_cmd cmd = data->cmd;
+	u32 *v = (u32 *)hdr->data;
+	u32 wf_sel = ntohl(v[0]);
+	u32 offset = ntohl(v[1]);
+	u32 num = ntohl(v[2]);
+	int ret, i;
+
+	if (cmd == HQA_CMD_READ_RF_REG) {
+		u32 *ptr = (u32 *)(hdr->data + 2);
+		u32 res;
+
+		hdr->len = htons(2 + num * 4);
+		for (i = 0; i < num && i < sizeof(hdr->data) / 4; i++) {
+			ret = atenl_rf_read(an, wf_sel, offset + i * 4, &res);
+			if (ret)
+				goto out;
+
+			res = htonl(res);
+			memcpy(ptr + i, &res, 4);
+		}
+	} else {
+		u32 *ptr = (u32 *)(hdr->data + 12);
+
+		for (i = 0; i < num && i < sizeof(hdr->data) / 4; i++) {
+			u32 val = ntohl(ptr[i]);
+
+			ret = atenl_rf_write(an, wf_sel, offset + i * 4, val);
+			if (ret)
+				goto out;
+		}
+	}
+
+	ret = 0;
+out:
+	memset(hdr->data, 0, 2);
+
+	return ret;
+}
+
+static int
 atenl_hqa_eeprom_bulk(struct atenl *an, struct atenl_data *data)
 {
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
@@ -448,12 +473,6 @@
 }
 
 static int
-atenl_hqa_skip(struct atenl *an, struct atenl_data *data)
-{
-	return 0;
-}
-
-static int
 atenl_hqa_check_efuse_mode(struct atenl *an, struct atenl_data *data)
 {
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
@@ -587,7 +606,7 @@
 	if (snprintf_error(sizeof(cmd), ret))
 		return;
 
-	atenl_dbg("[%d]%s: cmd: %s\n", getpid(), __func__, cmd);
+	atenl_dbg("%s: cmd: %s\n", __func__, cmd);
 
 	system(cmd);
 }
@@ -692,6 +711,8 @@
 
 	*(u32 *)(hdr->data + 2) = data->ext_id;
 
+	atenl_nl_set_aid(an, band, 0);
+
 	return 0;
 }
 
@@ -712,314 +733,504 @@
 	return 0;
 }
 
-static inline enum atenl_cmd atenl_get_cmd_by_id(u16 cmd_idx)
-{
-#define CMD_ID_GROUP	GENMASK(15, 8)
-	u8 group = FIELD_GET(CMD_ID_GROUP, cmd_idx);
-
-	if (cmd_idx == 0x1600)
-		return HQA_CMD_EXT;
-
-	if (group == 0x10) {
-		switch (cmd_idx) {
-		case 0x1000:
-			return HQA_CMD_OPEN_ADAPTER;
-		case 0x1001:
-			return HQA_CMD_CLOSE_ADAPTER;
-		case 0x100b:
-			return HQA_CMD_SET_TX_PATH;
-		case 0x100c:
-			return HQA_CMD_SET_RX_PATH;
-		case 0x1011:
-			return HQA_CMD_SET_TX_POWER;
-		case 0x1018:
-			return HQA_CMD_SET_TX_POWER_MANUAL;
-		case 0x100d:
-			return HQA_CMD_LEGACY;
-		default:
-			break;
-		}
-	} else if (group == 0x11) {
-		switch (cmd_idx) {
-		case 0x1104:
-			return HQA_CMD_SET_TX_BW;
-		case 0x1105:
-			return HQA_CMD_SET_TX_PKT_BW;
-		case 0x1106:
-			return HQA_CMD_SET_TX_PRI_BW;
-		case 0x1107:
-			return HQA_CMD_SET_FREQ_OFFSET;
-		case 0x1109:
-			return HQA_CMD_SET_TSSI;
-		case 0x110d:
-			return HQA_CMD_ANT_SWAP_CAP;
-		case 0x1101:
-		case 0x1102:
-			return HQA_CMD_LEGACY;
-		default:
-			break;
-		}
-	} else if (group == 0x12) {
-		switch (cmd_idx) {
-		case 0x1200:
-			return HQA_CMD_RESET_TX_RX_COUNTER;
-		default:
-			break;
-		}
-	} else if (group == 0x13) {
-		switch (cmd_idx) {
-		case 0x1301:
-			return HQA_CMD_WRITE_MAC_BBP_REG;
-		case 0x1302:
-			return HQA_CMD_READ_MAC_BBP_REG;
-		case 0x1307:
-			return HQA_CMD_READ_EEPROM_BULK;
-		case 0x1306:
-		case 0x1308:
-			return HQA_CMD_WRITE_EEPROM_BULK;
-		case 0x1309:
-			return HQA_CMD_CHECK_EFUSE_MODE;
-		case 0x130a:
-			return HQA_CMD_GET_EFUSE_FREE_BLOCK;
-		case 0x130d:
-			return HQA_CMD_GET_TX_POWER;
-		case 0x130e:
-			return HQA_CMD_SET_CFG;
-		case 0x130f:
-			return HQA_CMD_GET_FREQ_OFFSET;
-		case 0x1311:
-			return HQA_CMD_CONTINUOUS_TX;
-		case 0x1312:
-			return HQA_CMD_SET_RX_PKT_LEN;
-		case 0x1313:
-			return HQA_CMD_GET_TX_INFO;
-		case 0x1314:
-			return HQA_CMD_GET_CFG;
-		case 0x131f:
-			return HQA_CMD_UNKNOWN;
-		case 0x131a:
-			return HQA_CMD_GET_TX_TONE_POWER;
-		default:
-			break;
-		}
-	} else if (group == 0x14) {
-		switch (cmd_idx) {
-		case 0x1401:
-			return HQA_CMD_READ_TEMPERATURE;
-		default:
-			break;
-		}
-	} else if (group == 0x15) {
-		switch (cmd_idx) {
-		case 0x1500:
-			return HQA_CMD_GET_FW_INFO;
-		case 0x1505:
-			return HQA_CMD_SET_TSSI;
-		case 0x1509:
-			return HQA_CMD_SET_RF_MODE;
-		case 0x1511:
-			return HQA_CMD_WRITE_BUFFER_DONE;
-		case 0x1514:
-			return HQA_CMD_GET_CHIP_ID;
-		case 0x151b:
-			return HQA_CMD_GET_SUB_CHIP_ID;
-		case 0x151c:
-			return HQA_CMD_GET_RX_INFO;
-		case 0x151e:
-			return HQA_CMD_GET_RF_CAP;
-		case 0x1522:
-			return HQA_CMD_CHECK_EFUSE_MODE_TYPE;
-		case 0x1523:
-			return HQA_CMD_CHECK_EFUSE_MODE_NATIVE;
-		case 0x152d:
-			return HQA_CMD_GET_BAND;
-		case 0x1594:
-			return HQA_CMD_SET_RU;
-		case 0x1502:
-		case 0x150b:
-			return HQA_CMD_LEGACY;
-		default:
-			break;
-		}
-	}
-
-	return HQA_CMD_ERR;
-}
-
-static inline enum atenl_ext_cmd atenl_get_ext_cmd(u16 ext_cmd_idx)
-{
-#define EXT_CMD_ID_GROUP	GENMASK(7, 4)
-	u8 ext_group = FIELD_GET(EXT_CMD_ID_GROUP, ext_cmd_idx);
-
-	if (ext_group == 0) {
-		switch (ext_cmd_idx) {
-		case 0x1:
-			return HQA_EXT_CMD_SET_CHANNEL;
-		case 0x2:
-			return HQA_EXT_CMD_SET_TX;
-		case 0x3:
-			return HQA_EXT_CMD_START_TX;
-		case 0x4:
-			return HQA_EXT_CMD_START_RX;
-		case 0x5:
-			return HQA_EXT_CMD_STOP_TX;
-		case 0x6:
-			return HQA_EXT_CMD_STOP_RX;
-		case 0x8:
-			return HQA_EXT_CMD_IBF_SET_VAL;
-		case 0x9:
-			return HQA_EXT_CMD_IBF_GET_STATUS;
-		case 0xc:
-			return HQA_EXT_CMD_IBF_PROF_UPDATE_ALL;
-		default:
-			break;
-		}
-	} else if (ext_group == 1) {
-	} else if (ext_group == 2) {
-		switch (ext_cmd_idx) {
-		case 0x26:
-			return HQA_EXT_CMD_SET_TX_TIME_OPT;
-		case 0x27:
-			return HQA_EXT_CMD_OFF_CH_SCAN;
-		default:
-			break;
-		}
-	}
-
-	return HQA_EXT_CMD_UNSPEC;
-}
-
-#define ATENL_GROUP(_cmd, _resp_len, _ops)	\
-	[HQA_CMD_##_cmd] = { .resp_len=_resp_len, .ops=_ops }
-static const struct atenl_cmd_ops atenl_ops[] = {
-	ATENL_GROUP(OPEN_ADAPTER, 2, atenl_hqa_adapter),
-	ATENL_GROUP(CLOSE_ADAPTER, 2, atenl_hqa_adapter),
-	ATENL_GROUP(SET_TX_PATH, 2, atenl_nl_process),
-	ATENL_GROUP(SET_RX_PATH, 2, atenl_nl_process),
-	ATENL_GROUP(SET_TX_POWER, 2, atenl_nl_process),
-	ATENL_GROUP(SET_TX_POWER_MANUAL, 2, atenl_hqa_skip),
-	ATENL_GROUP(SET_TX_BW, 2, atenl_hqa_skip),
-	ATENL_GROUP(SET_TX_PKT_BW, 2, atenl_hqa_skip),
-	ATENL_GROUP(SET_TX_PRI_BW, 2, atenl_hqa_skip),
-	ATENL_GROUP(SET_FREQ_OFFSET, 2, atenl_nl_process),
-	ATENL_GROUP(ANT_SWAP_CAP, 6, atenl_hqa_skip),
-	ATENL_GROUP(RESET_TX_RX_COUNTER, 2, atenl_hqa_reset_counter),
-	ATENL_GROUP(WRITE_MAC_BBP_REG, 2, atenl_hqa_mac_bbp_reg),
-	ATENL_GROUP(READ_MAC_BBP_REG, 0, atenl_hqa_mac_bbp_reg),
-	ATENL_GROUP(READ_EEPROM_BULK, 0, atenl_hqa_eeprom_bulk),
-	ATENL_GROUP(WRITE_EEPROM_BULK, 2, atenl_hqa_eeprom_bulk),
-	ATENL_GROUP(CHECK_EFUSE_MODE, 6, atenl_hqa_check_efuse_mode),
-	ATENL_GROUP(GET_EFUSE_FREE_BLOCK, 6, atenl_hqa_get_efuse_free_block),
-	ATENL_GROUP(GET_TX_POWER, 10, atenl_hqa_get_tx_power),
-	ATENL_GROUP(GET_FREQ_OFFSET, 6, atenl_hqa_get_freq_offset), /*TODO: MCU CMD, read eeprom?*/
-	ATENL_GROUP(CONTINUOUS_TX, 6, atenl_nl_process),
-	ATENL_GROUP(SET_RX_PKT_LEN, 2, atenl_hqa_skip),
-	ATENL_GROUP(GET_TX_INFO, 10, atenl_nl_process),
-	ATENL_GROUP(GET_CFG, 6, atenl_hqa_get_cfg), /*TODO*/
-	ATENL_GROUP(GET_TX_TONE_POWER, 6, atenl_hqa_skip),
-	ATENL_GROUP(SET_CFG, 2, atenl_nl_process),
-	ATENL_GROUP(READ_TEMPERATURE, 6, atenl_hqa_read_temperature),
-	ATENL_GROUP(GET_FW_INFO, 32, atenl_hqa_skip), /* TODO: check format */
-	ATENL_GROUP(SET_TSSI, 2, atenl_nl_process),
-	ATENL_GROUP(SET_RF_MODE, 2, atenl_hqa_set_rf_mode),
-	ATENL_GROUP(WRITE_BUFFER_DONE, 2, atenl_hqa_eeprom_bulk),
-	ATENL_GROUP(GET_CHIP_ID, 6, atenl_hqa_get_chip_id),
-	ATENL_GROUP(GET_SUB_CHIP_ID, 6, atenl_hqa_get_sub_chip_id),
-	ATENL_GROUP(GET_RX_INFO, 0, atenl_nl_process),
-	ATENL_GROUP(GET_RF_CAP, 10, atenl_hqa_get_rf_cap),
-	ATENL_GROUP(CHECK_EFUSE_MODE_TYPE, 6, atenl_hqa_check_efuse_mode),
-	ATENL_GROUP(CHECK_EFUSE_MODE_NATIVE, 6, atenl_hqa_check_efuse_mode),
-	ATENL_GROUP(GET_BAND, 6, atenl_hqa_get_band),
-	ATENL_GROUP(SET_RU, 2, atenl_nl_process_many),
-
-	ATENL_GROUP(LEGACY, 2, atenl_hqa_skip),
-	ATENL_GROUP(UNKNOWN, 1024, atenl_hqa_skip),
+/* should be placed in order for binary search */
+static const struct atenl_ops hqa_ops[] = {
+	{
+		.cmd = HQA_CMD_OPEN_ADAPTER,
+		.cmd_id = 0x1000,
+		.resp_len = 2,
+		.ops = atenl_hqa_adapter,
+	},
+	{
+		.cmd = HQA_CMD_CLOSE_ADAPTER,
+		.cmd_id = 0x1001,
+		.resp_len = 2,
+		.ops = atenl_hqa_adapter,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_PATH,
+		.cmd_id = 0x100b,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_RX_PATH,
+		.cmd_id = 0x100c,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_LEGACY,
+		.cmd_id = 0x100d,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_POWER,
+		.cmd_id = 0x1011,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_POWER_MANUAL,
+		.cmd_id = 0x1018,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_LEGACY,
+		.cmd_id = 0x1101,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_LEGACY,
+		.cmd_id = 0x1102,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_BW,
+		.cmd_id = 0x1104,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_PKT_BW,
+		.cmd_id = 0x1105,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_SET_TX_PRI_BW,
+		.cmd_id = 0x1106,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_SET_FREQ_OFFSET,
+		.cmd_id = 0x1107,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_TSSI,
+		.cmd_id = 0x1109,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_EEPROM_TO_FW,
+		.cmd_id = 0x110c,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_ANT_SWAP_CAP,
+		.cmd_id = 0x110d,
+		.resp_len = 6,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_RESET_TX_RX_COUNTER,
+		.cmd_id = 0x1200,
+		.resp_len = 2,
+		.ops = atenl_hqa_reset_counter,
+	},
+	{
+		.cmd = HQA_CMD_READ_MAC_BBP_REG_QA,
+		.cmd_id = 0x1300,
+		.resp_len = 6,
+		.ops = atenl_hqa_mac_bbp_reg,
+	},
+	{
+		.cmd = HQA_CMD_WRITE_MAC_BBP_REG,
+		.cmd_id = 0x1301,
+		.resp_len = 2,
+		.ops = atenl_hqa_mac_bbp_reg,
+	},
+	{
+		.cmd = HQA_CMD_READ_MAC_BBP_REG,
+		.cmd_id = 0x1302,
+		.ops = atenl_hqa_mac_bbp_reg,
+	},
+	{
+		.cmd = HQA_CMD_READ_RF_REG,
+		.cmd_id = 0x1303,
+		.ops = atenl_hqa_rf_reg,
+	},
+	{
+		.cmd = HQA_CMD_WRITE_RF_REG,
+		.cmd_id = 0x1304,
+		.resp_len = 2,
+		.ops = atenl_hqa_rf_reg,
+	},
+	{
+		.cmd = HQA_CMD_WRITE_EEPROM_BULK,
+		.cmd_id = 0x1306,
+		.resp_len = 2,
+		.ops = atenl_hqa_eeprom_bulk,
+	},
+	{
+		.cmd = HQA_CMD_READ_EEPROM_BULK,
+		.cmd_id = 0x1307,
+		.ops = atenl_hqa_eeprom_bulk,
+	},
+	{
+		.cmd = HQA_CMD_WRITE_EEPROM_BULK,
+		.cmd_id = 0x1308,
+		.resp_len = 2,
+		.ops = atenl_hqa_eeprom_bulk,
+	},
+	{
+		.cmd = HQA_CMD_CHECK_EFUSE_MODE,
+		.cmd_id = 0x1309,
+		.resp_len = 6,
+		.ops = atenl_hqa_check_efuse_mode,
+	},
+	{
+		.cmd = HQA_CMD_GET_EFUSE_FREE_BLOCK,
+		.cmd_id = 0x130a,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_efuse_free_block,
+	},
+	{
+		.cmd = HQA_CMD_GET_TX_POWER,
+		.cmd_id = 0x130d,
+		.resp_len = 10,
+		.ops = atenl_hqa_get_tx_power,
+	},
+	{
+		.cmd = HQA_CMD_SET_CFG,
+		.cmd_id = 0x130e,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_GET_FREQ_OFFSET,
+		.cmd_id = 0x130f,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_freq_offset,
+	},
+	{
+		.cmd = HQA_CMD_CONTINUOUS_TX,
+		.cmd_id = 0x1311,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_RX_PKT_LEN,
+		.cmd_id = 0x1312,
+		.resp_len = 2,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_GET_TX_INFO,
+		.cmd_id = 0x1313,
+		.resp_len = 10,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_GET_CFG,
+		.cmd_id = 0x1314,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_cfg,
+	},
+	{
+		.cmd = HQA_CMD_GET_TX_TONE_POWER,
+		.cmd_id = 0x131a,
+		.resp_len = 6,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_UNKNOWN,
+		.cmd_id = 0x131f,
+		.resp_len = 1024,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd = HQA_CMD_READ_TEMPERATURE,
+		.cmd_id = 0x1401,
+		.resp_len = 6,
+		.ops = atenl_hqa_read_temperature,
+	},
+	{
+		.cmd = HQA_CMD_GET_FW_INFO,
+		.cmd_id = 0x1500,
+		.resp_len = 32,
+		.flags = ATENL_OPS_FLAG_SKIP,
+	},
+	{
+		.cmd_id = 0x1502,
+		.flags = ATENL_OPS_FLAG_LEGACY,
+	},
+	{
+		.cmd = HQA_CMD_SET_TSSI,
+		.cmd_id = 0x1505,
+		.resp_len = 2,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_SET_RF_MODE,
+		.cmd_id = 0x1509,
+		.resp_len = 2,
+		.ops = atenl_hqa_set_rf_mode,
+	},
+	{
+		.cmd_id = 0x150b,
+		.flags = ATENL_OPS_FLAG_LEGACY,
+	},
+	{
+		.cmd = HQA_CMD_WRITE_BUFFER_DONE,
+		.cmd_id = 0x1511,
+		.resp_len = 2,
+		.ops = atenl_hqa_eeprom_bulk,
+	},
+	{
+		.cmd = HQA_CMD_GET_CHIP_ID,
+		.cmd_id = 0x1514,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_chip_id,
+	},
+	{
+		.cmd = HQA_CMD_GET_SUB_CHIP_ID,
+		.cmd_id = 0x151b,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_sub_chip_id,
+	},
+	{
+		.cmd = HQA_CMD_GET_RX_INFO,
+		.cmd_id = 0x151c,
+		.ops = atenl_nl_process,
+	},
+	{
+		.cmd = HQA_CMD_GET_RF_CAP,
+		.cmd_id = 0x151e,
+		.resp_len = 10,
+		.ops = atenl_hqa_get_rf_cap,
+	},
+	{
+		.cmd = HQA_CMD_CHECK_EFUSE_MODE_TYPE,
+		.cmd_id = 0x1522,
+		.resp_len = 6,
+		.ops = atenl_hqa_check_efuse_mode,
+	},
+	{
+		.cmd = HQA_CMD_CHECK_EFUSE_MODE_NATIVE,
+		.cmd_id = 0x1523,
+		.resp_len = 6,
+		.ops = atenl_hqa_check_efuse_mode,
+	},
+	{
+		.cmd = HQA_CMD_GET_BAND,
+		.cmd_id = 0x152d,
+		.resp_len = 6,
+		.ops = atenl_hqa_get_band,
+	},
+	{
+		.cmd = HQA_CMD_SET_RU,
+		.cmd_id = 0x1594,
+		.resp_len = 2,
+		.ops = atenl_nl_process_many,
+	},
 };
-#undef ATENL_GROUP
 
-#define ATENL_EXT(_cmd, _resp_len, _ops)	\
-	[HQA_EXT_CMD_##_cmd] = { .resp_len=_resp_len, .ops=_ops }
-static const struct atenl_cmd_ops atenl_ext_ops[] = {
-	ATENL_EXT(SET_CHANNEL, 6, atenl_hqa_set_channel),
-	ATENL_EXT(SET_TX, 6, atenl_nl_process),
-	ATENL_EXT(START_TX, 6, atenl_nl_process),
-	ATENL_EXT(STOP_TX, 6, atenl_nl_process),
-	ATENL_EXT(START_RX, 6, atenl_nl_process),
-	ATENL_EXT(STOP_RX, 6, atenl_nl_process),
-	ATENL_EXT(SET_TX_TIME_OPT, 6, atenl_hqa_tx_time_option),
-	ATENL_EXT(OFF_CH_SCAN, 6, atenl_nl_process),
-	ATENL_EXT(IBF_SET_VAL, 6, atenl_nl_process),
-	ATENL_EXT(IBF_GET_STATUS, 10, atenl_nl_process),
-	ATENL_EXT(IBF_PROF_UPDATE_ALL, 6, atenl_nl_process_many),
+static const struct atenl_ops hqa_ops_ext[] = {
+	{
+		.cmd = HQA_EXT_CMD_SET_CHANNEL,
+		.cmd_id = 0x01,
+		.resp_len = 6,
+		.ops = atenl_hqa_set_channel,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_SET_TX,
+		.cmd_id = 0x02,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_START_TX,
+		.cmd_id = 0x03,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_START_RX,
+		.cmd_id = 0x04,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_STOP_TX,
+		.cmd_id = 0x05,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_STOP_RX,
+		.cmd_id = 0x06,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_IBF_SET_VAL,
+		.cmd_id = 0x08,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_IBF_GET_STATUS,
+		.cmd_id = 0x09,
+		.resp_len = 10,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_IBF_PROF_UPDATE_ALL,
+		.cmd_id = 0x0c,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_SET_TX_TIME_OPT,
+		.cmd_id = 0x26,
+		.resp_len = 6,
+		.ops = atenl_hqa_tx_time_option,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
+	{
+		.cmd = HQA_EXT_CMD_OFF_CH_SCAN,
+		.cmd_id = 0x27,
+		.resp_len = 6,
+		.ops = atenl_nl_process,
+		.flags = ATENL_OPS_FLAG_EXT_CMD,
+	},
 };
-#undef ATENL_EXT
 
-int atenl_hqa_recv(struct atenl *an, struct atenl_data *data)
+static const struct atenl_ops *
+atenl_get_ops(struct atenl_data *data)
 {
+	const struct atenl_ops *group;
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
-	u16 cmd_type = ntohs(hdr->cmd_type);
-	int fd = an->pipefd[PIPE_WRITE];
-	int ret;
+	u16 cmd_id = ntohs(hdr->cmd_id), id = cmd_id;
+	int size, low = 0, high;
 
-	if (ntohl(hdr->magic_no) != RACFG_MAGIC_NO)
-		return -EINVAL;
-
-	if (FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_ETHREQ &&
-	    FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_PLATFORM_MODULE) {
-		atenl_err("[%d]%s: cmd type error = 0x%x\n", getpid(), __func__, cmd_type);
-		return -EINVAL;
+	switch (cmd_id) {
+	case 0x1600:
+		group = hqa_ops_ext;
+		size = ARRAY_SIZE(hqa_ops_ext);
+		break;
+	default:
+		group = hqa_ops;
+		size = ARRAY_SIZE(hqa_ops);
+		break;
 	}
 
+	if (group[0].flags & ATENL_OPS_FLAG_EXT_CMD)
+		id = ntohl(*(u32 *)hdr->data);
+
-	atenl_dbg("[%d]%s: recv cmd type = 0x%x, id = 0x%x\n",
-		  getpid(), __func__, cmd_type, ntohs(hdr->cmd_id));
+	/* binary search */
+	high = size - 1;
+	while (low <= high) {
+		int mid = low + (high - low) / 2;
 
-	ret = write(fd, data, data->len);
-	if (ret < 0) {
-		perror("pipe write");
-		return ret;
+		if (group[mid].cmd_id == id)
+			return &group[mid];
+		else if (group[mid].cmd_id > id)
+			high = mid - 1;
+		else
+			low = mid + 1;
 	}
 
-	return 0;
+	return NULL;
 }
 
-int atenl_hqa_proc_cmd(struct atenl *an, struct atenl_data *data)
+static int
+atenl_hqa_handler(struct atenl *an, struct atenl_data *data)
 {
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
-	const struct atenl_cmd_ops *ops;
+	const struct atenl_ops *ops = NULL;
 	u16 cmd_id = ntohs(hdr->cmd_id);
 	u16 status = 0;
 
-	data->cmd = atenl_get_cmd_by_id(cmd_id);
-	if (data->cmd == HQA_CMD_ERR) {
-		atenl_err("Unknown command id: 0x%04x\n", cmd_id);
+	atenl_dbg("handle command: 0x%x\n", cmd_id);
+
+	ops = atenl_get_ops(data);
+	if (!ops || (!ops->ops && !ops->flags)) {
+		atenl_err("Unknown command id: 0x%x\n", cmd_id);
 		goto done;
 	}
 
-	if (data->cmd == HQA_CMD_EXT) {
-		data->ext_id = ntohl(*(u32 *)hdr->data);
-		data->ext_cmd = atenl_get_ext_cmd(data->ext_id);
-		if (data->ext_cmd == HQA_EXT_CMD_UNSPEC) {
-			atenl_err("Unknown ext command id: 0x%04x\n", data->ext_id);
-			goto done;
-		}
-
-		ops = &atenl_ext_ops[data->ext_cmd];
-	} else {
-		ops = &atenl_ops[data->cmd];
+	data->cmd = ops->cmd;
+	data->cmd_id = ops->cmd_id;
+	if (ops->flags & ATENL_OPS_FLAG_EXT_CMD) {
+		data->ext_cmd = ops->cmd;
+		data->ext_id = ops->cmd_id;
 	}
 
+	if (ops->flags & ATENL_OPS_FLAG_SKIP)
+		goto done;
+
-	atenl_dbg_print_data(data, __func__,
+	atenl_dbg_print_data(data->buf, __func__,
 			     ntohs(hdr->len) + ETH_HLEN + RACFG_HLEN);
 	if (ops->ops)
 		status = htons(ops->ops(an, data));
 	if (ops->resp_len)
 		hdr->len = htons(ops->resp_len);
 
-	*(u16 *)hdr->data = status;
-
 done:
+	*(u16 *)hdr->data = status;
 	data->len = ntohs(hdr->len) + ETH_HLEN + RACFG_HLEN;
 	hdr->cmd_type |= ~htons(RACFG_CMD_TYPE_MASK);
 
 	return 0;
 }
+
+int atenl_hqa_proc_cmd(struct atenl *an)
+{
+	struct atenl_data *data;
+	struct atenl_cmd_hdr *hdr;
+	u16 cmd_type;
+	int ret = -EINVAL;
+
+	data = calloc(1, sizeof(struct atenl_data));
+	if (!data)
+		return -ENOMEM;
+
+	ret = atenl_eth_recv(an, data);
+	if (ret)
+		goto out;
+
+	hdr = atenl_hdr(data);
+	if (ntohl(hdr->magic_no) != RACFG_MAGIC_NO)
+		goto out;
+
+	cmd_type = ntohs(hdr->cmd_type);
+	if (FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_ETHREQ &&
+	    FIELD_GET(RACFG_CMD_TYPE_MASK, cmd_type) != RACFG_CMD_TYPE_PLATFORM_MODULE) {
+		atenl_err("cmd type error = 0x%x\n", cmd_type);
+		goto out;
+	}
+
+	ret = atenl_hqa_handler(an, data);
+	if (ret)
+		goto out;
+
+	ret = atenl_eth_send(an, data);
+	if (ret)
+		goto out;
+
+	ret = 0;
+out:
+	free(data);
+
+	return ret;
+}
diff --git a/feed/atenl/src/main.c b/feed/atenl/src/main.c
index ec0a320..9667da3 100644
--- a/feed/atenl/src/main.c
+++ b/feed/atenl/src/main.c
@@ -58,7 +58,7 @@
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage:\n");
+	printf("Usage:\n");
 	printf("  %s [-u] [-i phyX]\n", progname);
 	printf("options:\n"
 	       "  -h = show help text\n"
@@ -70,13 +70,12 @@
 	exit(EXIT_FAILURE);
 }
 
-static int atenl_parent_work(struct atenl *an)
+static void atenl_handler_run(struct atenl *an)
 {
-	int sock_eth = an->sock_eth;
-	int count, ret = 0;
+	int count, sock_eth = an->sock_eth;
 	fd_set readfds;
 
-	atenl_info("[%d]%s: start for receiving HQA commands\n", getpid(), __func__);
+	atenl_info("Start atenl HQA command handler\n");
 
 	while (atenl_enable) {
 		FD_ZERO(&readfds);
@@ -85,92 +84,18 @@
 
 		if (count < 0) {
 			atenl_err("%s: select failed, %s\n", __func__, strerror(errno));
-			continue;
 		} else if (count == 0) {
 			usleep(1000);
-			continue;
 		} else {
-			if (FD_ISSET(sock_eth, &readfds)) {
-				struct atenl_data *data = calloc(1, sizeof(struct atenl_data));
-
-				ret = atenl_eth_recv(an, data);
-				if (ret) {
-					kill(an->child_pid, SIGUSR1);
-					return ret;
-				}
-
-				ret = atenl_hqa_recv(an, data);
-				if (ret < 0) {
-					kill(an->child_pid, SIGUSR1);
-					return ret;
-				}
-
-				free(data);
-			}
+			if (!FD_ISSET(sock_eth, &readfds))
+				continue;
+			atenl_hqa_proc_cmd(an);
 		}
 	}
 
-	atenl_info("[%d]%s: parent work end\n", getpid(), __func__);
-
-	return ret;
+	atenl_dbg("HQA command handler end\n");
 }
 
-static int atenl_child_work(struct atenl *an)
-{
-	int rfd = an->pipefd[PIPE_READ], count;
-	int ret = 0;
-	fd_set readfds;
-
-	atenl_info("[%d]%s: start for sending back results\n", getpid(), __func__);
-
-	while (atenl_enable) {
-		struct atenl_data *data = calloc(1, sizeof(struct atenl_data));
-
-		FD_ZERO(&readfds);
-		FD_SET(rfd, &readfds);
-
-		count = select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
-
-		if (count < 0) {
-			atenl_err("%s: select failed, %s\n", __func__, strerror(errno));
-			continue;
-		} else if (count == 0) {
-			usleep(1000);
-			continue;
-		} else {
-			if (FD_ISSET(rfd, &readfds)) {
-				count = read(rfd, data->buf, RACFG_PKT_MAX_SIZE);
-				atenl_dbg("[%d]PIPE Read %d bytes\n", getpid(), count);
-
-				if (count < 0) {
-					atenl_info("%s: %s\n", __func__, strerror(errno));
-				} else if (count == 0) {
-					continue;
-				} else {
-					int ret;
-
-					ret = atenl_hqa_proc_cmd(an, data);
-					if (ret) {
-						kill(getppid(), SIGUSR2);
-						goto out;
-					}
-
-					ret = atenl_eth_send(an, data);
-					if (ret) {
-						kill(getppid(), SIGUSR2);
-						goto out;
-					}
-				}
-			}
-		}
-	}
-
-out:
-	atenl_info("[%d]%s: child work end\n", getpid(), __func__);
-
-	return ret;
-}
-
 int main(int argc, char **argv)
 {
 	int opt, phy_idx, ret = 0;
@@ -191,8 +116,7 @@
 		switch (opt) {
 			case 'h':
 				usage();
-				free(an);
-				return 0;
+				goto out;
 			case 'i':
 				phy = optarg;
 				break;
@@ -204,16 +128,15 @@
 				cmd = optarg;
 				break;
 			default:
-				fprintf(stderr, "Not supported option\n");
-				break;
+				atenl_err("Not supported option: %c\n", opt);
+				goto out;
 		}
 	}
 
 	phy_idx = phy_lookup_idx(phy);
 	if (phy_idx < 0 || phy_idx > UCHAR_MAX) {
-		fprintf(stderr, "Could not find phy '%s'\n", phy);
-		free(an);
-		return 2;
+		atenl_err("Could not find phy '%s'\n", phy);
+		goto out;
 	}
 
 	if (cmd) {
@@ -226,8 +149,6 @@
 
 	/* background ourself */
 	if (!fork()) {
-		pid_t pid;
-
 		ret = atenl_eeprom_init(an, phy_idx);
 		if (ret)
 			goto out;
@@ -236,28 +157,7 @@
 		if (ret)
 			goto out;
 
-		ret = pipe(an->pipefd);
-		if (ret) {
-			perror("Pipe");
-			goto out;
-		}
-
-		pid = fork();
-		an->child_pid = pid;
-		if (pid < 0) {
-			perror("Fork");
-			ret = pid;
-			goto out;
-		} else if (pid == 0) {
-			close(an->pipefd[PIPE_WRITE]);
-			atenl_child_work(an);
-		} else {
-			int status;
-
-			close(an->pipefd[PIPE_READ]);
-			atenl_parent_work(an);
-			waitpid(pid, &status, 0);
-		}
+		atenl_handler_run(an);
 	} else {
 		usleep(800000);
 	}
diff --git a/feed/atenl/src/nl.c b/feed/atenl/src/nl.c
index e1d0d0f..368ffbd 100644
--- a/feed/atenl/src/nl.c
+++ b/feed/atenl/src/nl.c
@@ -214,28 +214,25 @@
 	u8 *addr1 = hdr->data + 36;
 	u8 *addr2 = addr1 + ETH_ALEN;
 	u8 *addr3 = addr2 + ETH_ALEN;
-	u8 default_addr[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
+	u8 def_mac[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
 	void *ptr, *a;
 
+	if (get_band_val(an, an->cur_band, use_tx_time))
+		set_band_val(an, an->cur_band, tx_time, ntohl(v[7]));
+	else
+		set_band_val(an, an->cur_band, tx_mpdu_len, ntohl(v[7]));
+
 	ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
 	if (!ptr)
 		return -ENOMEM;
 
-	if (get_band_val(an, an->cur_band, use_tx_time))
-		nla_put_u32(msg, MT76_TM_ATTR_TX_TIME, ntohl(v[7]));
-	else
-		nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, ntohl(v[7]));
-
 	a = nla_nest_start(msg, MT76_TM_ATTR_MAC_ADDRS);
 	if (!a)
 		return -ENOMEM;
 
-	if (is_multicast_ether_addr(addr1))
-		nla_put(msg, 0, ETH_ALEN, default_addr);
-	else
-		nla_put(msg, 0, ETH_ALEN, addr1);
-	nla_put(msg, 1, ETH_ALEN, addr2);
-	nla_put(msg, 2, ETH_ALEN, addr3);
+	nla_put(msg, 0, ETH_ALEN, use_default_addr(addr1) ? def_mac : addr1);
+	nla_put(msg, 1, ETH_ALEN, use_default_addr(addr2) ? def_mac : addr2);
+	nla_put(msg, 2, ETH_ALEN, use_default_addr(addr3) ? def_mac : addr3);
 
 	nla_nest_end(msg, a);
 
@@ -288,6 +285,13 @@
 		nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, ntohl(v[8]));
 		nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, ntohl(v[15]));
 
+		if (get_band_val(an, band, use_tx_time))
+			nla_put_u32(msg, MT76_TM_ATTR_TX_TIME,
+				    get_band_val(an, band, tx_time));
+		else
+			nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH,
+				    get_band_val(an, band, tx_mpdu_len));
+
 		/* for chips after 7915, tx need to use at least wcid = 1 */
 		if (!is_mt7915(an) && !aid)
 			aid = 1;
@@ -405,7 +409,7 @@
 
 	nla_nest_end(msg, ptr);
 
-	memcpy(hdr->data + 2, &data->ext_id, 4);
+	*(u32 *)(hdr->data + 2) = data->ext_id;
 
 	return 0;
 }
@@ -421,7 +425,7 @@
 
 	nl_attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
 	if (!nl_attr) {
-		fprintf(stderr, "Testdata attribute not found\n");
+		atenl_err("Testdata attribute not found\n");
 		return NL_SKIP;
 	}
 
@@ -572,7 +576,7 @@
 
 	nl_attr = unl_find_attr(&nl_priv->unl, msg, NL80211_ATTR_TESTDATA);
 	if (!nl_attr) {
-		fprintf(stderr, "Testdata attribute not found\n");
+		atenl_err("Testdata attribute not found\n");
 		return NL_SKIP;
 	}
 
@@ -585,9 +589,10 @@
 	rx_cur.len_mismatch = nla_get_u64(tb2[MT76_TM_STATS_ATTR_RX_LEN_MISMATCH]);
 	rx_cur.ok_cnt = rx_cur.total - rx_cur.err_cnt - rx_cur.len_mismatch;
 
-	if (!anb->reset_rx_cnt) {
+	if (!anb->reset_rx_cnt ||
+	    get_band_val(an, an->cur_band, cur_state) == MT76_TM_STATE_RX_FRAMES) {
 #define RX_COUNT_DIFF(_field)	\
-	rx_diff._field = (rx_cur._field) - (anb->rx_stat._field)
+	rx_diff._field = (rx_cur._field) - (anb->rx_stat._field);
 		RX_COUNT_DIFF(total);
 		RX_COUNT_DIFF(err_cnt);
 		RX_COUNT_DIFF(len_mismatch);
@@ -691,7 +696,7 @@
 		char buf[10];
 
 		if (unl_genl_init(&nl_priv->unl, "nl80211") < 0) {
-			fprintf(stderr, "Failed to connect to nl80211\n");
+			atenl_err("Failed to connect to nl80211\n");
 			return 2;
 		}
 
@@ -728,8 +733,107 @@
 	return 0;
 }
 
-static int atenl_nl_ibf_set_val(struct atenl *an, struct atenl_data *data,
-				struct atenl_nl_priv *nl_priv)
+static int
+atenl_nl_ibf_init(struct atenl *an, u8 band)
+{
+	struct atenl_nl_priv nl_priv = {};
+	struct nl_msg *msg;
+	void *ptr, *a;
+	int ret;
+
+	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+		atenl_err("Failed to connect to nl80211\n");
+		return 2;
+	}
+
+	msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+	nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, band, phy_idx));
+
+	ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, MT76_TM_TX_MODE_HT);
+	nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, an->ibf_mcs);
+	nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, an->ibf_ant);
+	nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_INIT);
+
+	a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+	if (!a) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	nla_put_u16(msg, 0, 1);
+	nla_nest_end(msg, a);
+
+	nla_nest_end(msg, ptr);
+
+	ret = unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+out:
+	unl_free(&nl_priv.unl);
+	return ret;
+}
+
+static int
+atenl_nl_ibf_e2p_update(struct atenl *an)
+{
+	struct atenl_nl_priv nl_priv = {};
+	struct nl_msg *msg;
+	void *ptr, *a;
+	int ret;
+
+	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+		atenl_err("Failed to connect to nl80211\n");
+		return 2;
+	}
+
+	msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+	nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, an->cur_band, phy_idx));
+
+	ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_E2P_UPDATE);
+	a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
+	if (!a) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	nla_put_u16(msg, 0, 0);
+	nla_nest_end(msg, a);
+
+	nla_nest_end(msg, ptr);
+
+	ret = unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+out:
+	unl_free(&nl_priv.unl);
+	return ret;
+}
+
+static void
+atenl_get_ibf_cal_result(struct atenl *an)
+{
+	u16 offset;
+
+	if (an->adie_id == 0x7975)
+		offset = 0x651;
+	else if (an->adie_id == 0x7976)
+		offset = 0x60a;
+
+	/* per group size = 40, for group 0-8 */
+	atenl_eeprom_read_from_driver(an, offset, 40 * 9);
+}
+
+static int
+atenl_nl_ibf_set_val(struct atenl *an, struct atenl_data *data,
+		     struct atenl_nl_priv *nl_priv)
 {
 #define MT_IBF(_act)	MT76_TM_TXBF_ACT_##_act
 	static const u8 bf_act_map[] = {
@@ -744,6 +848,7 @@
 	u32 *v = (u32 *)(hdr->data + 4);
 	u32 action = ntohl(v[0]);
 	u16 val[8];
+	u8 tmp_ant;
 	void *ptr, *a;
 	char cmd[64];
 	int i;
@@ -759,24 +864,15 @@
 		return -ENOMEM;
 
 	switch (action) {
-	case TXBF_ACT_INIT:
-		nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, MT76_TM_TX_MODE_HT);
-		nla_put_u8(msg, MT76_TM_ATTR_AID, 1);
-		nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_INIT);
-
-		a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
-		if (!a)
-			return -ENOMEM;
-		nla_put_u16(msg, 0, 1);
-		if (!val[0])
-			nla_put_u16(msg, 1, 1);	/* init */
-		nla_nest_end(msg, a);
-		break;
 	case TXBF_ACT_CHANNEL:
-		sprintf(cmd, "iw dev mon%d set channel %u HT20",
-			get_band_val(an, an->cur_band, phy_idx), val[0]);
-		system(cmd);
-		printf("%s: %s", __func__, cmd);
+		an->cur_band = val[1];
+		/* a sanity to prevent script band idx error */
+		if (val[0] > 14)
+			an->cur_band = 1;
+		atenl_nl_ibf_init(an, an->cur_band);
+		atenl_set_channel(an, 0, an->cur_band, val[0], 0, 0);
+
+		nla_put_u8(msg, MT76_TM_ATTR_AID, 0);
 		nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_UPDATE_CH);
 		a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
 		if (!a)
@@ -785,10 +881,14 @@
 		nla_nest_end(msg, a);
 		break;
 	case TXBF_ACT_MCS:
-		nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, val[0]);
-		nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, (1 << DIV_ROUND_UP(val[0], 8)) - 1);
-		break;
-	case TXBF_ACT_POWER:
+		tmp_ant = (1 << DIV_ROUND_UP(val[0], 8)) - 1 ?: 1;
+		/* sometimes the correct band idx will be set after this action,
+		 * so maintain a temp variable to allow mcs update in anthor action.
+		 */
+		an->ibf_mcs = val[0];
+		an->ibf_ant = tmp_ant;
+		nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, an->ibf_mcs);
+		nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, an->ibf_ant);
 		break;
 	case TXBF_ACT_TX_ANT:
 		nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, val[0]);
@@ -801,8 +901,9 @@
 		break;
 	case TXBF_ACT_TX_PKT:
 		nla_put_u8(msg, MT76_TM_ATTR_AID, val[1]);
-		nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, 10000000);
 		nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_TX_PREP);
+		nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, 10000000);
+		nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, 1024);
 		a = nla_nest_start(msg, MT76_TM_ATTR_TXBF_PARAM);
 		if (!a)
 			return -ENOMEM;
@@ -814,6 +915,7 @@
 		atenl_set_attr_state(an, msg, an->cur_band, MT76_TM_STATE_TX_FRAMES);
 		break;
 	case TXBF_ACT_IBF_PHASE_COMP:
+		nla_put_u8(msg, MT76_TM_ATTR_AID, 1);
 	case TXBF_ACT_IBF_PROF_UPDATE:
 	case TXBF_ACT_EBF_PROF_UPDATE:
 	case TXBF_ACT_IBF_PHASE_CAL:
@@ -827,8 +929,8 @@
 		nla_nest_end(msg, a);
 		break;
 	case TXBF_ACT_IBF_PHASE_E2P_UPDATE:
-		atenl_eeprom_read_from_driver(an, 0x651, 0x28 * 9);
-		atenl_eeprom_write_mtd(an);
+		atenl_nl_ibf_e2p_update(an);
+		atenl_get_ibf_cal_result(an);
 
 		nla_put_u8(msg, MT76_TM_ATTR_AID, 0);
 		nla_put_u8(msg, MT76_TM_ATTR_TXBF_ACT, MT76_TM_TXBF_ACT_INIT);
@@ -839,13 +941,15 @@
 		nla_put_u16(msg, 0, 0);
 		nla_nest_end(msg, a);
 		break;
+	case TXBF_ACT_INIT:
+	case TXBF_ACT_POWER:
 	default:
 		break;
 	}
 
 	nla_nest_end(msg, ptr);
 
-	memcpy(hdr->data + 2, &data->ext_id, 4);
+	*(u32 *)(hdr->data + 2) = data->ext_id;
 
 	return unl_genl_request(&nl_priv->unl, msg, NULL, NULL);
 }
@@ -857,7 +961,7 @@
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
 	u32 status = htonl(1);
 
-	memcpy(hdr->data + 2, &data->ext_id, 4);
+	*(u32 *)(hdr->data + 2) = data->ext_id;
 	memcpy(hdr->data + 6, &status, 4);
 
 	return 0;
@@ -878,7 +982,7 @@
 		int j;
 
 		if (unl_genl_init(&nl_priv->unl, "nl80211") < 0) {
-			fprintf(stderr, "Failed to connect to nl80211\n");
+			atenl_err("Failed to connect to nl80211\n");
 			return 2;
 		}
 
@@ -907,7 +1011,7 @@
 		unl_free(&nl_priv->unl);
 	}
 
-	memcpy(hdr->data + 2, &data->ext_id, 4);
+	*(u32 *)(hdr->data + 2) = data->ext_id;
 
 	return 0;
 }
@@ -954,7 +1058,7 @@
 		ops = &nl_ops[data->cmd];
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return -1;
 	}
 
@@ -973,7 +1077,7 @@
 	}
 
 	if (ret)
-		atenl_err("command process error: %d (%d)\n", data->cmd, data->ext_cmd);
+		atenl_err("command process error: 0x%x (0x%x)\n", data->cmd_id, data->ext_id);
 
 	unl_free(&nl_priv.unl);
 
@@ -1005,7 +1109,7 @@
 	void *ptr;
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return 2;
 	}
 
@@ -1027,6 +1131,35 @@
 	return 0;
 }
 
+int atenl_nl_set_aid(struct atenl *an, u8 band, u8 aid)
+{
+	struct atenl_nl_priv nl_priv = {};
+	struct nl_msg *msg;
+	void *ptr;
+
+	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
+		atenl_err("Failed to connect to nl80211\n");
+		return 2;
+	}
+
+	msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, false);
+	nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, band, phy_idx));
+
+	ptr = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
+	if (!ptr)
+		return -ENOMEM;
+
+	nla_put_u8(msg, MT76_TM_ATTR_AID, aid);
+
+	nla_nest_end(msg, ptr);
+
+	unl_genl_request(&nl_priv.unl, msg, NULL, NULL);
+
+	unl_free(&nl_priv.unl);
+
+	return 0;
+}
+
 static int atenl_nl_check_mtd_cb(struct nl_msg *msg, void *arg)
 {
 	struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
@@ -1054,7 +1187,7 @@
 	struct nl_msg *msg;
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return 2;
 	}
 
@@ -1075,7 +1208,7 @@
 	int i;
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return 2;
 	}
 
@@ -1120,7 +1253,7 @@
 	void *ptr;
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return 2;
 	}
 
@@ -1150,7 +1283,7 @@
 	void *ptr;
 
 	if (unl_genl_init(&nl_priv.unl, "nl80211") < 0) {
-		fprintf(stderr, "Failed to connect to nl80211\n");
+		atenl_err("Failed to connect to nl80211\n");
 		return 2;
 	}
 
diff --git a/feed/atenl/src/nl.h b/feed/atenl/src/nl.h
index 2a0685d..0f21578 100644
--- a/feed/atenl/src/nl.h
+++ b/feed/atenl/src/nl.h
@@ -78,25 +78,23 @@
 	MT76_TM_ATTR_DRV_DATA,
 
 	MT76_TM_ATTR_MAC_ADDRS,
+	MT76_TM_ATTR_AID,
+	MT76_TM_ATTR_RU_ALLOC,
+	MT76_TM_ATTR_RU_IDX,
 
 	MT76_TM_ATTR_EEPROM_ACTION,
 	MT76_TM_ATTR_EEPROM_OFFSET,
 	MT76_TM_ATTR_EEPROM_VAL,
 
 	MT76_TM_ATTR_CFG,
+	MT76_TM_ATTR_TXBF_ACT,
+	MT76_TM_ATTR_TXBF_PARAM,
 
 	MT76_TM_ATTR_OFF_CH_SCAN_CH,
 	MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
 	MT76_TM_ATTR_OFF_CH_SCAN_BW,
 	MT76_TM_ATTR_OFF_CH_SCAN_PATH,
 
-	MT76_TM_ATTR_AID,
-	MT76_TM_ATTR_RU_ALLOC,
-	MT76_TM_ATTR_RU_IDX,
-
-	MT76_TM_ATTR_TXBF_ACT,
-	MT76_TM_ATTR_TXBF_PARAM,
-
 	/* keep last */
 	NUM_MT76_TM_ATTRS,
 	MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
@@ -233,6 +231,7 @@
 	MT76_TM_TXBF_ACT_EBF_PROF_UPDATE,
 	MT76_TM_TXBF_ACT_PHASE_CAL,
 	MT76_TM_TXBF_ACT_PROF_UPDATE_ALL,
+	MT76_TM_TXBF_ACT_E2P_UPDATE,
 
 	/* keep last */
 	NUM_MT76_TM_TXBF_ACT,
diff --git a/feed/atenl/src/util.c b/feed/atenl/src/util.c
index 1b10663..b224040 100644
--- a/feed/atenl/src/util.c
+++ b/feed/atenl/src/util.c
@@ -103,3 +103,115 @@
 
 	return ret;
 }
+
+int atenl_rf_read(struct atenl *an, u32 wf_sel, u32 offset, u32 *res)
+{
+	char dir[64], buf[16];
+	unsigned long val;
+	int fd, ret;
+	u32 regidx;
+
+	/* merge wf_sel and offset into regidx */
+	regidx = FIELD_PREP(GENMASK(31, 28), wf_sel) |
+		 FIELD_PREP(GENMASK(27, 0), offset);
+
+	/* write regidx */
+	ret = snprintf(dir, sizeof(dir),
+		       "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+		       get_band_val(an, 0, phy_idx));
+	if (snprintf_error(sizeof(dir), ret))
+		return ret;
+
+	fd = open(dir, O_WRONLY);
+	if (fd < 0)
+		return fd;
+
+	ret = snprintf(buf, sizeof(buf), "0x%x", regidx);
+	if (snprintf_error(sizeof(buf), ret))
+		goto out;
+
+	lseek(fd, 0, SEEK_SET);
+	write(fd, buf, sizeof(buf));
+	close(fd);
+
+	/* read from rf_regval */
+	ret = snprintf(dir, sizeof(dir),
+		       "/sys/kernel/debug/ieee80211/phy%d/mt76/rf_regval",
+		       get_band_val(an, 0, phy_idx));
+	if (snprintf_error(sizeof(dir), ret))
+		return ret;
+
+	fd = open(dir, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	ret = read(fd, buf, sizeof(buf) - 1);
+	if (ret < 0)
+		goto out;
+	buf[ret] = 0;
+
+	val = strtoul(buf, NULL, 16);
+	if (val > (u32) -1)
+		return -EINVAL;
+
+	*res = val;
+	ret = 0;
+out:
+	close(fd);
+
+	return ret;
+}
+
+int atenl_rf_write(struct atenl *an, u32 wf_sel, u32 offset, u32 val)
+{
+	char dir[64], buf[16];
+	int fd, ret;
+	u32 regidx;
+
+	/* merge wf_sel and offset into regidx */
+	regidx = FIELD_PREP(GENMASK(31, 28), wf_sel) |
+		 FIELD_PREP(GENMASK(27, 0), offset);
+
+	/* write regidx */
+	ret = snprintf(dir, sizeof(dir),
+		       "/sys/kernel/debug/ieee80211/phy%d/mt76/regidx",
+		       get_band_val(an, 0, phy_idx));
+	if (snprintf_error(sizeof(dir), ret))
+		return ret;
+
+	fd = open(dir, O_WRONLY);
+	if (fd < 0)
+		return fd;
+
+	ret = snprintf(buf, sizeof(buf), "0x%x", regidx);
+	if (snprintf_error(sizeof(buf), ret))
+		goto out;
+
+	lseek(fd, 0, SEEK_SET);
+	write(fd, buf, sizeof(buf));
+	close(fd);
+
+	/* write value into rf_val */
+	ret = snprintf(dir, sizeof(dir),
+		       "/sys/kernel/debug/ieee80211/phy%d/mt76/rf_regval",
+		       get_band_val(an, 0, phy_idx));
+	if (snprintf_error(sizeof(dir), ret))
+		return ret;
+
+	fd = open(dir, O_WRONLY);
+	if (fd < 0)
+		return fd;
+
+	ret = snprintf(buf, sizeof(buf), "0x%x", val);
+	if (snprintf_error(sizeof(buf), ret))
+		goto out;
+	buf[ret] = 0;
+
+	lseek(fd, 0, SEEK_SET);
+	write(fd, buf, sizeof(buf));
+	ret = 0;
+out:
+	close(fd);
+
+	return ret;
+}
diff --git a/feed/atenl/src/util.h b/feed/atenl/src/util.h
index a02b956..45c97b5 100644
--- a/feed/atenl/src/util.h
+++ b/feed/atenl/src/util.h
@@ -43,6 +43,10 @@
 	({								\
 		(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask));	\
 	})
+#define FIELD_PREP(_mask, _val)						\
+	({								\
+		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\
+	})
 
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
@@ -78,6 +82,24 @@
 	return 0x01 & addr[0];
 }
 
+static inline bool is_unicast_ether_addr(const u8 *addr)
+{
+	return !is_multicast_ether_addr(addr);
+}
+
+static inline bool is_zero_ether_addr(const u8 *addr)
+{
+	return (*(const u16 *)(addr + 0) |
+		*(const u16 *)(addr + 2) |
+		*(const u16 *)(addr + 4)) == 0;
+}
+
+static inline bool use_default_addr(const u8 *addr)
+{
+	return !is_unicast_ether_addr(addr) ||
+	       is_zero_ether_addr(addr);
+}
+
 static inline void eth_broadcast_addr(u8 *addr)
 {
 	memset(addr, 0xff, ETH_ALEN);