/****************************************************************************** * * 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(¶m->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; }