/******************************************************************************
 *
 * Copyright(c) 2019 - 2020 Realtek Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 *****************************************************************************/
#include "phl_headers.h"

void __reset_snd_grp(struct phl_snd_grp *grp)
{
	u8 i = 0;

	grp->snd_type = PHL_SND_TYPE_INVALID;
	grp->band = 0;
	grp->num_sta = 0;
	grp->wrole_idx = 0;
	grp->grp_tier = PHL_SND_GRP_TIER_1;
	grp->snd_sts = PHL_SND_STS_PENDING;
	for (i = 0; i < MAX_NUM_STA_SND_GRP; i++) {
		grp->sta[i].valid = false;
		grp->sta[i].macid = 0;
		grp->sta[i].bw = CHANNEL_WIDTH_20;
		grp->sta[i].snd_fb_t = PHL_SND_FB_TYPE_SU;
		grp->sta[i].npda_sta_info = 0;
		grp->sta[i].bf_entry = NULL;
		grp->sta[i].snd_sts = PHL_SND_STS_PENDING;
	}
}

enum rtw_phl_status _phl_snd_init_snd_grp(
	struct phl_info_t *phl_info)
{
	enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *param = &snd->snd_param;
	u8 i = 0;
	do {
		if (param->snd_grp == NULL) {
			status = RTW_PHL_STATUS_FAILURE;
			break;
		}
		for (i = 0; i < MAX_SND_GRP_NUM; i++) {
			__reset_snd_grp(&param->snd_grp[i]);
			param->snd_grp[i].gidx = i;
		}
	} while (0);

	return status;
}
#ifdef CONFIG_FSM
/* For EXTERNAL application to create Sound object */
/* @fsm: FSM main structure which created by phl_snd_new_fsm()
 * @phl_info: private data structure to invoke hal/phl function
 *
 * return
 */
enum rtw_phl_status phl_snd_new_obj(
	struct fsm_main *fsm,
	struct phl_info_t *phl_info)
{
	enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
	struct phl_sound_obj *snd_obj = NULL;
	struct fsm_obj *obj = NULL;
	void *drv_priv = phl_to_drvpriv(phl_info);
	FUNCIN();

	do {
		snd_obj = phl_fsm_new_obj(
				fsm, (void **)&obj, sizeof(*snd_obj));

		if (snd_obj == NULL) {
			status = RTW_PHL_STATUS_RESOURCE;
			break;
		}
		phl_info->snd_obj = snd_obj;

		snd_obj->fsm = fsm;
		snd_obj->fsm_obj = obj;
		snd_obj->phl_info = phl_info;

		/*Init the snd group static resources here*/
		status = _phl_snd_init_snd_grp(phl_info);

		/* init obj local use variable */
		PHL_INFO("snd_fsm_func_init_st_hdl : PHL SND FSM Module Start Work\n");
		_os_spinlock_init(drv_priv, &snd_obj->snd_lock);
		_os_spinlock_init(drv_priv, &snd_obj->cmd_lock);
		phl_snd_func_snd_init(snd_obj->phl_info);

	} while (0);

	if (RTW_PHL_STATUS_SUCCESS != status) {
		PHL_ERR("phl_snd_init_obj FAIL\n");
		/* phl fsm module will handle to free the phl fsm related object*/
		/* phl_snd_deinit_obj(phl_info); */
	}

	FUNCOUT();
	return status;
}
#endif

/* PHL SOUND EXTERNAL APIs */
/* get sounding in progress */
u8 rtw_phl_snd_chk_in_progress(void *phl)
{
	u8 ret = 0;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	void *d = phl_to_drvpriv(phl_info);

	_os_spinlock(d, &snd->snd_lock, _bh, NULL);
	ret = snd->snd_in_progress;
	_os_spinunlock(d, &snd->snd_lock, _bh, NULL);

	return ret;
}

/**
 * rtw_phl_sound_start
 * @phl:(struct phl_info_t *)
 * @st_dlg_tkn: start dialog token value, if 0, it will use previous sounding dialog token;
 * @period: sounding process period (group--> next group)
 * @test_flag: test mode flags
 **/
enum rtw_phl_status
rtw_phl_sound_start(void *phl, u8 wrole_idx, u8 st_dlg_tkn, u8 period, u8 test_flag)
{
#ifdef CONFIG_FSM
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_snd_start_req snd_req;

	snd_req.wrole = (void *)rtw_phl_get_wrole_by_ridx(phl_info->phl_com, wrole_idx);

	snd_req.dialog_token = (st_dlg_tkn == 0) ?
					snd->snd_param.snd_dialog_token : st_dlg_tkn;
	snd_req.proc_timeout_ms = SND_PROC_DEFAULT_TIMEOUT; /* Default Value */
	snd_req.proc_period = (period > SND_PROC_DEFAULT_PERIOD) ?
					SND_PROC_DEFAULT_PERIOD : period; /*MAX = Default Value */
	snd_req.test_flag = test_flag;
	if (test_flag&PHL_SND_TEST_F_PASS_STS_CHK)
		snd_req.bypass_sts_chk = true;
	else
		snd_req.bypass_sts_chk = false; /* Default False */

	return phl_snd_fsm_ev_start_func(phl, &snd_req);
#else
	return RTW_PHL_STATUS_FAILURE;
#endif
}

enum rtw_phl_status
rtw_phl_sound_down_ev(void *phl)
{
	enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;

#ifdef CONFIG_FSM
	status = phl_snd_fsm_ev_c2h_snd_down(phl);
#else
	status = RTW_PHL_STATUS_FAILURE;
#endif
	return status;
}


enum rtw_phl_status
rtw_phl_sound_abort(void *phl)
{
#ifdef CONFIG_FSM
	return phl_snd_fsm_ev_abort(phl);
#else
	return RTW_PHL_STATUS_FAILURE;
#endif
}

/* set fixed mode parameters APIs*/
void rtw_phl_snd_dump_fix_para(struct phl_info_t *phl_info)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_snd_fix_param *para = NULL;
	u8 i = 0;

	para = &snd->snd_param.fix_param;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "===> rtw_phl_snd_fix_dump_para \n");

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "test_flag = 0x%x \n", snd->snd_param.test_flag);

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "en_fix_gidx = %d \n", para->en_fix_gidx ? 1 : 0);
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "en_fix_fb_type = %d \n", para->en_fix_fb_type ? 1 : 0);
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "en_fix_sta = %d \n", para->en_fix_sta ? 1 : 0);
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "en_fix_snd_bw = %d \n", para->en_fix_snd_bw ? 1 : 0);

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "grp_idx = %d \n", para->grp_idx);
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "snd_fb_type = %d \n", para->snd_fb_type);

	for (i = 0; i < MAX_NUM_STA_SND_GRP; i++) {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "sta_macid[i] = 0x%x \n", para->sta_macid[i]);
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "bw[i] = %d \n",para->bw[i]);
	}

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "<=== rtw_phl_snd_fix_dump_para \n");
}
/* fixed group idx */
void rtw_phl_snd_fix_gidx(struct phl_info_t *phl_info, bool en, u8 gidx)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "rtw_phl_snd_fix_gidx() set sounding gidx = 0x%x\n", gidx);
	if (en) {
		snd->snd_param.fix_param.en_fix_gidx = 1;
		snd->snd_param.fix_param.grp_idx = gidx;
	} else {
		snd->snd_param.fix_param.en_fix_gidx = 0;
	}
}
/* fixed snd feedback type */
void rtw_phl_snd_fix_snd_fb_type(struct phl_info_t *phl_info,
				 bool en, enum snd_fb_type fb_type)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "rtw_phl_snd_fix_gidx() set sounding fb_type = 0x%x\n",
		 fb_type);
	if (en) {
		snd->snd_param.fix_param.en_fix_fb_type = 1;
		snd->snd_param.fix_param.snd_fb_type = fb_type;
	} else {
		snd->snd_param.fix_param.en_fix_fb_type = 0;
	}
}

/* fixed sounding sta macids */
void rtw_phl_snd_fix_set_sta(struct phl_info_t *phl_info,
					bool en, u8 sidx, u16 macid)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "rtw_phl_snd_fix_set_sta() set sta[%d] macid = 0x%x\n",
		 sidx, macid);
	if (en) {
		snd->snd_param.fix_param.en_fix_sta = 1;
		if (sidx < MAX_NUM_STA_SND_GRP)
			snd->snd_param.fix_param.sta_macid[sidx] = macid;
		else
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "ERROR, sidx >= 4\n");
	} else {
		snd->snd_param.fix_param.en_fix_sta = 0;
	}
}

/* fixed sounding sta bw */
void rtw_phl_snd_fix_set_bw(struct phl_info_t *phl_info,
					bool en, u8 sidx, enum channel_width bw)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "rtw_phl_snd_fix_set_bw() set sta[%d] bw = 0x%x\n", sidx, bw);
	if (en) {
		snd->snd_param.fix_param.en_fix_snd_bw = 1;
		if (sidx < MAX_NUM_STA_SND_GRP)
			snd->snd_param.fix_param.bw[sidx] = bw;
		else
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "ERROR, sidx >= 4\n");
	} else {
		snd->snd_param.fix_param.en_fix_snd_bw = 0;
	}
}

/* set forced fw tx mu-mimo (forced fw tx decision) */
void rtw_phl_snd_fix_tx_he_mu(struct phl_info_t *phl_info, u8 gid, bool en)
{
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "rtw_phl_snd_fix_tx_mu_para()\n");

	rtw_hal_bf_set_txmu_para(phl_info->hal, gid, en,
				 HAL_PROT_NO_PROETCT, HAL_ACK_N_USER_BA);

	rtw_hal_bf_set_fix_mode(phl_info->hal, gid, en);
}


/* PHL SOUND INTERNAL APIs */
/* SND FUNC */
enum rtw_phl_status
phl_snd_func_snd_init(struct phl_info_t *phl_info)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	void *d = phl_to_drvpriv(phl_info);
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	u8 f_ru_tbl_80m[MAX_SND_HE_BFRP_USER_NUM][MAX_SND_HE_BFRP_USER_NUM] = {
		{RTW_HE_RU996_1, RTW_HE_RU996_1, RTW_HE_RU996_1 ,RTW_HE_RU996_1},
		{RTW_HE_RU484_1, RTW_HE_RU484_2, RTW_HE_RU996_1 ,RTW_HE_RU996_1},
		{RTW_HE_RU484_1, RTW_HE_RU242_3, RTW_HE_RU242_4 ,RTW_HE_RU996_1},
		{RTW_HE_RU242_1, RTW_HE_RU242_2, RTW_HE_RU242_3 ,RTW_HE_RU242_4}
	};

	u8 f_ru_tbl_20m[MAX_SND_HE_BFRP_USER_NUM][MAX_SND_HE_BFRP_USER_NUM] = {
		{RTW_HE_RU242_1, RTW_HE_RU242_1, RTW_HE_RU242_1, RTW_HE_RU242_1},
		{RTW_HE_RU106_1, RTW_HE_RU106_1, RTW_HE_RU242_1, RTW_HE_RU242_1},
		{RTW_HE_RU106_1, RTW_HE_RU52_3, RTW_HE_RU52_4, RTW_HE_RU242_1},
		{RTW_HE_RU52_1, RTW_HE_RU52_2, RTW_HE_RU52_3, RTW_HE_RU52_4}
	};

	/* Add Other Sounding FUNC/PRCO Initialization Here */
	snd->snd_param.snd_proc_timeout_ms = SND_PROC_DEFAULT_TIMEOUT;/* ms */
	snd->snd_param.cur_proc_grp_idx = 0;
	snd->snd_param.pre_proc_grp_idx = 0;
	snd->snd_param.snd_dialog_token = 1;
	snd->snd_param.snd_func_grp_num = 0;
	snd->snd_param.grp_used_map = 0;
	snd->snd_param.snd_proc_period = SND_PROC_DEFAULT_PERIOD;
	snd->snd_param.snd_fail_counter = 0;

	/*fixed_ru_tbl*/
	_os_mem_cpy(d, snd->snd_param.fix_param.f_ru_tbl_20, f_ru_tbl_20m,
		    MAX_SND_HE_BFRP_USER_NUM * MAX_SND_HE_BFRP_USER_NUM);
	_os_mem_cpy(d, snd->snd_param.fix_param.f_ru_tbl_80, f_ru_tbl_80m,
		    MAX_SND_HE_BFRP_USER_NUM * MAX_SND_HE_BFRP_USER_NUM);

	return pstatus;
}

enum rtw_phl_status
phl_snd_func_pre_config(struct phl_info_t *phl_info)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	void *d = phl_to_drvpriv(phl_info);

	snd_param->proc_start_time = _os_get_cur_time_ms();
	snd_param->cur_proc_grp_idx = 0; /* default start from group idx 0 */
	snd_param->pre_proc_grp_idx = 0;
	_os_spinlock(d, &snd->snd_lock, _bh, NULL);
	snd->is_terminated = 0;
	snd->snd_in_progress = 1;
	_os_spinunlock(d, &snd->snd_lock, _bh, NULL);

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "PHL SND FUNC Start with SND Dialog Token = 0x%x\n",
		 snd_param->snd_dialog_token);


	return pstatus;
}


/* SND_FUNC : GROUP related */
/**
 * phl_snd_proc_get_grp()
 * 	get the grp(struct phl_sound_grp *) with group index.
 * input:
 * @gidx: group idx.
 * return:
 * @grp: (struct phl_snd_grp *grp), NULL = FAIL;
 */
struct phl_snd_grp *
phl_snd_get_grp_byidx(struct phl_info_t *phl_info, u8 gidx)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	struct phl_snd_grp *grp = NULL;

	do {
		if (gidx >= MAX_SND_GRP_NUM)
			break;

		if (!(snd_param->grp_used_map & BIT(gidx)))
			break;

		if (0 == snd_param->snd_grp[gidx].num_sta)
			break;

		if (PHL_SND_TYPE_INVALID == snd_param->snd_grp[gidx].snd_type)
			break;

		grp = &snd_param->snd_grp[gidx];

	} while (0);

	return grp;
}

/**
 * phl_snd_func_remove_grp()
 * 	remove the target sounding grp from sound process;
 * input:
 * @grp: (struct phl_snd_grp *) target sounding grp,
 */
enum rtw_phl_status
phl_snd_func_remove_grp(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;

	if (grp == NULL) {
		return pstatus;
	}
	if (snd_param->grp_used_map & BIT(grp->gidx)) {

		/* Check and Release all the BF resource */
		pstatus = phl_snd_proc_release_res(phl_info, grp);
		if (pstatus != RTW_PHL_STATUS_SUCCESS) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "PHL SND Remove Grp : release BF resouce fail\n");
		}

		/* Reset group content to default value */
		__reset_snd_grp(grp);

		/* Clear Group BIT */
		snd_param->grp_used_map &= ~BIT(grp->gidx);
		snd_param->snd_func_grp_num--;

	} else {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "PHL SND Remove Grp : Group is not in used!!!\n");
	}

	return pstatus;
}

/**
 * phl_snd_func_remove_grp_all()
 * 	remove the all of the sounding grp from sound process;
 */
void
phl_snd_func_remove_grp_all(struct phl_info_t *phl_info)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct phl_snd_grp *grp = NULL;
	u8 idx = 0;

	for(idx = 0; idx < MAX_SND_GRP_NUM; idx++) {
		grp = phl_snd_get_grp_byidx(phl_info, idx);
		if (grp != NULL) {
			pstatus = phl_snd_func_remove_grp(phl_info, grp);
			if (pstatus != RTW_PHL_STATUS_SUCCESS) {
				PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "Remove SND GRP[%d] Fail\n", idx);
			}
		}
	}
}

/**
 * _phl_snd_get_available_gidx()
 *	Get available group resource.
 * return:
 * @gidx: u8, the group idx in snd_param->grp[n]
 */
u8 _phl_snd_get_available_gidx(struct phl_sound_obj *snd)
{
	struct phl_sound_param *param = &snd->snd_param;
	u8 gidx = MAX_SND_GRP_NUM;

	for (gidx = 0; gidx < MAX_SND_GRP_NUM; gidx++) {
		if (!(param->grp_used_map & BIT(gidx))) {
			param->grp_used_map |= BIT(gidx);
			break;
		}
	}

	return gidx;
}

/**
 * _phl_snd_func_grp_add_sta()
 *	Add the STA into sounding group.
 * input:
 * @sta: (struct rtw_phl_stainfo_t *) the target sta to be added.
 * 	 the function will use  the macid / bw information in sta_info;
 * @gidx: the group idx to add
 */
enum rtw_phl_status
_phl_snd_func_grp_add_sta(
	struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta, u8 gidx)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	struct phl_snd_grp *grp = NULL;
	u8 i = 0;
	bool chk = false;

	do {

		if (NULL == sta) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "STA == NULL !!!!\n");
			break;
		}

		if (gidx >= MAX_SND_GRP_NUM) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "Get SND Grp Resource Fail : gidx >= MAX_SND_GRP_NUM\n");
			break;
		}

		grp = &snd_param->snd_grp[gidx];

		/* check grp->sta[i].macid with sta->macid, skip it if same.*/
		for (i = 0; i < grp->num_sta; i++) {
			if(grp->sta[i].macid == sta->macid) {
				chk = true;
				break;
			}
		}
		if (true == chk)
			break;

		if (grp->num_sta >= MAX_NUM_STA_SND_GRP) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "The SND Grp is already has 4 STAs\n");
			break;
		}

		grp->sta[grp->num_sta].macid = sta->macid;
		grp->sta[grp->num_sta].snd_sts = PHL_SND_STS_PENDING;
		grp->sta[grp->num_sta].bw = sta->chandef.bw;
		grp->sta[grp->num_sta].valid = true;
		grp->num_sta++;
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "sta bw = %d\n", sta->chandef.bw);
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "grp->num_sta = %d\n", grp->num_sta);

		pstatus = RTW_PHL_STATUS_SUCCESS;
	} while (0);

	return pstatus;
}

/**
 * phl_snd_func_add_snd_grp :
 * 	Add a Sounding Group with Primary STA for FW Sounding
 * @phl_info: struct phl_info_t *
 * @he_snd: 1 = HE , 0 =VHT
 * @gidx: return value, snd group idxx in group list
 * @psta: (struct rtw_phl_stainfo_t *)Primary Sounding STA,
 *         if pSTA is unavailable , SND PROC for this group will be terminated.
 **/
enum rtw_phl_status
phl_snd_func_add_snd_grp(
	struct phl_info_t *phl_info, bool he_snd,
	u8 wrole_idx, struct rtw_phl_stainfo_t *psta, u8 *gidx)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	struct phl_snd_grp *grp = NULL;
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;

	do {
		/* Check Primary STA Available*/
		if (psta == NULL) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "psta == NULL\n");
			break;
		}

		/* Get available sounding group resource */
		*gidx = _phl_snd_get_available_gidx(snd);
		if (*gidx >= MAX_SND_GRP_NUM) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "Get SND Grp Resource Fail : gidx >= MAX_SND_GRP_NUM\n");
			break;
		}

		grp = &(snd_param->snd_grp[*gidx]);
		grp->band = psta->wrole->hw_band;
		grp->snd_type = he_snd ? PHL_SND_TYPE_HE_SW :
					 PHL_SND_TYPE_VHT_SW;
		grp->wrole_idx = wrole_idx;
		grp->snd_sts = PHL_SND_STS_PENDING;
		grp->num_sta = 0;

		/* Primary STA use idx-0 */
		_phl_snd_func_grp_add_sta(phl_info, psta, *gidx);

		snd_param->snd_func_grp_num++;

		pstatus = RTW_PHL_STATUS_SUCCESS;

		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "phl_snd_func_add_snd_grp : Add group[%d] Success\n",
			  *gidx);
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "phl_snd_func_add_snd_grp : grp->snd_type 0x%x\n",
			  grp->snd_type);

	} while (0);

	return pstatus;
}

/**
 * _phl_snd_func_set_grp_fb_mu()
 * 	Set the whole sounding grp's feedback type = MU
 * input:
 * @grp: (struct phl_snd_grp *) the target group.
 */
void _phl_snd_func_set_grp_fb_mu(struct phl_snd_grp *grp)
{
	u8 i = 0;
	if (grp == NULL)
		return;
	for (i = 0; i < grp->num_sta; i++) {
		grp->sta[i].snd_fb_t = PHL_SND_FB_TYPE_MU;
	}
}

/**
 * phl_snd_func_grouping()
 * 	function for soundind fsm state : SND_FUNC_READY
 * input:
 * @wroleidx: the index of wrole which the sounding proc run under with.
 */
enum rtw_phl_status
phl_snd_func_grouping(struct phl_info_t *phl_info, u8 wroleidx)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	struct phl_snd_fix_param *fix_para = &snd->snd_param.fix_param;
	struct rtw_wifi_role_t *wrole = NULL;
	struct rtw_phl_stainfo_t *self = NULL, *sta;
	struct phl_snd_grp *grp = NULL;
	void *drv = phl_to_drvpriv(phl_info);
	struct phl_queue *sta_queue;
	u8 gidx = 0;
	u8 cnt = 0;

	wrole = rtw_phl_get_wrole_by_ridx(phl_info->phl_com, wroleidx);

	/* if wrole(STA) is linked, seft = AP */
	/* if wrole is AP, self = ???? */
	self = rtw_phl_get_stainfo_self(phl_info, wrole);
	if (self == NULL) {
		PHL_ERR("Cannot get self's phl_sta\n");
		return pstatus;
	}
	sta_queue = &wrole->assoc_sta_queue;
	if (PHL_RTYPE_STATION == wrole->type) {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, " PHL_RTYPE_STATION == wrole->type \n");
		/* STA Mode : Only SU TxBF with AP */

		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "self->macid = 0x%x \n", self->macid);
		debug_dump_mac_address(self->mac_addr);

		pstatus = phl_snd_func_add_snd_grp(
				phl_info,
				(self->wmode & WLAN_MD_11AX) ? true :
							       false,
				wrole->id, self, &gidx);
		grp = &snd_param->snd_grp[gidx];
		grp->grp_tier = PHL_SND_GRP_TIER_0;
		grp->sta[0].snd_fb_t = PHL_SND_FB_TYPE_SU;
		grp->snd_type = (self->wmode & WLAN_MD_11AX) ?
				PHL_SND_TYPE_HE_HW : PHL_SND_TYPE_VHT_HW;
	} else {
#if 1
		/* Test Code: Group-1 :Forced MU Sounding with first 1~4 STAs */
		/* the mu sounding list shall get from mu grouping module */
		cnt = 0;
		_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
		phl_list_for_loop(sta, struct rtw_phl_stainfo_t,
				  &wrole->assoc_sta_queue.queue, list) {
			if (is_broadcast_mac_addr(sta->mac_addr))
				continue;
			if (sta == self)
				continue;
			/* First STA */
			if (cnt == 0) {
				pstatus = phl_snd_func_add_snd_grp(
						phl_info,
						(sta->wmode & WLAN_MD_11AX) ?
							 true : false,
						wrole->id, sta, &gidx);
				if (pstatus != RTW_PHL_STATUS_SUCCESS)
					break;
			} else {
				/* get next associated sta and add to group */
				_phl_snd_func_grp_add_sta(phl_info, sta, gidx);
			}
			cnt++;
			if (cnt >= 4)
				break;
		}
		_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
		if(pstatus != RTW_PHL_STATUS_SUCCESS)
			return RTW_PHL_STATUS_FAILURE;
		grp = &snd_param->snd_grp[gidx];
		grp->grp_tier = PHL_SND_GRP_TIER_0;
		/* Test : forced MU */
		_phl_snd_func_set_grp_fb_mu(&snd_param->snd_grp[gidx]);
#endif
	}

	/*TODO: fixed paramters gidx when multi-group */
	if (snd_param->test_flag&PHL_SND_TEST_F_GRP_SND_PARA) {
		/*Test Mode force set the group fb type = MU */
		if (fix_para->en_fix_fb_type) {
			if (PHL_SND_FB_TYPE_MU == fix_para->snd_fb_type) {
				_phl_snd_func_set_grp_fb_mu(
						&snd_param->snd_grp[gidx]);
			}
			/**
			 * Note : 8852A only support two CSI Buffer for SU,
			 * take care that num of STA in SU sounding of a group shall < 2.
			 **/
		}

		if(fix_para->en_fix_snd_bw) {
			grp = &snd_param->snd_grp[gidx];
			for (cnt = 0; cnt < MAX_NUM_STA_SND_GRP; cnt++) {
				if (grp->sta[cnt].valid)
					grp->sta[cnt].bw = fix_para->bw[cnt];
			}
		}
	} else {
		grp = &snd_param->snd_grp[gidx];
		if (grp->num_sta > 2) {
			/* forced using MU feedback because of SU CSI buffer number */
			_phl_snd_func_set_grp_fb_mu(&snd_param->snd_grp[gidx]);
		}
	}

	if (snd_param->test_flag & PHL_SND_TEST_F_GRP_EN_BF_FIX) {
		snd_param->snd_grp[gidx].en_fix_mode = 1; /* post confg forced mode setting */
	}
	return pstatus;

}

/* SND PROC */

/* Free BF/CQI resource */
enum rtw_phl_status
_phl_snd_proc_release_res_cqi(
	struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;

	/*CQI Fb doesn't query any resource*/

	return pstatus;
}

enum rtw_phl_status
_phl_snd_proc_release_res_bf(
	struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	struct phl_snd_sta *snd_sta;
	struct rtw_phl_stainfo_t *sta = NULL;
	u8 idx = 0;

	for (idx = 0; idx < grp->num_sta; idx++) {
		snd_sta = &grp->sta[idx];
		if(0 == snd_sta->valid)
			continue;

		sta = rtw_phl_get_stainfo_by_macid(
				phl_info, snd_sta->macid);
		if (NULL == sta) {
			PHL_ERR("_phl_snd_proc_release_res_bf: Cannot find STA macid 0x%x in PHL STA Info List \n",
				 snd_sta->macid);
			continue;
		}
		if (NULL == sta->hal_sta->bf_entry)
			continue;

		hstatus = rtw_hal_snd_release_proc_sta_res(phl_info->hal, sta);
		if(hstatus != RTW_HAL_STATUS_SUCCESS) {
			PHL_ERR("_phl_snd_proc_release_res_bf: macid 0x%x Free Sounding Resource FAIL \n",
				 snd_sta->macid);
			continue;
		}
		/* un link the bf entry to STA info */
		sta->hal_sta->bf_entry = NULL;
	}
	return pstatus;
}
/**
 * phl_snd_proc_release_res:
 * 	Release the sounding resource for the group
 * @phl_info: phl_info_t
 * @grp: (struct phl_snd_grp *) sounding gorup for release resource
 **/
enum rtw_phl_status
phl_snd_proc_release_res(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct phl_snd_sta *snd_sta;
	snd_sta = &grp->sta[0];
	if (snd_sta->snd_fb_t == PHL_SND_FB_TYPE_CQI)
		pstatus = _phl_snd_proc_release_res_cqi(phl_info, grp);
	else
		pstatus = _phl_snd_proc_release_res_bf(phl_info, grp);

	return pstatus;
}

/**
 * _phl_snd_proc_get_bf_res_cqi_fb:
 * 	CQI Sounding doesn't need BF Reresource
 * @phl_info: phl_info_t
 * @grp: (struct phl_sound_grp *) sounding gorup
 * @nsta: return value : how many sta query resource success
 **/
enum rtw_phl_status
_phl_snd_proc_get_res_cqi_fb(
	struct phl_info_t *phl_info, struct phl_snd_grp *grp, u8 *nsta)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct phl_snd_sta *snd_sta;
	u8 idx = 0;
	struct rtw_phl_stainfo_t *sta = NULL;

	*nsta = 0;

	for (idx = 0; idx < grp->num_sta; idx++) {
		snd_sta = &grp->sta[idx];
		sta = rtw_phl_get_stainfo_by_macid(phl_info, snd_sta->macid);
		if (NULL == sta) {
			PHL_ERR("phl_snd_proc_get_bf_res: Cannot find STA macid 0x%x in PHL STA Info List \n",
				 snd_sta->macid);
			continue;
		}

		rtw_hal_snd_ndpa_sta_info_he(
			sta,
			&snd_sta->npda_sta_info,
			snd_sta->bw,
			PHL_SND_FB_TYPE_CQI);

		(*nsta)++;
	}
	if (*nsta == 0) {
		grp->snd_sts = PHL_SND_STS_FAILURE;
		pstatus = RTW_PHL_STATUS_FAILURE;
	}
	if (*nsta != grp->num_sta) {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, " Sounding STAs is fewer than group sta because of resource!");
	}

	return pstatus;
}

/**
 * _phl_snd_proc_get_res_bf:
 * 	Get BF Resource for SU/MU Sounding
 * @phl_info: phl_info_t
 * @grp: (struct phl_sound_grp *) sounding gorup
 * @nsta: return value : how many sta query bf resource success
 **/
enum rtw_phl_status
_phl_snd_proc_get_res_bf(
	struct phl_info_t *phl_info, struct phl_snd_grp *grp, u8 *nsta)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	struct phl_snd_sta *snd_sta;
	u8 idx = 0;
	struct rtw_phl_stainfo_t *sta = NULL;
	bool mu, qry_bf;

	*nsta = 0;

	for (idx = 0; idx < grp->num_sta; idx++) {
		snd_sta = &grp->sta[idx];
		sta = rtw_phl_get_stainfo_by_macid(phl_info, snd_sta->macid);
		if (NULL == sta) {
			PHL_ERR("phl_snd_proc_get_bf_res: Cannot find STA macid 0x%x in PHL STA Info List \n",
				 snd_sta->macid);
			continue;
		}

		mu = (snd_sta->snd_fb_t == PHL_SND_FB_TYPE_MU) ? true:false;
		qry_bf = false;
		if (sta->hal_sta->bf_entry != NULL) {
			/* The sta already have BF reource */
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, " sta->bf_entry != NULL\n");
			/* Check the BF resource */
			hstatus = rtw_hal_snd_chk_bf_res(phl_info->hal,
							 sta, mu, sta->chandef.bw);
			if (RTW_HAL_STATUS_FAILURE == hstatus) {
				rtw_hal_snd_release_proc_sta_res(phl_info->hal,
								 sta);
				qry_bf = true;

			} else {
				PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "Use Original BF Resource \n");
				qry_bf = false;
			}
		} else {
			qry_bf = true;
		}

		if (true == qry_bf) {
			hstatus = rtw_hal_snd_query_proc_sta_res(
					phl_info->hal, sta, mu, sta->chandef.bw,
					grp->en_swap_mode);
			if (hstatus != RTW_HAL_STATUS_SUCCESS) {
				PHL_ERR("phl_snd_proc_get_bf_res: macid 0x%x query sounding resource FAIL \n",
				snd_sta->macid);
				if (grp->en_swap_mode) {
					break;/* break in swap mode if one of sta query bf res fail */
				}
				continue;
			}
		}
		if (grp->snd_type >= PHL_SND_TYPE_HE_HW) {
			rtw_hal_snd_ndpa_sta_info_he(
				sta,
				&snd_sta->npda_sta_info,
				snd_sta->bw,
				snd_sta->snd_fb_t);
		} else {
			rtw_hal_snd_ndpa_sta_info_vht(sta,
				&snd_sta->npda_sta_info, mu);
		}

		/* Link STA information to Group Information */
		snd_sta->bf_entry = sta->hal_sta->bf_entry;

		(*nsta)++;
	}

	if (*nsta == 0) {
		grp->snd_sts = PHL_SND_STS_FAILURE;
		pstatus = RTW_PHL_STATUS_FAILURE;
	}
	if (*nsta != grp->num_sta) {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "FAIL : Sounding STAs is fewer than group sta because of resource!\n");
		pstatus = RTW_PHL_STATUS_FAILURE;
	}

	return pstatus;
}
/* snd proc get BF/CSI resource */
enum rtw_phl_status
phl_snd_proc_get_res(
	struct phl_info_t *phl_info, struct phl_snd_grp *grp, u8 *nsta)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct phl_snd_sta *snd_sta;
	FUNCIN_WSTS(pstatus);

	snd_sta = &grp->sta[0];
	/* CQI Fb cannot mixed with SU/MU feedback type*/
	if(snd_sta->snd_fb_t == PHL_SND_FB_TYPE_CQI)
		pstatus = _phl_snd_proc_get_res_cqi_fb(phl_info, grp, nsta);
	else
		pstatus = _phl_snd_proc_get_res_bf(phl_info, grp, nsta);

	if(pstatus != RTW_PHL_STATUS_SUCCESS)
		grp->snd_sts = PHL_SND_STS_FAILURE;

	FUNCOUT_WSTS(pstatus);
	return pstatus;
}

/* 2. SND Preconfiguration */

/**
 * _get_mu_mimo_gid_2sta()
 * 	hard code for 8852A, gid relattion-ship
 **/
static u8 _get_mu_mimo_gid_2sta(u8 primary, u8 secondary)
{
	u8 gid_tbl[6][6] = { {0xFF, 1, 2, 3, 4, 5},
					{16, 0xFF, 6, 7, 8, 9},
					{17, 21, 0xFF, 10, 11, 12},
					{18, 22, 25, 0xFF, 13, 14},
					{19, 23, 26, 28, 0xFF, 15},
					{20, 24, 27, 29, 30, 0xFF} };
	u8 ret = 0xFF;

	if ((primary < 6) && (secondary < 6))
		ret = gid_tbl[primary][secondary];

	return ret;
}
/* pre calculate mu-gid */
enum rtw_phl_status
phl_snd_cal_mu_grp_bitmap(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct rtw_phl_stainfo_t *psta_info = NULL;
	struct rtw_phl_stainfo_t *tmp_psta_info = NULL;
	struct phl_snd_sta *sta = NULL;
	struct phl_snd_sta *tmp_sta = NULL;
	u8 bfmu_idx , bfmu_idx_tmp;
	u8 i = 0, j = 0;

	for (i = 0; i < MAX_NUM_STA_SND_GRP; i++) {
		sta = &grp->sta[i];
		if (false == sta->valid)
			continue;
		/* primary STA */
		psta_info = rtw_phl_get_stainfo_by_macid(
				phl_info, sta->macid);

		if (NULL == sta->bf_entry)
			continue;
		if (false == rtw_hal_bf_chk_bf_type(phl_info->hal,
				psta_info, true)) {
			continue; /*BF SU Entry*/
		}
		/* primary STA MU Entry Idx */
		bfmu_idx = rtw_hal_bf_get_sumu_idx(phl_info->hal,
							sta->bf_entry);

		psta_info->hal_sta->mugrp_bmp = 0; /* clear first */

		for (j = 0; j < MAX_NUM_STA_SND_GRP; j++) {
			if (j == i) /* self */
				continue;
			/* secondary sta */
			tmp_sta = &grp->sta[j];

			if (NULL == tmp_sta->bf_entry)
				continue;

			tmp_psta_info = rtw_phl_get_stainfo_by_macid(
					phl_info, tmp_sta->macid);

			if (false == rtw_hal_bf_chk_bf_type(phl_info->hal,
				tmp_psta_info, true)) {
				continue; /* BF SU Entry */
			}

			/* secondary sta MU Entry Idx */
			bfmu_idx_tmp = rtw_hal_bf_get_sumu_idx(phl_info->hal,
						tmp_sta->bf_entry);

			/* Default set group bit enable = 1 */
			/* grp bitmap doesn't include self */
			/**   BIT    0     1     2     3     4
			 *  MU_0 : MU_1  MU_2  MU_3  MU_4  MU_5
			 *  MU_1 : MU_0  MU_2  MU_3  MU_4  MU_5
			 *  MU_2 : MU_0  MU_1  MU_3  MU_4  MU_5
			 *  ...
			 *  MU_5 : MU_0  MU_1  MU_2  MU_3  MU_4
			 **/

			if (bfmu_idx_tmp > bfmu_idx) {
				psta_info->hal_sta->mugrp_bmp |=
							BIT(bfmu_idx_tmp - 1);
			} else {
				psta_info->hal_sta->mugrp_bmp |=
							BIT(bfmu_idx_tmp);
			}
		}
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "sta(macid = 0x%x)  mugrp_bmp = 0x%x \n",
			 psta_info->macid, psta_info->hal_sta->mugrp_bmp);
	}

	return pstatus;
}

/* Preconfiguration before souding */
enum rtw_phl_status
phl_snd_proc_precfg(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	struct phl_snd_sta *sta = NULL;
	u8 idx = 0;
	struct rtw_phl_stainfo_t *psta_info = NULL;
	FUNCIN_WSTS(pstatus);
	do {
		if (grp == NULL) {
			pstatus = RTW_PHL_STATUS_FAILURE;
			break;
		}
		if (PHL_SND_TYPE_INVALID == grp->snd_type) {
			/* both SW/HW mode need to set call halmac api to set bf entry */
			break;
		}
		for (idx = 0; idx < MAX_NUM_STA_SND_GRP; idx++) {
			sta = &grp->sta[idx];
			if (false == sta->valid)
				continue;
			psta_info = rtw_phl_get_stainfo_by_macid(
						phl_info, sta->macid);

			/*check bf entry available and snd_fb_type = SU/MU */
			if ((NULL != psta_info->hal_sta->bf_entry) &&
				(PHL_SND_FB_TYPE_CQI != sta->snd_fb_t)) {

				hstatus = rtw_hal_snd_proc_pre_cfg_sta(
						phl_info->hal, psta_info);

				if (hstatus != RTW_HAL_STATUS_SUCCESS) {
					pstatus = RTW_PHL_STATUS_FAILURE;
				}
			}
		}
		/* Prepare Group bitmap for Tx MU-MIMO */
		if (PHL_SND_FB_TYPE_MU == grp->sta[0].snd_fb_t)
			pstatus = phl_snd_cal_mu_grp_bitmap(phl_info, grp);

	} while (0);


	if(pstatus != RTW_PHL_STATUS_SUCCESS)
		grp->snd_sts = PHL_SND_STS_FAILURE;
	FUNCOUT_WSTS(pstatus);
	return pstatus;
}
/* 3. Send Sounding Command to HAL/FW */
/*TODO: RU Allocation is now hard code value */
/* HE TB Sounding : 2 sta in a grp */
void
_phl_snd_proc_fw_cmd_he_tb_2sta(struct phl_info_t *phl_info,
				struct phl_snd_grp *grp,
				u8 *cmd, u8 bfrp_num)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct rtw_phl_stainfo_t *sta_info = NULL;
	u8 *f_ru_tbl = NULL;

	if (grp->num_sta != 2)
		return;
	/* get first sta */
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[0].macid);

	if (bfrp_num == 1) {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[1][0];/* Fixed 20MHz RU Table of 2 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[1][0];/* Fixed 80MHz RU Table of 2 STA */
	} else {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[0][0];/* Fixed 20MHz RU Table of 1 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[0][0];/* Fixed 80MHz RU Table of 1 STA */
	}

	/* fill commmand */
	rtw_hal_snd_ax_fwcmd_tb_pri(phl_info->hal, cmd, grp->sta[0].bw,
		sta_info, grp->num_sta, 0);
	/* Always use BFRP#0 for primary user */
	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[0].npda_sta_info,
			sta_info,
			f_ru_tbl[0],
			0,
			0,
			0);

	/*get second sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[1].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[1].npda_sta_info,
			sta_info,
			f_ru_tbl[1],
			1,
			(bfrp_num == 1) ? 0 : 1,
			(bfrp_num == 1) ? 1 : 0);

}

/* HE TB Sounding : 3 sta in a grp */
void
_phl_snd_proc_fw_cmd_he_tb_3sta(struct phl_info_t *phl_info,
				struct phl_snd_grp *grp,
				u8 *cmd, u8 bfrp_num)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct rtw_phl_stainfo_t *sta_info = NULL;
	u8 *f_ru_tbl = NULL;

	if(grp->num_sta != 3)
		return;
	/* get first sta */
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[0].macid);

	if (bfrp_num == 1) {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[2][0];/* Fixed 20MHz RU Table of 3 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[2][0];/* Fixed 80MHz RU Table of 3 STA */
	} else {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[1][0];/* Fixed 20MHz RU Table of 2 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[1][0];/* Fixed 80MHz RU Table of 2 STA */
	}

	/* fill commmand */
	rtw_hal_snd_ax_fwcmd_tb_pri(phl_info->hal, cmd, grp->sta[0].bw,
		sta_info, grp->num_sta, 0);
	/* Always use BFRP#0 for primary user */
	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[0].npda_sta_info,
			sta_info,
			f_ru_tbl[0],
			0,
			0,
			0);

	/*get second sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[1].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[1].npda_sta_info,
			sta_info,
			f_ru_tbl[1],
			1,
			0,
			1);
	/*get third sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[2].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[2].npda_sta_info,
			sta_info,
			f_ru_tbl[2],
			2,
			(bfrp_num == 1) ? 0 : 1,
			(bfrp_num == 1) ? 2 : 0);

}

/* HE TB Sounding : 4 sta in a grp */
void
_phl_snd_proc_fw_cmd_he_tb_4sta(struct phl_info_t *phl_info,
				struct phl_snd_grp *grp,
				u8 *cmd, u8 bfrp_num)
{
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct rtw_phl_stainfo_t *sta_info = NULL;
	u8 *f_ru_tbl = NULL;

	if(grp->num_sta != 4)
		return;
	/* get first sta */
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[0].macid);

	if (bfrp_num == 1) {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[3][0];/* Fixed 20MHz RU Table of 4 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[3][0];/* Fixed 80MHz RU Table of 4 STA */
	} else {
		if (CHANNEL_WIDTH_20 == grp->sta[0].bw)
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_20[1][0];/* Fixed 20MHz RU Table of 2 STA */
		else
			f_ru_tbl = &snd->snd_param.fix_param.f_ru_tbl_80[1][0];/* Fixed 80MHz RU Table of 2 STA */
	}

	/* fill commmand */
	rtw_hal_snd_ax_fwcmd_tb_pri(phl_info->hal, cmd, grp->sta[0].bw,
		sta_info, grp->num_sta, 0);
	/* Always use BFRP#0 for primary user */
	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[0].npda_sta_info,
			sta_info,
			f_ru_tbl[0],
			0,
			0,
			0);

	/*get second sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[1].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[1].npda_sta_info,
			sta_info,
			f_ru_tbl[1],
			1,
			(bfrp_num == 1) ? 0 : 0,
			(bfrp_num == 1) ? 1 : 1);
	/*get third sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[2].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[2].npda_sta_info,
			sta_info,
			f_ru_tbl[2],
			2,
			(bfrp_num == 1) ? 0 : 1,
			(bfrp_num == 1) ? 2 : 0);

	/*get 4th sta*/
	sta_info = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[3].macid);

	rtw_hal_snd_ax_fwcmd_tb_add_sta(
			phl_info->hal, cmd,
			&grp->sta[3].npda_sta_info,
			sta_info,
			f_ru_tbl[3],
			3,
			(bfrp_num == 1) ? 0 : 1,
			(bfrp_num == 1) ? 3 : 1);

}


enum rtw_phl_status
phl_snd_proc_start_sounding_fw(struct phl_info_t *phl_info,
			       struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct phl_sound_param *snd_param = &snd->snd_param;
	struct rtw_phl_stainfo_t *sta_info = NULL;
	u8 *cmd = NULL;
	u8 i = 0;
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "==> phl_snd_proc_start_sounding_fw \n");
	do {
		if (NULL == grp)
			break;
		if(grp->sta[0].valid == 0)
			break;

		/*get first sta*/
		sta_info = rtw_phl_get_stainfo_by_macid(
					phl_info, grp->sta[0].macid);

		switch (grp->snd_type) {
		case PHL_SND_TYPE_VHT_SW:
		{
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "--> PHL_SND_TYPE_VHT_SW\n");
			cmd = rtw_hal_snd_prepare_snd_cmd(phl_info->hal);
			if (cmd == NULL)
				break;
			if (grp->num_sta == 1) {
				rtw_hal_snd_vht_fwcmd_su(
						phl_info->hal, cmd,
						grp->sta[0].bw,
						sta_info,
						&grp->sta[0].npda_sta_info);

			} else {
				rtw_hal_snd_vht_fwcmd_mu_pri(
						phl_info->hal, cmd,
						grp->sta[0].bw,
						sta_info,
						grp->num_sta,
						&grp->sta[0].npda_sta_info);

				for (i = 1; i < grp->num_sta; i++) {
					if(grp->sta[i].valid == 0)
						break;
					sta_info = rtw_phl_get_stainfo_by_macid(
						phl_info, grp->sta[i].macid);

					rtw_hal_snd_vht_fwcmd_mu_add_sta(
						phl_info->hal, cmd,
						&grp->sta[i].npda_sta_info,
						sta_info,
						i,
						(i==(grp->num_sta-1)) ? 1 : 0
						);
				}
			}
			rtw_hal_snd_set_fw_cmd_dialogtkn(
					phl_info->hal, cmd,
					0,
					snd_param->snd_dialog_token);
			hstatus = rtw_hal_snd_send_fw_cmd(phl_info->hal, cmd);
			if (hstatus != RTW_HAL_STATUS_SUCCESS) {
				PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_,
					  "ERROR: rtw_hal_snd_send_fw_cmd Fail!!!!\n");
			}
			/* free cmd buf at last !!! */
			hstatus = rtw_hal_snd_release_snd_cmd(phl_info->hal, cmd);
		}
		break;
		case PHL_SND_TYPE_HE_SW:
		{
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "--> PHL_SND_TYPE_HE_SW\n");
			cmd = rtw_hal_snd_prepare_snd_cmd(phl_info->hal);
			if (cmd == NULL)
				break;
			if (grp->num_sta == 1) {
				rtw_hal_snd_ax_fwcmd_nontb(
					phl_info->hal, cmd,
					grp->sta[0].bw,
					sta_info,
					&grp->sta[0].npda_sta_info);
			} else {
				/* Default use only 1 BFRP */
				/* TODO: Fixed mode or when to use 2 BFRP */
				if (grp->num_sta == 4)
					_phl_snd_proc_fw_cmd_he_tb_4sta(
							phl_info, grp, cmd, 1);
				else if (grp->num_sta == 3)
					_phl_snd_proc_fw_cmd_he_tb_3sta(
							phl_info, grp, cmd, 1);
				else if (grp->num_sta == 2)
					_phl_snd_proc_fw_cmd_he_tb_2sta(
							phl_info, grp, cmd, 1);
				else
					PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "phl sounding : 1 sta with HE-TB case is NOT Ready ; need add fake sta into NDPA\n");
			}
			rtw_hal_snd_set_fw_cmd_dialogtkn(
					phl_info->hal, cmd,
					1,
					snd_param->snd_dialog_token);

			hstatus = rtw_hal_snd_send_fw_cmd(phl_info->hal, cmd);
			if (hstatus != RTW_HAL_STATUS_SUCCESS) {
				PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_,
					  "ERROR: rtw_hal_snd_send_fw_cmd Fail!!!!\n");
			}
			/* free cmd buf at last !!! */
			hstatus = rtw_hal_snd_release_snd_cmd(phl_info->hal, cmd);
		}
		break;
		case PHL_SND_TYPE_VHT_HW:
		{
			u8 dialog_tkn = (snd->snd_param.snd_dialog_token << 2);
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_,
				  "PHL_SND_TYPE_VHT_HW:\n");
			if(NULL == snd->ops.snd_send_ndpa)
				break;
			rtw_hal_snd_mac_ctrl(phl_info->hal, sta_info->wrole->hw_band, 0);
			pstatus = snd->ops.snd_send_ndpa(
					phl_to_drvpriv(phl_info),
					sta_info->wrole,
					&dialog_tkn,
					&grp->sta[0].npda_sta_info,
					grp->sta[0].bw);
		}
		break;
		case PHL_SND_TYPE_HE_HW:
		{
			u8 dialog_tkn = (snd->snd_param.snd_dialog_token << 2) | BIT(1);
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_,
				  "PHL_SND_TYPE_HE_HW:\n");
			if(NULL == snd->ops.snd_send_ndpa)
				break;
			rtw_hal_snd_mac_ctrl(phl_info->hal, sta_info->wrole->hw_band, 0);
			pstatus = snd->ops.snd_send_ndpa(
					phl_to_drvpriv(phl_info),
					sta_info->wrole,
					&dialog_tkn,
					&grp->sta[0].npda_sta_info,
					grp->sta[0].bw);
		}
		break;
		case PHL_SND_TYPE_INVALID:
		default:
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "ERROR: grp->snd_type invalid\n");
			break;
		}
		pstatus = RTW_PHL_STATUS_SUCCESS;
	} while (0);
	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "<== phl_snd_proc_start_sounding_fw \n");
	return pstatus;
}

/* 4. Post Configruation */

/* BY MU_GID if MU Sounding */
enum rtw_phl_status
_phl_snd_proc_postcfg_mu_gid(struct phl_info_t *phl_info,
					struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	struct rtw_phl_stainfo_t *psta_info = NULL;
	struct phl_snd_sta *sta = NULL;
	struct rtw_hal_muba_info ba_info;
	u8 i = 0, j = 0;
	u8 bfmu_idx;
	u8 mugrp_bmp = 0;
	u8 gid = 0xFF;


	for (i = 0; i < MAX_NUM_STA_SND_GRP; i++) {
		sta = &grp->sta[i];
		if((false == sta->valid) || (NULL == sta->bf_entry))
			continue;

		bfmu_idx = rtw_hal_bf_get_sumu_idx(phl_info->hal,
					sta->bf_entry);

		psta_info = rtw_phl_get_stainfo_by_macid(
					phl_info, sta->macid);

		mugrp_bmp = psta_info->hal_sta->mugrp_bmp;

		/* GID(X + Y)'s setting is same as GID(Y + X)*/
		for (j = bfmu_idx; j < 5; j++) {
			if (mugrp_bmp & BIT(j)) {
				gid = _get_mu_mimo_gid_2sta(bfmu_idx, j + 1);
				/*Prepare MU BAR Info*/
				rtw_hal_bf_preset_mu_ba_info(phl_info->hal,
							psta_info, &ba_info);
				PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "snd_post_cfg : gid = 0x%x \n", gid);
				hstatus = rtw_hal_snd_proc_post_cfg_gid(
						phl_info->hal,
						gid,
						(void *)&ba_info);

				if (RTW_HAL_STATUS_SUCCESS != hstatus) {
					pstatus = RTW_PHL_STATUS_FAILURE;
				}
			}
		}
	}

	return pstatus;
}
/* Per STA setting */
enum rtw_phl_status
_phl_snd_proc_postcfg_sta(struct phl_info_t *phl_info,
				struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct rtw_phl_stainfo_t *psta_info = NULL;
	struct phl_snd_sta *sta = NULL;
	u8 idx = 0;
	bool mu = false;

	/*post config for a single sta*/
	for (idx = 0; idx < MAX_NUM_STA_SND_GRP; idx++) {
		sta = &grp->sta[idx];
		mu = (sta->snd_fb_t == PHL_SND_FB_TYPE_MU) ? true : false;
		if (false == sta->valid)
			continue;

		psta_info = rtw_phl_get_stainfo_by_macid(phl_info, sta->macid);
		if (NULL == psta_info)
			continue;

		rtw_hal_snd_polling_snd_sts(phl_info->hal, psta_info);
		if (RTW_HAL_STATUS_SUCCESS ==
			rtw_hal_bf_get_entry_snd_sts(psta_info->hal_sta->bf_entry)) {
			sta->snd_sts = PHL_SND_STS_SUCCESS;
		} else {
			sta->snd_sts = PHL_SND_STS_FAILURE;
		}

		if ((PHL_SND_STS_SUCCESS != sta->snd_sts) &&
			(false == snd->snd_param.bypass_snd_sts_chk)) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "SKIP STA Post Config because of sounding fail\n");
			continue; /*Sounding Fail, Next STA */
		}

		hstatus = rtw_hal_snd_proc_post_cfg_sta(phl_info->hal,
							psta_info, mu);

		if (hstatus != RTW_HAL_STATUS_SUCCESS) {
			pstatus = RTW_PHL_STATUS_FAILURE;
		}
	}

	return pstatus;
}
/* SND PROC Post Config API for FSM */
enum rtw_phl_status
phl_snd_proc_postcfg(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	bool mu = false, he = true;

	FUNCIN();

	do {
		if (grp == NULL) {
			pstatus = RTW_PHL_STATUS_FAILURE;
			break;
		}
		he = (grp->snd_type >= PHL_SND_TYPE_HE_HW) ? true : false;
		mu = (grp->sta[0].snd_fb_t == PHL_SND_FB_TYPE_MU) ? true :
								    false;

		/* 1. post config for whole sounding group */
		if (grp->skip_post_cfg & BIT(1)) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "SKIP SND PROC POST CFG - Group \n");
		} else {
			hstatus = rtw_hal_snd_proc_post_cfg(
						phl_info->hal,
						he,
						mu,
						grp->en_fix_mode);
			if (hstatus != RTW_HAL_STATUS_SUCCESS) {
				pstatus = RTW_PHL_STATUS_FAILURE;
			}
		}

		/* 2. post config for gid (STA + STA) */
		if (grp->skip_post_cfg & BIT(2)) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "SKIP SND PROC POST CFG - GID \n");
		} else {
			if (true == mu) {
				/* only mu sounding has gid related config */
				_phl_snd_proc_postcfg_mu_gid(phl_info, grp);
			}
		}

		/* 3. (Shall always at last) post config for each STA in group */
		if (grp->skip_post_cfg & BIT(3)) {
			PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "SKIP SND PROC POST CFG - STA \n");
		} else {
			_phl_snd_proc_postcfg_sta(phl_info, grp);
		}

	} while (0);

	FUNCOUT();
	return pstatus;
}

/* SND_PROC_DOWN --> Next Sounding : Check sounding module status */
enum rtw_phl_status
phl_snd_proc_chk_condition(struct phl_info_t *phl_info, struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_sound_obj *snd = (struct phl_sound_obj *)phl_info->snd_obj;
	struct rtw_wifi_role_t *role =
		(struct rtw_wifi_role_t *)snd->snd_param.m_wrole;
	struct phl_snd_sta *sta = NULL;
	struct rtw_phl_stainfo_t *psta = NULL;
	struct phl_sound_param *para = &snd->snd_param;
	u8 i = 0;
	u8 terminate = 0;
	/* TODO: Add any conditions to stop the sounding fsm here */
	do {
		if (true == snd->is_terminated)
			break;

		if (NULL != role) {
			if (PHL_RTYPE_STATION == role->type) {
				if (MLME_NO_LINK == role->mstate)
					break;
				psta = rtw_phl_get_stainfo_self(phl_info, role);
				if (rtw_hal_bf_get_entry_snd_sts(
						psta->hal_sta->bf_entry)) {
					para->snd_fail_counter++;
					if (para->snd_fail_counter > 10) {
						PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_ ,
							  "Sounding Fail Count > 10, break sounding !!!!\n");
						break;
					}
				} else {
					para->snd_fail_counter = 0;
				}
			} else if (PHL_RTYPE_AP == role->type) {
				if (false == role->active)
					break;
				if (grp->sta[0].bw > role->chandef.bw)
					break;
				if (0 == grp->num_sta)
					break;
				for (i = 0; i < grp->num_sta; i++) {
					sta = &grp->sta[i];
					psta = rtw_phl_get_stainfo_by_macid(phl_info, sta->macid);
					if (NULL == psta) {
						terminate = 1;
						break;
					}
					if (false == psta->active) {
						terminate = 1;
						break;
					}
					if (sta->bw != psta->chandef.bw) {
						terminate = 1;
						break;
					}
				}
				if(terminate)
					break;
			}
		}

		pstatus = RTW_PHL_STATUS_SUCCESS;
	} while (0);

	return pstatus;
}



/**
 * Check the previous sounding group sounding status and free the resource.
 * if grp is TIER0 grp, skip release BF/CQI resource.
 **/
void
phl_snd_proc_chk_prev_grp(struct phl_info_t *phl_info,
			  struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	bool free_res = false;

	if (PHL_SND_STS_FAILURE == grp->snd_sts) {
		/* Sounding Fail */
		free_res = true;
	} else if ((PHL_SND_GRP_TIER_1 == grp->grp_tier) && (PHL_SND_STS_PENDING != grp->snd_sts)) {
		/* Sounding Success and Group is TIER_1 */
		free_res = true;
	}

	if (free_res) {
		PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_, "Free Previous SND Group's Resource\n");
		pstatus = phl_snd_proc_release_res(phl_info, grp);
	}

	return;
}

enum rtw_phl_status
phl_snd_polling_pri_sta_sts(struct phl_info_t *phl_info,
			    struct phl_snd_grp *grp)
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
	struct rtw_phl_stainfo_t *sta = NULL;

	PHL_TRACE(COMP_PHL_SOUND, _PHL_INFO_,
		  "phl_snd_polling_stutus : polling primay sta sounding status\n");
	sta = rtw_phl_get_stainfo_by_macid(phl_info, grp->sta[0].macid);
	if (sta != NULL) {
		if (sta->active == true)
			rtw_hal_snd_polling_snd_sts(phl_info->hal, sta);
		else
			pstatus = RTW_PHL_STATUS_FAILURE;
	} else {
		pstatus = RTW_PHL_STATUS_FAILURE;
	}

	return pstatus;
}

void
phl_snd_mac_ctrl(struct phl_info_t *phl_info,
		 struct rtw_wifi_role_t *wrole, u8 ctrl)
{
	enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
	hstatus = rtw_hal_snd_mac_ctrl(phl_info->hal, wrole->hw_band, ctrl);
}

enum rtw_phl_status
rtw_phl_snd_init_ops_send_ndpa(void *phl,
                               enum rtw_phl_status (*snd_send_ndpa)(void *,
                                                                  struct rtw_wifi_role_t *,
                                                                  u8 *,
                                                                  u32 *,
                                                                  enum channel_width))
{
	enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
	struct phl_info_t *phl_info = (struct phl_info_t *)phl;
	struct phl_sound_obj *snd = NULL;
	if((phl_info != NULL) && (snd_send_ndpa != NULL)) {
		if (phl_info->snd_obj != NULL) {
			snd = (struct phl_sound_obj *)phl_info->snd_obj;
			snd->ops.snd_send_ndpa = snd_send_ndpa;
			pstatus = RTW_PHL_STATUS_SUCCESS;
		}
	}
	return pstatus;
}