Merge "[[RDKB]rdkb development:MRU]"
diff --git a/src/logan_wifi/wifi_hal.c b/src/logan_wifi/wifi_hal.c
index a9bd236..6b593a9 100644
--- a/src/logan_wifi/wifi_hal.c
+++ b/src/logan_wifi/wifi_hal.c
@@ -286,6 +286,23 @@
 	wdev_extended_ap_metric ext_ap_metric;
 } wdev_ap_metric;
 
+enum mld_type {
+	AP_MLD_SINGLE_LINK,
+	AP_MLD_MULTI_LINK,
+};
+
+struct multi_link_device {
+	unsigned char mld_mac[6];
+	unsigned char mld_index;
+//	enum mld_type type;
+	unsigned char affiliated_ap_bitmap[6];
+};
+
+struct mld_configuration {
+	unsigned char valid_mld_bitmap[9];
+	struct multi_link_device mld[66]; /*0,65 - invalid, 1~16 multi-link mld, 17-64 single link mld*/
+};
+struct mld_configuration mld_config;
 
 static int util_unii_5g_centerfreq(const char *ht_mode, int channel);
 static int util_unii_6g_centerfreq(const char *ht_mode, int channel);
@@ -527,6 +544,37 @@
 }
 #endif /* WIFI_HAL_VERSION_3 */
 
+#define _syscmd_secure(retBuf, retBufSize, fmt, args...) \
+	({ \
+		FILE *f;	\
+		char *ptr = retBuf; \
+		int bufSize = retBufSize, bufbytes = 0, readbytes = 0, cmd_ret = -1;	\
+		WIFI_ENTRY_EXIT_DEBUG("Inside %s:%d\n",__func__, __LINE__);	\
+		f = v_secure_popen("r", fmt, ##args);	\
+		if(f) {	\
+			while(!feof(f))	\
+			{	\
+				*ptr = 0;	\
+				if(bufSize>=128) {	\
+					bufbytes=128;	\
+				} else {	\
+					bufbytes=bufSize-1;	\
+				}	\
+				if (fgets(ptr,bufbytes,f) == NULL)	\
+					break;	\
+				readbytes=strlen(ptr);	\
+				if(!readbytes)	\
+					break;	\
+				bufSize-=readbytes;	\
+				ptr += readbytes;	\
+			}	\
+			cmd_ret = v_secure_pclose(f);	\
+			retBuf[retBufSize-1]=0;	\
+		}	\
+		WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);	\
+		cmd_ret;	\
+	})
+
 
 static char l1profile[32] = "/etc/wireless/l1profile.dat";
 char main_prefix[MAX_NUM_RADIOS][IFNAMSIZ];
@@ -537,7 +585,615 @@
 
 static int array_index_to_vap_index(UINT radioIndex, int arrayIndex);
 static int vap_index_to_array_index(int vapIndex, int *radioIndex, int *arrayIndex);
+static int wifi_datfileRead(char *conf_file, char *param, char *output, int output_size);
+int hwaddr_aton2(const char *txt, unsigned char *addr);
+static int wifi_GetInterfaceName(int apIndex, char *interface_name);
+INT wifi_getMaxRadioNumber(INT *max_radio_num);
+static int wifi_BandProfileRead(int card_idx,
+								int radio_idx,
+								char *param,
+								char *output,
+								int output_size,
+								char *default_value);
+static int array_index_to_vap_index(UINT radioIndex, int arrayIndex);
+struct params
+{
+	char * name;
+	char * value;
+};
+static int wifi_datfileWrite(char *conf_file, struct params *list, int item_count);
+
+#ifdef WIFI_HAL_VERSION_3
+#define MAX_ML_MLD_CNT	16	/*Max multi-link MLD*/
+//#define MAX_SL_MLD_CNT	48	/*MAX single-link MLD*/
+
+static void mld_set(unsigned char mld_index, unsigned char set)
+{
+	mld_config.valid_mld_bitmap[mld_index / 8] |= set ? (1 << (mld_index % 8)) : ~(1 << (mld_index % 8));
+}
+
+static unsigned char mld_test(unsigned char mld_index)
+{
+	return mld_config.valid_mld_bitmap[mld_index / 8] & (1 << (mld_index % 8));
+}
+
+static void mld_ap_set(struct multi_link_device *mld, unsigned char ap_index, unsigned char set)
+{
+	mld->affiliated_ap_bitmap[ap_index / 8] |= set ? (1 << (ap_index % 8)) : ~(1 << (ap_index % 8));
+}
+
+static unsigned char mld_ap_test(struct multi_link_device *mld, unsigned char ap_index)
+{
+	return mld->affiliated_ap_bitmap[ap_index / 8] & (1 << (ap_index % 8));
+}
+
+static int eht_mld_config_init(void)
+{
+	char config_file[128] = {0}, str_mldgroup[256], *buf, mac_str[32] = {0};
+	int res, band, bss_idx;
+	char *token;
+	long mld_index;
+	unsigned char ap_index;
+	struct multi_link_device *mld;
+	char *mld_start, *mldaddr_start, *mldaddr_end;
+
+	buf = (char*)malloc(4096);
+	if (!buf) {
+		wifi_debug(DEBUG_ERROR, "fail to allocate memory\n");
+		return RETURN_ERR;
+	}
+	wifi_debug(DEBUG_ERROR, "==========>\n");
+
+	memset(&mld_config, 0, sizeof(mld_config));
+	for (band = 0; band < MAX_NUM_RADIOS; band++) {
+		res = snprintf(config_file, sizeof(config_file), "%s%d.dat", LOGAN_DAT_FILE, band);
+		if (os_snprintf_error(sizeof(config_file), res)) {
+			wifi_debug(DEBUG_ERROR, "Unexpected snprintf fail\n");
+			free(buf);
+			return RETURN_ERR;
+		}
+
+		wifi_datfileRead(config_file, "MldGroup", str_mldgroup, sizeof(str_mldgroup));
+		token = strtok(str_mldgroup, ";");
+		bss_idx = 0;
+		while(token != NULL && bss_idx < 16) {
+			if (hal_strtol(token, 10, &mld_index) < 0) {
+				wifi_debug(DEBUG_ERROR, "strtol fail\n");
+				break;
+			}
+
+			if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+				wifi_debug(DEBUG_ERROR, "invalid mld_index %ld, skip it.\n", mld_index);
+				bss_idx++;
+				token = strtok(NULL, ";");
+				continue;
+			}
+
+			mld_set(mld_index, 1);
+			ap_index = array_index_to_vap_index(band, bss_idx);
+
+			mld = &(mld_config.mld[mld_index]);
+			mld->mld_index = mld_index;
+			/* need to fulfill mld mac later.
+			memcpy(mld->mld_mac, mac, sizeof(mld->mld_mac));
+			*/
+//			mld->type = mld_index <= MAX_ML_MLD_CNT ? AP_MLD_MULTI_LINK : AP_MLD_SINGLE_LINK;
+			mld_ap_set(mld, ap_index, 1);
+			bss_idx++;
+			token = strtok(NULL, ";");
+			wifi_debug(DEBUG_ERROR, "mld[%ld] affiliated ap[%d].\n", mld_index, ap_index);
+		}
+	}
+
+	res = _syscmd_secure(buf, 4096,
+		"mwctl ra0 show bssmngr;dmesg | tail -500 | grep -e \"MLD\" -e \"bss_mngr_con_info_show\"");
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "_syscmd_secure fail\n");
+		free(buf);
+		return RETURN_ERR;
+	}
+
+	/*fulfill mld mac address from bssmngr information*/
+	mld_start = strstr(buf, "bss_mngr_con_info_show");
+	if (!mld_start) {
+		wifi_debug(DEBUG_ERROR, "fail to find string \"bss_mngr_con_info_show\"\n");
+		printf("*************************\n");
+		printf("buf:%s\n", buf);
+		printf("*************************\n");
+		free(buf);
+		return RETURN_ERR;
+	}
+	mld_start = strstr(mld_start, "MLD[");
+	if (!mld_start) {
+		printf("*************************\n");
+		printf("mld_start:%s\n", buf);
+		printf("*************************\n");
+	}
+	while (mld_start) {
+		if (hal_strtol(mld_start + 4, 10, &mld_index) < 0) {
+			wifi_debug(DEBUG_ERROR, "strtol fail\n");
+			free(buf);
+			return RETURN_ERR;
+		}
+
+		if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+			wifi_debug(DEBUG_ERROR, "invalid mld_index %ld\n", mld_index);
+			mld_start = strstr(mld_start + 4, "MLD[");
+			continue;
+		}
+
+		if (!mld_test(mld_index)) {
+			wifi_debug(DEBUG_ERROR, "mld(%ld) is not obvoiusly config in dat file, stll maintain it!",
+				mld_index);
+			mld_set(mld_index, 1);
+		}
+
+		mld = &(mld_config.mld[mld_index]);
+		mldaddr_start = strstr(mld_start, "MLD Addr:");
+		if (!mldaddr_start) {
+			wifi_debug(DEBUG_ERROR, "invalid mld address from bssmngr, mld_index=%ld\n", mld_index);
+			free(buf);
+			return RETURN_ERR;
+		}
+		mldaddr_end = strstr(mldaddr_start, ")");
+		if (!mldaddr_end) {
+			wifi_debug(DEBUG_ERROR, "invalid mld address from bssmngr, mld_index=%ld\n", mld_index);
+			free(buf);
+			return RETURN_ERR;
+		}
+
+		memset(mac_str, 0, sizeof(mac_str));
+		if ((mldaddr_end - mldaddr_start - 10) >= sizeof(mac_str)) {
+			wifi_debug(DEBUG_ERROR, "invalid mld address string from bssmngr, mld_index=%ld\n",
+				mld_index);
+			free(buf);
+			return RETURN_ERR;
+		}
+		strncpy(mac_str, mldaddr_start + 10, mldaddr_end - mldaddr_start - 10);
+		if (!hwaddr_aton2(mac_str, mld->mld_mac)) {
+			wifi_debug(DEBUG_ERROR, "invalid mld address from bssmngr, mld_index=%ld\n", mld_index);
+			free(buf);
+			return RETURN_ERR;
+		}
+
+		mld_start = strstr(mldaddr_end, "MLD[");
+	}
+	wifi_debug(DEBUG_ERROR, "<==========\n");
+
+	free(buf);
+	return RETURN_OK;
+}
+
+static unsigned char mld_ap_test_all_mlds(unsigned char ap_index)
+{
+	unsigned char mld_index;
+	struct multi_link_device *mld;
+
+	for (mld_index = 1; mld_index <= MAX_ML_MLD_CNT; mld_index++) {
+
+		if (!mld_test(mld_index))
+			continue;
+
+		mld = &(mld_config.mld[mld_index]);
+
+		if (mld_ap_test(mld, ap_index))
+			return mld_index;
+	}
+
+	return 0;
+}
+
+static void mld_info_display(void)
+{
+	unsigned char mld_index, ap_index;
+	struct multi_link_device *mld;
+	char interface_name[IF_NAME_SIZE] = {0};
+
+	wifi_debug(DEBUG_ERROR, "==========>\n");
+	for (mld_index = 1; mld_index <= MAX_ML_MLD_CNT; mld_index++) {
+		if (!mld_test(mld_index))
+			continue;
+
+		mld = &(mld_config.mld[mld_index]);
+
+		printf("MLD[%02d]: %02x:%02x:%02x:%02x:%02x:%02x\n\tAffiliated AP:\n", (int)(mld->mld_index),
+			mld->mld_mac[0], mld->mld_mac[1], mld->mld_mac[2],
+			mld->mld_mac[3], mld->mld_mac[4], mld->mld_mac[5]);
+
+		for (ap_index = 0; ap_index <= MAX_APS; ap_index++) {
+			if (!mld_ap_test(mld, ap_index))
+				continue;
+			if (wifi_GetInterfaceName(ap_index, interface_name) != RETURN_OK) {
+				wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+				continue;
+			}
+			printf("\tap[%d] %s\n", (int)ap_index, interface_name);
+		}
+	}
+	wifi_debug(DEBUG_ERROR, "<==========\n");
+}
+
+INT wifi_eht_create_ap_mld(unsigned char mld_index, unsigned char *mac)
+{
+	int res;
+//	enum mld_type type;
+	struct multi_link_device *mld;
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	if (mld_test(mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld already exist with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+//	type = mld_index <= MAX_ML_MLD_CNT ? AP_MLD_MULTI_LINK : AP_MLD_SINGLE_LINK;
+
+	res = v_secure_system("mwctl ra0 set apmld=create:group=%u,addr=%02x:%02x:%02x:%02x:%02x:%02x",
+		mld_index, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "fail to create mld with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld_set(mld_index, 1);
+
+	mld = &(mld_config.mld[mld_index]);
+	mld->mld_index = mld_index;
+	memcpy(mld->mld_mac, mac, sizeof(mld->mld_mac));
+//	mld->type = type;
+	memset(mld->affiliated_ap_bitmap, 0, sizeof(mld->affiliated_ap_bitmap));
+
+	return RETURN_OK;
+
+}
+
+INT wifi_eht_destroy_ap_mld(unsigned char mld_index)
+{
+	int res;
+	struct multi_link_device *mld;
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	if (!mld_test(mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	res = v_secure_system("mwctl ra0 set apmld=destroy:group=%u", mld_index);
+
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "fail to destroy mld with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld_set(mld_index, 0);
+	mld = &(mld_config.mld[mld_index]);
+	memset(mld, 0, sizeof(*mld));
+
+	return RETURN_OK;
+}
+
+INT wifi_eht_list_ap_mld(unsigned char mld_index[], unsigned char *mld_num)
+{
+	unsigned char i, j = 0;
+
+//	for (i = 1; i <= (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT); i++) {
+	for (i = 1; i <= MAX_ML_MLD_CNT; i++) {
+		if (mld_test(i))
+			mld_index[j++] = i;
+	}
+
+	*mld_num = j;
+
+	return RETURN_OK;
+}
+
+INT wifi_eht_add_to_ap_mld(unsigned char mld_index, INT ap_index)
+{
+	int res;
+//	enum mld_type type;
+	struct multi_link_device *mld;
+	char interface_name[IF_NAME_SIZE] = {0};
+	unsigned char i;
+	int max_radio_num;
+
+	if (ap_index < 0 || ap_index >= MAX_APS) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+	if (wifi_GetInterfaceName(ap_index, interface_name) != RETURN_OK) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	if (!mld_test(mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld = &(mld_config.mld[mld_index]);
+#if 0
+	if (mld->type == AP_MLD_SINGLE_LINK) {
+		/*check single link mld is not occupied by other ap*/
+		for (i = 0; i < MAX_APS; i++) {
+			if(mld_ap_test(mld, i))
+				break;
+		}
+
+		if (i < MAX_APS) {
+			if (i == ap_index) {
+				wifi_debug(DEBUG_ERROR, "current ap(%d) has already joined single link mld(%d)\n", i, mld_index);
+				return RETURN_OK;
+			}
+			wifi_debug(DEBUG_ERROR,
+				"single link mld(%d) already has an affiliated AP(ap_index %d)\n", mld_index, i);
+			return RETURN_ERR;
+		}
+	} else if (mld->type == AP_MLD_MULTI_LINK) {
+#endif
+		/*check if a same band ap already has been joined before*/
+	wifi_getMaxRadioNumber(&max_radio_num);
+	if(max_radio_num == 0){
+		return RETURN_ERR;
+	}
+	for (i = 0; i < MAX_APS; i++) {
+		if(mld_ap_test(mld, i)) {
+			if (i == ap_index) {
+				wifi_debug(DEBUG_ERROR, "current ap(index=%d) has already joined current mld\n", i);
+				return RETURN_OK;
+			}
+			if ((i % max_radio_num) == (ap_index % max_radio_num)) {
+				wifi_debug(DEBUG_ERROR, "same band ap(index=%d) has already joined current mld\n", i);
+				return RETURN_ERR;
+			}
+		}
+	}
+
+	res = v_secure_system("mwctl %s set apmld=addlink:group=%u", interface_name, mld_index);
+
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "fail to add ap to ap mld with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld_ap_set(mld, ap_index, 1);
 
+	return RETURN_OK;
+}
+
+INT wifi_eht_remove_from_ap_mld(unsigned char mld_index, INT ap_index)
+{
+	int res;
+//	enum mld_type type;
+	struct multi_link_device *mld;
+	char interface_name[IF_NAME_SIZE] = {0};
+
+	if (ap_index < 0 || ap_index >= MAX_APS) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+	if (wifi_GetInterfaceName(ap_index, interface_name) != RETURN_OK) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	if (!mld_test(mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld = &(mld_config.mld[mld_index]);
+
+	res = v_secure_system("mwctl %s apmld=dellink", interface_name);
+
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "fail to del ap from ap mld with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld_ap_set(mld, ap_index, 0);
+
+	return RETURN_OK;
+}
+
+INT wifi_eht_get_ap_from_mld(unsigned char mld_index, unsigned char ap_index[], unsigned char *ap_num)
+{
+	unsigned char i, j = 0;
+	struct multi_link_device *mld;
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (mld_index == 0 || mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	if (!mld_test(mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with mld_index %d\n", mld_index);
+		return RETURN_ERR;
+	}
+
+	mld = &(mld_config.mld[mld_index]);
+
+	for (i = 0; i < MAX_APS; i++) {
+		if (mld_ap_test(mld, i))
+			ap_index[j++] = i;
+	}
+
+	*ap_num = j;
+
+	return RETURN_OK;
+}
+
+INT wifi_eht_mld_ap_transfer(unsigned char old_mld_index,
+	unsigned char new_mld_index, INT ap_index)
+{
+	int res;
+//	enum mld_type type;
+	struct multi_link_device *mld, *old_mld;
+	char interface_name[IF_NAME_SIZE] = {0};
+	unsigned char i;
+	int max_radio_num;
+
+	if (old_mld_index == new_mld_index) {
+		wifi_debug(DEBUG_ERROR, "same mld index %d\n", new_mld_index);
+		return RETURN_OK;
+	}
+
+	if (ap_index < 0 || ap_index >= MAX_APS) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+	if (wifi_GetInterfaceName(ap_index, interface_name) != RETURN_OK) {
+		wifi_debug(DEBUG_ERROR, "invalid ap_index %d\n", ap_index);
+		return RETURN_ERR;
+	}
+
+	if (old_mld_index == 0 || old_mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid old_mld_index %d\n", old_mld_index);
+		return RETURN_ERR;
+	}
+	old_mld = mld = &(mld_config.mld[new_mld_index]);
+
+	if (!mld_test(old_mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with old_mld_index %d\n", old_mld_index);
+		return RETURN_ERR;
+	}
+
+
+//	if (mld_index == 0 || mld_index > (MAX_ML_MLD_CNT + MAX_SL_MLD_CNT)) {
+	if (new_mld_index == 0 || new_mld_index > MAX_ML_MLD_CNT) {
+		wifi_debug(DEBUG_ERROR, "invalid mld_index %d\n", new_mld_index);
+		return RETURN_ERR;
+	}
+
+	if (!mld_test(new_mld_index)) {
+		wifi_debug(DEBUG_ERROR, "mld does not exist with mld_index %d\n", new_mld_index);
+		return RETURN_ERR;
+	}
+
+	mld = &(mld_config.mld[new_mld_index]);
+
+	wifi_getMaxRadioNumber(&max_radio_num);
+	if(max_radio_num == 0){
+		return RETURN_ERR;
+	}
+	for (i = 0; i < MAX_APS; i++) {
+		if(mld_ap_test(mld, i)) {
+			if (i == ap_index) {
+				wifi_debug(DEBUG_ERROR, "current ap has already joined current mld\n");
+				return RETURN_OK;
+			}
+			if ((i % max_radio_num) == (ap_index % max_radio_num)) {
+				wifi_debug(DEBUG_ERROR, "same band ap(index=%d) has already joined current mld\n", i);
+				return RETURN_ERR;
+			}
+		}
+	}
+
+	res = v_secure_system("mwctl %s set apmld=tsfrlink:group=%u", interface_name, new_mld_index);
+
+	if (res) {
+		wifi_debug(DEBUG_ERROR, "fail to transfer ap to ap mld with mld_index %d\n", new_mld_index);
+		return RETURN_ERR;
+	}
+
+	mld_ap_set(old_mld, ap_index, 0);
+	mld_ap_set(mld, ap_index, 1);
+
+	return RETURN_OK;
+}
+
+INT wifi_eht_config_sync2_dat_by_radio(unsigned char band)
+{
+	unsigned char bss_idx, mld_index;
+	char config_file_dat[128] = {0}, MldGroup_V_Str[128] = {0}, buf[64] = {0};
+	int res, vap_index, len = 0, bssidnum;
+	struct params MldGroup;
+
+	if (band >= MAX_NUM_RADIOS) {
+		wifi_debug(DEBUG_ERROR, "invalid band %u\n", band);
+		return RETURN_ERR;
+	}
+
+	res = wifi_BandProfileRead(0, band, "BssidNum", buf, sizeof(buf), "0");
+	if (res != 0) {
+		wifi_debug(DEBUG_ERROR, "wifi_BandProfileRead BssidNum failed\n");
+		return RETURN_ERR;
+	}
+
+	bssidnum = atoi(buf);
+	if (bssidnum <= 0)  {
+		wifi_debug(DEBUG_ERROR, "invalid BssidNum %s\n", buf);
+		return RETURN_ERR;
+	}
+	if (bssidnum > LOGAN_MAX_NUM_VAP_PER_RADIO) {
+		wifi_debug(DEBUG_ERROR, "bss_num is larger than %d\n", LOGAN_MAX_NUM_VAP_PER_RADIO);
+		return RETURN_ERR;
+	}
+
+	res = snprintf(config_file_dat, sizeof(config_file_dat), "%s%d.dat", LOGAN_DAT_FILE, band);
+	if (os_snprintf_error(sizeof(config_file_dat), res)) {
+		wifi_debug(DEBUG_ERROR, "Unexpected snprintf fail\n");
+		return RETURN_ERR;
+	}
+
+	for (bss_idx = 0; bss_idx < bssidnum; bss_idx++) {
+		vap_index = array_index_to_vap_index(band, bss_idx);
+		if (vap_index == RETURN_ERR) {
+			wifi_debug(DEBUG_ERROR, "invalide vap index, band=%d, bss_idx=%d\n", (int)band, (int)bss_idx);
+			break;
+		}
+		mld_index = mld_ap_test_all_mlds(vap_index);
+		res = snprintf(&(MldGroup_V_Str[len]), sizeof(MldGroup_V_Str) - len, "%u;", mld_index);
+		if (os_snprintf_error(sizeof(MldGroup_V_Str) - len, res)) {
+			wifi_debug(DEBUG_ERROR, "Unexpected snprintf fail\n");
+			break;
+		}
+		len += res;
+	}
+
+	MldGroup.name = "MldGroup";
+	MldGroup.value = MldGroup_V_Str;
+	wifi_datfileWrite(config_file_dat, &MldGroup, 1);
+	wifi_debug(DEBUG_ERROR, "band[%u] MldGroup=%s\n", band, MldGroup_V_Str);
+
+	return RETURN_OK;
+}
+
+void wifi_eht_config_sync2_dat(void)
+{
+	unsigned char band;
+
+	for (band = 0; band < MAX_NUM_RADIOS; band++) {
+		wifi_eht_config_sync2_dat_by_radio(band);
+	}
+}
+
+#endif
 
 static int
 get_value(const char *conf_file, const char *param, char *value, int len)
@@ -788,44 +1444,6 @@
 
 BOOL multiple_set = FALSE;
 
-struct params
-{
-	char * name;
-	char * value;
-};
-
-#define _syscmd_secure(retBuf, retBufSize, fmt, args...) \
-	({ \
-		FILE *f;	\
-		char *ptr = retBuf; \
-		int bufSize = retBufSize, bufbytes = 0, readbytes = 0, cmd_ret = -1;	\
-		WIFI_ENTRY_EXIT_DEBUG("Inside %s:%d\n",__func__, __LINE__);	\
-		f = v_secure_popen("r", fmt, ##args);	\
-		if(f) {	\
-			while(!feof(f))	\
-			{	\
-				*ptr = 0;	\
-				if(bufSize>=128) {	\
-					bufbytes=128;	\
-				} else {	\
-					bufbytes=bufSize-1;	\
-				}	\
-				if (fgets(ptr,bufbytes,f) == NULL)	\
-					break;	\
-				readbytes=strlen(ptr);	\
-				if(!readbytes)	\
-					break;	\
-				bufSize-=readbytes;	\
-				ptr += readbytes;	\
-			}	\
-			cmd_ret = v_secure_pclose(f);	\
-			retBuf[retBufSize-1]=0;	\
-		}	\
-		WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);	\
-		cmd_ret;	\
-	})
-
-
 /*static int _syscmd(char *cmd, char *retBuf, int retBufSize)
 {
 	FILE *f;
@@ -2100,7 +2718,9 @@
 		wifi_vap_status_reset();
 		wifi_radio_reset_count_reset();
 		wifi_BringUpInterfaces();
+		eht_mld_config_init();
 		CallOnce = 0;
+		mld_info_display();
 	}
 
 	WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);
@@ -8678,7 +9298,7 @@
 
 	ap_idx = array_index_to_vap_index(radio_idx, bss_idx);
 
-	if (ap_idx >= 0 && ap_idx < MAX_VAP) {
+	if (ap_idx >= 0 && ap_idx < MAX_APS) {
 		printf("%s: hostapd conf not find, inf(%s), use inf idx(%d).\n",
 			__func__, inputSsidString, ap_idx);
 		*output_int = ap_idx;
@@ -18721,6 +19341,9 @@
 	char buf[32] = {0};
 	wifi_vap_security_t security = {0};
 	int res = RETURN_OK;
+	wifi_vap_info_t *vap;
+	wifi_mld_info_t *mld_info;
+	unsigned char mld_index;
 
 	WIFI_ENTRY_EXIT_DEBUG("Inside %s:%d\n",__func__, __LINE__);
 	printf("Entering %s index = %d\n", __func__, (int)index);
@@ -18795,14 +19418,6 @@
 		}
 		map->vap_array[i].u.bss_info.showSsid = enabled;
 
-		ret = wifi_getApIsolationEnable(vap_index, &enabled);
-		if (ret != RETURN_OK) {
-			printf("%s: wifi_getApIsolationEnable return error\n", __func__);
-			res = RETURN_ERR;
-			continue;
-		}
-		map->vap_array[i].u.bss_info.isolation = enabled;
-
 		ret = wifi_getApMaxAssociatedDevices(vap_index, &output);
 		if (ret != RETURN_OK) {
 			printf("%s: wifi_getApMaxAssociatedDevices return error\n", __func__);
@@ -18896,7 +19511,33 @@
 		}
 		map->vap_array[i].u.bss_info.mcast2ucast = enabled;
 
-		// TODO: wps, noack
+		ret = wifi_getApIsolationEnable(vap_index, &enabled);
+		if (ret != RETURN_OK) {
+			printf("%s: wifi_getApIsolationEnable return error\n", __func__);
+//			res = RETURN_ERR;
+			continue;
+		}
+		map->vap_array[i].u.bss_info.isolation = enabled;
+	}
+
+	for (i = 0; i < map->num_vaps; i++)
+	{
+		map->vap_array[i].radio_index = index;
+		vap = &(map->vap_array[i]);
+
+		mld_info = &(vap->u.bss_info.mld_info);
+		memset(mld_info, 0, sizeof(*mld_info));
+		memcpy(mld_info->local_addr, map->vap_array[i].u.bss_info.bssid, 6);
+
+		mld_index = mld_ap_test_all_mlds(vap->vap_index);
+		if (mld_index) {
+			memcpy(mld_info->common_info.mld_addr, mld_config.mld[mld_index].mld_mac, 6);
+			mld_info->common_info.mld_enable = TRUE;
+			mld_info->common_info.mld_index = mld_index;
+		}
+		wifi_debug(DEBUG_ERROR,
+				"vap_index[%d], mld_enable=%d, mld_index[%d]\n",
+				vap->vap_index, mld_info->common_info.mld_enable, mld_info->common_info.mld_index);
 	}
 	WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);
 	return res;
@@ -19018,7 +19659,11 @@
 	BOOL enable = FALSE;
 	int band_idx;
 	int res;
-
+	wifi_mld_common_info_t *mld_info;
+	unsigned char mld_index;
+	unsigned char ap_index_array[MAX_APS] = {0};
+	unsigned char ap_array_num;
+	char interface_name[IF_NAME_SIZE] = {0};
 
 	WIFI_ENTRY_EXIT_DEBUG("Inside %s:%d\n",__func__, __LINE__);
 	printf("Entering %s index = %d\n", __func__, (int)index);
@@ -19175,11 +19820,101 @@
 		ret = wifi_setApIsolationEnable(vap_info->vap_index, vap_info->u.bss_info.isolation);
 		if (ret != RETURN_OK) {
 			wifi_debug(DEBUG_ERROR, "wifi_setApIsolationEnable return error\n");
-			return RETURN_ERR;
+//			return RETURN_ERR;
 		}
 
 		// TODO mgmtPowerControl, interworking, wps
 	}
+
+	/*process mlo operation*/
+	for (i = 0; i < map->num_vaps; i++)
+	{
+		vap_info = &map->vap_array[i];
+		mld_info = &vap_info->u.bss_info.mld_info.common_info;
+
+		wifi_debug(DEBUG_ERROR, "process mlo operation\n");
+		if (!mld_info->mld_enable) {
+			wifi_debug(DEBUG_ERROR, "disable mlo on vap[%d]\n",
+				(int)vap_info->vap_index);
+			mld_index = mld_ap_test_all_mlds((int)vap_info->vap_index);
+			if (mld_index) {
+				wifi_debug(DEBUG_ERROR, "mlo disabled, remove ap(%d) from mld group(%d)\n",
+					(int)vap_info->vap_index, (int)mld_index);
+				if (wifi_eht_remove_from_ap_mld(mld_index, vap_info->vap_index)) {
+					wifi_debug(DEBUG_ERROR, "fail to remove ap(%d) from mld(%d)\n",
+						(int)vap_info->vap_index, (int)mld_index);
+					continue;
+				}
+
+				if (wifi_GetInterfaceName(vap_info->vap_index, interface_name) == RETURN_OK) {
+						return RETURN_ERR;
+					res = _syscmd_secure(buf, sizeof(buf), "ifconfig %s down", interface_name);
+					if (res) {
+						wifi_debug(DEBUG_ERROR, "_syscmd_secure fail\n");
+					}
+					res = _syscmd_secure(buf, sizeof(buf), "ifconfig %s up", interface_name);
+					if (res) {
+						wifi_debug(DEBUG_ERROR, "_syscmd_secure fail\n");
+					}
+				}
+
+				if (wifi_eht_get_ap_from_mld(mld_index, ap_index_array, &ap_array_num)) {
+					wifi_debug(DEBUG_ERROR,
+						"fail to get all aps from mld(%d), destroy it.\n", mld_index);
+					continue;
+				}
+
+				if (ap_array_num == 0) {
+					wifi_debug(DEBUG_ERROR,
+						"there's no affiliated ap in mld(%d), destroy it.\n", mld_index);
+					wifi_eht_destroy_ap_mld(mld_index);
+				}
+			}
+		} else {
+			if (mld_info->mld_index == 0 || mld_info->mld_index > MAX_ML_MLD_CNT) {
+				wifi_debug(DEBUG_ERROR, "invalid mld index %d, ignore it.\n",
+					(int)mld_info->mld_index);
+				continue;
+			}
+
+			if (!mld_test(mld_info->mld_index)) {
+				if (wifi_eht_create_ap_mld(mld_info->mld_index, mld_info->mld_addr)) {
+					wifi_debug(DEBUG_ERROR,
+						"fail to create ap mld(%d)\n", mld_info->mld_index);
+					continue;
+				}
+			} else {
+				if(mld_ap_test(&(mld_config.mld[mld_info->mld_index]), vap_info->vap_index)) {
+					wifi_debug(DEBUG_ERROR,
+						"current vap(%d) is already the affiliated ap of mld(%d)\n",
+						vap_info->vap_index, mld_info->mld_index);
+					continue;
+				}
+			}
+			mld_index = mld_ap_test_all_mlds(vap_info->vap_index);
+
+			if (mld_index != 0) {
+				/*transfer*/
+				wifi_eht_mld_ap_transfer(mld_index, mld_info->mld_index, vap_info->vap_index);
+
+				if (wifi_eht_get_ap_from_mld(mld_index, ap_index_array, &ap_array_num)) {
+					wifi_debug(DEBUG_ERROR,
+						"fail to get all aps from mld(%d), destroy it.\n", mld_index);
+					continue;
+				}
+				if (ap_array_num == 0) {
+					wifi_debug(DEBUG_ERROR,
+						"there's no affiliated ap in mld(%d), destroy it.\n", mld_index);
+					wifi_eht_destroy_ap_mld(mld_index);
+				}
+			} else {
+				/*join*/
+				wifi_eht_add_to_ap_mld(mld_info->mld_index, vap_info->vap_index);
+			}
+		}
+	}
+	mld_info_display();
+	wifi_eht_config_sync2_dat_by_radio(index);
 	WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);
 	return RETURN_OK;
 }
diff --git a/src/wifi/wifi_hal.c b/src/wifi/wifi_hal.c
index e58b87e..28f98b4 100644
--- a/src/wifi/wifi_hal.c
+++ b/src/wifi/wifi_hal.c
@@ -7843,6 +7843,50 @@
     return NULL;
 }
 
+static INT wifi_getAssocConnectMode(INT apIndex, CHAR *input_string, CHAR *OperatingStandard)
+{
+    wifi_band band;
+	int ieee80211_mode = 0;
+
+	if (!input_string || !OperatingStandard)
+		return RETURN_ERR;
+
+	band = wifi_index_to_band(apIndex);
+
+	if (strstr(input_string, "HE"))
+		ieee80211_mode = WIFI_MODE_AX;
+	else if (strstr(input_string, "VHT"))
+		ieee80211_mode = WIFI_MODE_AC;
+	else if (strstr(input_string, "EHT"))
+		ieee80211_mode = WIFI_MODE_BE;
+
+	switch (ieee80211_mode) {
+		case WIFI_MODE_AC:
+			strncpy(OperatingStandard, "ac", 2);
+			break;
+		case WIFI_MODE_AX:
+			strncpy(OperatingStandard, "ax", 2);
+			break;
+		case WIFI_MODE_BE:
+			strncpy(OperatingStandard, "be", 2);
+			break;
+		default:
+			if(band == band_2_4)
+				strncpy(OperatingStandard,"b,g,n", 5);
+			else if(band == band_5)
+				strncpy(OperatingStandard,"a,n", 3);
+			else if(band == band_6)
+				strncpy(OperatingStandard,"ax", 2);
+			else {
+				wifi_dbg_printf("%s: failed to parse band %d\n", __FUNCTION__, band);
+				return RETURN_ERR;
+			}
+	}
+
+
+	return RETURN_OK;
+}
+
 INT wifi_getApAssociatedDeviceDiagnosticResult3(INT apIndex, wifi_associated_dev3_t **associated_dev_array, UINT *output_array_size)
 {
     unsigned int assoc_cnt = 0;
@@ -7870,8 +7914,9 @@
     //    signal avg:-67 [-71, -71] dBm
     //    Station 28:c2:1f:25:5f:99 (on wifi0)
     //    signal avg:-67 [-71, -70] dBm
-    if (sprintf(cmd,"iw dev %s station dump | tr -d '\\t' | grep 'Station\\|signal avg'", interface_name) < 0) {
-        wifi_dbg_printf("%s: failed to build iw dev command for %s\n", __FUNCTION__, interface_name);
+    if (sprintf(cmd,"iw dev %s station dump | tr -d '\t' | grep -E 'Station|signal avg"
+		"|tx bitrate|rx bitrate'", interface_name) < 0) {
+		wifi_dbg_printf("%s: failed to build iw dev command for %s\n", __FUNCTION__, interface_name);
         return RETURN_ERR;
     }
 
@@ -7912,6 +7957,7 @@
             }
         }
         else if (i < 0) {
+			wifi_dbg_printf("%s: not find station\n", __FUNCTION__);
             ptr = get_line_from_str_buf(ptr, line);
             continue; // We didn't detect 'station' entry yet
         }
@@ -7926,13 +7972,30 @@
             temp[i].cli_RSSI = rssi;
             temp[i].cli_SNR = 95 + rssi; // We use constant -95 noise floor
         }
+        else if (strstr(line, "tx bitrate")) {
+			ret = wifi_getAssocConnectMode(apIndex, line, temp[i].cli_OperatingStandard);
+			if (ret == RETURN_ERR) {
+				wifi_dbg_printf("%s: failed to execute tx get assoc connect mode command.\n", __FUNCTION__);
+				return ret;
+			}
+        }
+        else if (strstr(line, "rx bitrate")) {
+			/* if tx get ac, ax, be mode, need not get mode from rx */
+			if (strncmp(temp[i].cli_OperatingStandard,"ac", 2) &&
+				strncmp(temp[i].cli_OperatingStandard,"ax", 2) &&
+				strncmp(temp[i].cli_OperatingStandard,"be", 2)) {
+				ret = wifi_getAssocConnectMode(apIndex, line, temp[i].cli_OperatingStandard);
+				if (ret == RETURN_ERR) {
+					wifi_dbg_printf("%s: failed to execute rx get assoc connect mode command.\n", __FUNCTION__);
+					return ret;
+				}
+			}
+		}
         // Here other fields can be parsed if added to filter of 'iw dev' command
-
         ptr = get_line_from_str_buf(ptr, line);
-    };
+	}
 
     WIFI_ENTRY_EXIT_DEBUG("Exiting %s:%d\n",__func__, __LINE__);
-
     return RETURN_OK;
 }