974 lines
28 KiB
C
974 lines
28 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 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.
|
|
*
|
|
*****************************************************************************/
|
|
#define _PHL_ECSA_C_
|
|
#include "phl_headers.h"
|
|
|
|
#ifdef CONFIG_PHL_ECSA
|
|
void
|
|
_phl_ecsa_dump_param(
|
|
struct rtw_phl_ecsa_param *param
|
|
)
|
|
{
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Channel %d\n", __FUNCTION__,
|
|
param->ch);
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Op class %d\n", __FUNCTION__,
|
|
param->op_class);
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Count %d\n", __FUNCTION__,
|
|
param->count);
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Mode %d\n", __FUNCTION__,
|
|
param->mode);
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Delay time %d\n", __FUNCTION__,
|
|
param->delay_start_ms);
|
|
PHL_DUMP_CHAN_DEF(&(param->new_chan_def));
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
_phl_ecsa_tx_pause(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
|
|
/* Pause SW Tx */
|
|
rtw_phl_tx_stop(phl_info);
|
|
rtw_phl_tx_req_notify(phl_info);
|
|
|
|
/* Disable hw tx all */
|
|
if (rtw_hal_dfs_pause_tx(phl_info->hal, wifi_role->hw_band, true) ==
|
|
RTW_HAL_STATUS_SUCCESS) {
|
|
status = RTW_PHL_STATUS_SUCCESS;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "[ECSA] hw tx pause OK\n");
|
|
} else {
|
|
status = RTW_PHL_STATUS_FAILURE;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "[ECSA] hw tx pause fail\n");
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
_phl_ecsa_tx_resume(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
|
|
/* Enable hw tx all */
|
|
if (rtw_hal_dfs_pause_tx(phl_info->hal, wifi_role->hw_band, false) ==
|
|
RTW_HAL_STATUS_SUCCESS) {
|
|
status = RTW_PHL_STATUS_SUCCESS;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "[ECSA] hw tx unpause OK\n");
|
|
} else {
|
|
status = RTW_PHL_STATUS_FAILURE;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "[ECSA] hw tx unpause fail\n");
|
|
}
|
|
|
|
rtw_phl_tx_resume(phl_info);
|
|
|
|
return status;
|
|
}
|
|
|
|
u32
|
|
_phl_ecsa_calculate_next_timer_ap(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct rtw_bcn_info_cmn *bcn_cmn = NULL;
|
|
u32 tsf_h = 0, tsf_l = 0;
|
|
u64 tsf = 0;
|
|
u32 beacon_period_us = 0, timeslot_us = 0, next_timeslot_us = 0;
|
|
u32 current_time_ms = _os_get_cur_time_ms();
|
|
|
|
if (RTW_HAL_STATUS_SUCCESS != rtw_hal_get_tsf(phl_info->hal,
|
|
ecsa_ctrl->role->hw_port,
|
|
&tsf_h,
|
|
&tsf_l)) {
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "_phl_ecsa_timer_callback(): Get tsf fail\n");
|
|
return 0;
|
|
}
|
|
tsf = tsf_h;
|
|
tsf = tsf << 32;
|
|
tsf |= tsf_l;
|
|
|
|
bcn_cmn = &ecsa_ctrl->role->bcn_cmn;
|
|
beacon_period_us = bcn_cmn->bcn_interval * TU;
|
|
|
|
timeslot_us = (u32)_os_modular64(tsf, beacon_period_us);
|
|
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: CurTimeMs = %d State = %x timeslot = %d\n",
|
|
__FUNCTION__, current_time_ms, ecsa_ctrl->state, timeslot_us);
|
|
|
|
if(ecsa_ctrl->state == ECSA_STATE_START){
|
|
next_timeslot_us = beacon_period_us - timeslot_us + (2 * TU);
|
|
}
|
|
/* To make sure first ECSA IE show in Beacon */
|
|
else if(ecsa_ctrl->state == ECSA_STATE_UPDATE_FIRST_BCN_DONE){
|
|
next_timeslot_us = (beacon_period_us - timeslot_us -
|
|
ECSA_UPDATE_BCN_BEFORE_TBTT_US);
|
|
ecsa_ctrl->expected_tbtt_ms = current_time_ms +
|
|
(beacon_period_us - timeslot_us)/1000;
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_COUNT_DOWN){
|
|
if(ecsa_ctrl->ecsa_param.count == 1){
|
|
next_timeslot_us = (beacon_period_us - timeslot_us) +
|
|
ECSA_SWITCH_TIME_AFTER_LAST_COUNT_DOWN;
|
|
}
|
|
else{
|
|
next_timeslot_us = (beacon_period_us - timeslot_us) +
|
|
(beacon_period_us - ECSA_UPDATE_BCN_BEFORE_TBTT_US);
|
|
|
|
ecsa_ctrl->expected_tbtt_ms = current_time_ms +
|
|
(2 * beacon_period_us - timeslot_us)/1000;
|
|
}
|
|
}
|
|
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Expected tbtt %d!\n", __FUNCTION__, ecsa_ctrl->expected_tbtt_ms);
|
|
return next_timeslot_us/1000;
|
|
}
|
|
|
|
u32
|
|
_phl_ecsa_calculate_next_timer_sta(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct rtw_phl_stainfo_t *sta = NULL;
|
|
u32 beacon_period_us = 0, next_timeslot = 0;
|
|
u32 current_time_ms = 0;
|
|
|
|
current_time_ms = _os_get_cur_time_ms();
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: CurTimeMs = %d State = %x\n",
|
|
__FUNCTION__, current_time_ms, ecsa_ctrl->state);
|
|
|
|
sta = rtw_phl_get_stainfo_self(phl_info, wifi_role);
|
|
if(sta == NULL){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_ERR_, "%s: Get sta info fail!\n",
|
|
__FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
beacon_period_us = sta->asoc_cap.bcn_interval * TU;
|
|
|
|
if(ecsa_ctrl->state == ECSA_STATE_START){
|
|
next_timeslot = 0;
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_COUNT_DOWN){
|
|
u8 count = ecsa_ctrl->ecsa_param.count;
|
|
next_timeslot = (beacon_period_us * count) / 1000; /* ms */
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_SWITCH){
|
|
next_timeslot = 1000; /* 1s */
|
|
}
|
|
|
|
return next_timeslot;
|
|
}
|
|
|
|
void
|
|
_phl_ecsa_calculate_next_timer(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
void *d = phlcom_to_drvpriv(phl_com);
|
|
u32 next_timeslot = 0; /* ms */
|
|
|
|
if(IS_ECSA_TYPE_AP(ecsa_ctrl))
|
|
next_timeslot = _phl_ecsa_calculate_next_timer_ap(ecsa_ctrl);
|
|
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl))
|
|
next_timeslot = _phl_ecsa_calculate_next_timer_sta(ecsa_ctrl);;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: Next time slot %d!\n", __FUNCTION__, next_timeslot);
|
|
_os_set_timer(d, &ecsa_ctrl->timer, next_timeslot);
|
|
|
|
}
|
|
|
|
void _phl_ecsa_state_change_ap(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct phl_msg msg = {0};
|
|
struct phl_msg_attribute attr = {0};
|
|
void *d = phlcom_to_drvpriv(phl_com);
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_FG_MDL_ECSA);
|
|
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: CurTimeMs = %d State = %x\n",
|
|
__FUNCTION__, _os_get_cur_time_ms(), ecsa_ctrl->state);
|
|
|
|
/* Protect ECSA state change to prevent timer callback racing */
|
|
_os_spinlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
|
|
if(ecsa_ctrl->state == ECSA_STATE_WAIT_DELAY){
|
|
status = rtw_phl_ecsa_cmd_request(phl_info, ecsa_ctrl->role);
|
|
if(status != RTW_PHL_STATUS_SUCCESS){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: ECSA command fail!\n", __FUNCTION__);
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
}
|
|
else{
|
|
ecsa_ctrl->state = ECSA_STATE_START;
|
|
}
|
|
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_START){
|
|
ecsa_ctrl->state = ECSA_STATE_UPDATE_FIRST_BCN_DONE;
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_UPDATE_FIRST_BCN_DONE);
|
|
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
/* To make sure first ECSA IE show in Beacon */
|
|
else if(ecsa_ctrl->state == ECSA_STATE_UPDATE_FIRST_BCN_DONE){
|
|
ecsa_ctrl->state = ECSA_STATE_COUNT_DOWN;
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_COUNT_DOWN);
|
|
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_COUNT_DOWN){
|
|
if(ecsa_ctrl->ecsa_param.count == 1){
|
|
ecsa_ctrl->state = ECSA_STATE_SWITCH;
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_SWITCH_START);
|
|
msg.rsvd[0] = (u8*)ecsa_ctrl->role;
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
else{
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_COUNT_DOWN);
|
|
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
}
|
|
_os_spinunlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
}
|
|
|
|
void _phl_ecsa_state_change_sta(
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct rtw_phl_com_t *phl_com = ecsa_ctrl->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct phl_msg msg = {0};
|
|
struct phl_msg_attribute attr = {0};
|
|
void *d = phlcom_to_drvpriv(phl_com);
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_FG_MDL_ECSA);
|
|
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: CurTimeMs = %d State = %x\n",
|
|
__FUNCTION__, _os_get_cur_time_ms(), ecsa_ctrl->state);
|
|
|
|
/* Protect ECSA state change to prevent timer callback racing */
|
|
_os_spinlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
|
|
if(ecsa_ctrl->state == ECSA_STATE_WAIT_DELAY){
|
|
status = rtw_phl_ecsa_cmd_request(phl_info, ecsa_ctrl->role);
|
|
if(status != RTW_PHL_STATUS_SUCCESS){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: ECSA command fail!\n", __FUNCTION__);
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
}
|
|
else{
|
|
ecsa_ctrl->state = ECSA_STATE_START;
|
|
}
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_START){
|
|
ecsa_ctrl->state = ECSA_STATE_COUNT_DOWN;
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_COUNT_DOWN);
|
|
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_COUNT_DOWN){
|
|
ecsa_ctrl->state = ECSA_STATE_SWITCH;
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_SWITCH_START);
|
|
msg.rsvd[0] = (u8*)ecsa_ctrl->role;
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
else if(ecsa_ctrl->state == ECSA_STATE_SWITCH){
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_CHECK_TX_RESUME);
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
_os_spinunlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
}
|
|
|
|
void
|
|
_phl_ecsa_timer_callback(
|
|
void *context
|
|
)
|
|
{
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = (struct phl_ecsa_ctrl_t *)context;
|
|
|
|
if(IS_ECSA_TYPE_AP(ecsa_ctrl))
|
|
_phl_ecsa_state_change_ap(ecsa_ctrl);
|
|
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl))
|
|
_phl_ecsa_state_change_sta(ecsa_ctrl);
|
|
}
|
|
|
|
void
|
|
_phl_ecsa_cmd_abort_hdlr(
|
|
void* dispr,
|
|
void* priv,
|
|
bool abort
|
|
)
|
|
{
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = (struct phl_ecsa_ctrl_t *)priv;
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
struct rtw_phl_com_t *phl_com = wifi_role->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct rtw_phl_ecsa_ops *ops = &ecsa_ctrl->ops;
|
|
struct phl_msg msg = {0};
|
|
struct phl_msg_attribute attr = {0};
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
void *d = phlcom_to_drvpriv(phl_com);
|
|
|
|
_os_cancel_timer(d, &ecsa_ctrl->timer);
|
|
|
|
/* ECSA AP abort handle */
|
|
if(IS_ECSA_TYPE_AP(ecsa_ctrl) &&
|
|
ecsa_ctrl->ecsa_param.flag != 0){
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
CLEAR_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_BCN);
|
|
CLEAR_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_PROBERSP);
|
|
/* Update Bcn */
|
|
if(ops->update_beacon)
|
|
ops->update_beacon(ops->priv, wifi_role);
|
|
}
|
|
|
|
/* ECSA STA abort handle */
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl)){
|
|
if(ecsa_ctrl->ecsa_param.mode == true)
|
|
_phl_ecsa_tx_resume(ecsa_ctrl);
|
|
|
|
if(ops->ecsa_complete)
|
|
ops->ecsa_complete(ops->priv, wifi_role);
|
|
}
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_FG_MDL_ECSA);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_DONE);
|
|
|
|
if(abort)
|
|
attr.opt = MSG_OPT_SEND_IN_ABORT;
|
|
|
|
pstatus = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
|
|
if(pstatus != RTW_PHL_STATUS_SUCCESS) {
|
|
PHL_ERR("%s:[ECSA] dispr_send_msg failed (0x%X)\n",
|
|
__FUNCTION__, pstatus);
|
|
}
|
|
}
|
|
|
|
enum phl_mdl_ret_code
|
|
_phl_ecsa_cmd_acquired(
|
|
void* dispr,
|
|
void* priv)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
enum phl_mdl_ret_code ret = MDL_RET_FAIL;
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = (struct phl_ecsa_ctrl_t *)priv;
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
struct rtw_phl_com_t *phl_com = wifi_role->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
struct phl_msg msg = {0};
|
|
struct phl_msg_attribute attr = {0};
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_FG_MDL_ECSA);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_ECSA_START);
|
|
|
|
status = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
goto exit;
|
|
}
|
|
|
|
ret = MDL_RET_SUCCESS;
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
enum phl_mdl_ret_code
|
|
_phl_ecsa_cmd_abort(
|
|
void* dispr,
|
|
void* priv)
|
|
{
|
|
_phl_ecsa_cmd_abort_hdlr(dispr, priv, true);
|
|
return MDL_RET_SUCCESS;
|
|
}
|
|
|
|
enum phl_mdl_ret_code
|
|
_phl_ecsa_cmd_msg_hdlr(
|
|
void* dispr,
|
|
void* priv,
|
|
struct phl_msg* msg)
|
|
{
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = (struct phl_ecsa_ctrl_t *)priv;
|
|
struct rtw_wifi_role_t *wifi_role = ecsa_ctrl->role;
|
|
struct rtw_phl_com_t *phl_com = wifi_role->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
void *d = phlcom_to_drvpriv(phl_com);
|
|
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct phl_msg nextmsg = {0};
|
|
struct phl_msg_attribute attr = {0};
|
|
struct rtw_phl_ecsa_ops *ops = &ecsa_ctrl->ops;
|
|
u32 current_time_ms = _os_get_cur_time_ms();
|
|
struct rtw_bcn_info_cmn *bcn_cmn = &ecsa_ctrl->role->bcn_cmn;
|
|
u32 beacon_period_ms = bcn_cmn->bcn_interval * TU / 1000;
|
|
u8 countdown_n = 1;
|
|
struct rtw_chan_def chdef_to_switch = {0};
|
|
|
|
if(MSG_MDL_ID_FIELD(msg->msg_id) != PHL_FG_MDL_ECSA) {
|
|
return MDL_RET_IGNORE;
|
|
}
|
|
|
|
if(IS_MSG_FAIL(msg->msg_id)) {
|
|
_phl_ecsa_cmd_abort_hdlr(dispr, priv, false);
|
|
status = phl_disp_eng_free_token(phl_info,
|
|
wifi_role->hw_band,
|
|
&ecsa_ctrl->req_hdl);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_WARN("%s: Free token fail!\n", __FUNCTION__);
|
|
return MDL_RET_SUCCESS;
|
|
}
|
|
|
|
SET_MSG_MDL_ID_FIELD(nextmsg.msg_id, PHL_FG_MDL_ECSA);
|
|
|
|
switch(MSG_EVT_ID_FIELD(msg->msg_id)){
|
|
case MSG_EVT_ECSA_START:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_START\n", __FUNCTION__);
|
|
if(IS_ECSA_TYPE_AP(ecsa_ctrl)){
|
|
SET_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_BCN);
|
|
/* Update Bcn */
|
|
if(ops->update_beacon)
|
|
ops->update_beacon(ops->priv, wifi_role);
|
|
}
|
|
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl) &&
|
|
ecsa_ctrl->ecsa_param.mode == true){
|
|
_phl_ecsa_tx_pause(ecsa_ctrl);
|
|
}
|
|
_phl_ecsa_calculate_next_timer(ecsa_ctrl);
|
|
break;
|
|
case MSG_EVT_ECSA_UPDATE_FIRST_BCN_DONE:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_UPDATE_FIRST_BCN_DONE\n", __FUNCTION__);
|
|
SET_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_PROBERSP);
|
|
_phl_ecsa_calculate_next_timer(ecsa_ctrl);
|
|
break;
|
|
case MSG_EVT_ECSA_COUNT_DOWN:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_COUNT_DOWN\n", __FUNCTION__);
|
|
|
|
/* Count down mode of STA ECSA only calculate the switch time */
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl)){
|
|
_phl_ecsa_calculate_next_timer(ecsa_ctrl);
|
|
break;
|
|
}
|
|
|
|
/* Count down mode of AP ECSA calculate the update beacon time */
|
|
if(ecsa_ctrl->expected_tbtt_ms > current_time_ms){
|
|
countdown_n = 1;
|
|
}
|
|
else{
|
|
/*
|
|
* There may be delay time during msg delivery,
|
|
* calulate the actual countdown value
|
|
*/
|
|
countdown_n = (u8)((current_time_ms-(ecsa_ctrl->expected_tbtt_ms))%beacon_period_ms+1);
|
|
|
|
}
|
|
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: count down %d\n", __FUNCTION__, countdown_n);
|
|
|
|
if(ecsa_ctrl->ecsa_param.count > countdown_n){
|
|
ecsa_ctrl->ecsa_param.count -= countdown_n;
|
|
/* Update Bcn */
|
|
if(ops->update_beacon)
|
|
ops->update_beacon(ops->priv, wifi_role);
|
|
_phl_ecsa_calculate_next_timer(ecsa_ctrl);
|
|
}
|
|
else{
|
|
/*
|
|
* If the countdown value is less than 1,
|
|
* we have to switch channel immediately
|
|
*/
|
|
ecsa_ctrl->ecsa_param.count = 0;
|
|
ecsa_ctrl->state = ECSA_STATE_SWITCH;
|
|
SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_ECSA_SWITCH_START);
|
|
nextmsg.rsvd[0] = (u8*)ecsa_ctrl->role;
|
|
status = phl_disp_eng_send_msg(phl_info,
|
|
&nextmsg,
|
|
&attr,
|
|
NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: Send msg fail!\n", __FUNCTION__);
|
|
}
|
|
break;
|
|
case MSG_EVT_ECSA_SWITCH_START:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_SWITCH_START\n", __FUNCTION__);
|
|
|
|
/* Update channel info */
|
|
if(ops->update_chan_info){
|
|
ops->update_chan_info(ops->priv,
|
|
wifi_role,
|
|
ecsa_ctrl->ecsa_param.new_chan_def);
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: update_chan_info done!\n", __FUNCTION__);
|
|
}
|
|
else{
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: update_chan_info is NULL!\n", __FUNCTION__);
|
|
}
|
|
|
|
/* AP mode ECSA should update beacon to remove ECSA IE and update the channel info */
|
|
if(IS_ECSA_TYPE_AP(ecsa_ctrl)){
|
|
CLEAR_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_BCN);
|
|
CLEAR_STATUS_FLAG(ecsa_ctrl->ecsa_param.flag,
|
|
ECSA_PARAM_FLAG_APPEND_PROBERSP);
|
|
/* Update Bcn */
|
|
if(ops->update_beacon)
|
|
ops->update_beacon(ops->priv, wifi_role);
|
|
}
|
|
|
|
/*
|
|
* We should use chandef of the chanctx to switch,
|
|
* the bw may not be same as the ECSA operating class
|
|
* because of the SCC mode with different bandwidth.
|
|
*/
|
|
if(wifi_role->chanctx != NULL){
|
|
_os_mem_cpy(d, &chdef_to_switch, &(wifi_role->chanctx->chan_def),
|
|
sizeof(struct rtw_chan_def));
|
|
if(wifi_role->chanctx->chan_def.chan !=
|
|
ecsa_ctrl->ecsa_param.new_chan_def.chan)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: channel is not same as ECSA parameter!\n",
|
|
__FUNCTION__);
|
|
}
|
|
else{
|
|
_os_mem_cpy(d, &chdef_to_switch, &(ecsa_ctrl->ecsa_param.new_chan_def),
|
|
sizeof(struct rtw_chan_def));
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: chanctx of role is NULL use ECSA parameter!\n",
|
|
__FUNCTION__);
|
|
}
|
|
|
|
/* Switch channel */
|
|
phl_set_ch_bw(wifi_role, &chdef_to_switch, true);
|
|
|
|
SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_ECSA_SWITCH_DONE);
|
|
nextmsg.rsvd[0] = (u8*)ecsa_ctrl->role;
|
|
status = phl_disp_eng_send_msg(phl_info,
|
|
&nextmsg,
|
|
&attr,
|
|
NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: Send msg fail!\n", __FUNCTION__);
|
|
break;
|
|
case MSG_EVT_ECSA_SWITCH_DONE:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_SWITCH_DONE\n", __FUNCTION__);
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl) &&
|
|
ecsa_ctrl->ecsa_param.mode == true){
|
|
SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_ECSA_CHECK_TX_RESUME);
|
|
status = phl_disp_eng_send_msg(phl_info, &nextmsg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: Send msg fail!\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
|
|
SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_ECSA_DONE);
|
|
status = phl_disp_eng_send_msg(phl_info,
|
|
&nextmsg,
|
|
&attr,
|
|
NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: Send msg fail!\n", __FUNCTION__);
|
|
break;
|
|
case MSG_EVT_ECSA_CHECK_TX_RESUME:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_CHECK_TX_RESUME\n", __FUNCTION__);
|
|
if(IS_ECSA_TYPE_STA(ecsa_ctrl) &&
|
|
ecsa_ctrl->ecsa_param.mode == true){
|
|
/*
|
|
* TODO: If driver support DFS-slave with radar
|
|
* detection, ECSA should tx un-pause directly
|
|
* and the tx pause should be handled by DFS-slave.
|
|
*/
|
|
if(ops->check_tx_resume_allow){
|
|
if(!ops->check_tx_resume_allow(ops->priv, wifi_role)){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: Keep Tx pause...\n", __FUNCTION__);
|
|
_phl_ecsa_calculate_next_timer(ecsa_ctrl);
|
|
break;
|
|
}
|
|
}
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: Tx resume!\n", __FUNCTION__);
|
|
_phl_ecsa_tx_resume(ecsa_ctrl);
|
|
}
|
|
|
|
SET_MSG_EVT_ID_FIELD(nextmsg.msg_id, MSG_EVT_ECSA_DONE);
|
|
status = phl_disp_eng_send_msg(phl_info, &nextmsg, &attr, NULL);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: Send msg fail!\n", __FUNCTION__);
|
|
|
|
break;
|
|
case MSG_EVT_ECSA_DONE:
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_,
|
|
"%s: MSG_EVT_ECSA_DONE\n", __FUNCTION__);
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
|
|
if(ops->ecsa_complete){
|
|
ops->ecsa_complete(ops->priv, wifi_role);
|
|
}
|
|
else{
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: ecsa_complete is NULL!\n", __FUNCTION__);
|
|
}
|
|
|
|
status = phl_disp_eng_free_token(phl_info,
|
|
wifi_role->hw_band,
|
|
&ecsa_ctrl->req_hdl);
|
|
if(status != RTW_PHL_STATUS_SUCCESS)
|
|
PHL_WARN("%s: Free token fail!\n", __FUNCTION__);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
enum phl_mdl_ret_code
|
|
_phl_ecsa_cmd_set_info(
|
|
void* dispr,
|
|
void* priv,
|
|
struct phl_module_op_info* info)
|
|
{
|
|
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
|
|
|
|
/* PHL_INFO(" %s :: info->op_code=%d \n", __func__, info->op_code); */
|
|
return ret;
|
|
}
|
|
|
|
enum phl_mdl_ret_code
|
|
_phl_ecsa_cmd_query_info(
|
|
void* dispr,
|
|
void* priv,
|
|
struct phl_module_op_info* info)
|
|
{
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = (struct phl_ecsa_ctrl_t *)priv;
|
|
enum phl_mdl_ret_code ret = MDL_RET_IGNORE;
|
|
/* PHL_INFO(" %s :: info->op_code=%d \n", __func__, info->op_code); */
|
|
|
|
switch(info->op_code) {
|
|
case FG_REQ_OP_GET_ROLE:
|
|
info->outbuf = (u8*)ecsa_ctrl->role;
|
|
ret = MDL_RET_SUCCESS;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_ecsa_cmd_request(
|
|
void *phl,
|
|
struct rtw_wifi_role_t *role
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
struct phl_cmd_token_req req={0};
|
|
|
|
if(ecsa_ctrl == NULL)
|
|
goto exit;
|
|
|
|
/* Fill foreground command request */
|
|
req.module_id= PHL_FG_MDL_ECSA;
|
|
req.priv = ecsa_ctrl;
|
|
req.role = role;
|
|
|
|
req.acquired = _phl_ecsa_cmd_acquired;
|
|
req.abort = _phl_ecsa_cmd_abort;
|
|
req.msg_hdlr = _phl_ecsa_cmd_msg_hdlr;
|
|
req.set_info = _phl_ecsa_cmd_set_info;
|
|
req.query_info = _phl_ecsa_cmd_query_info;
|
|
|
|
status = phl_disp_eng_add_token_req(phl, role->hw_band, &req,
|
|
&ecsa_ctrl->req_hdl);
|
|
if((status != RTW_PHL_STATUS_SUCCESS) &&
|
|
(status != RTW_PHL_STATUS_PENDING))
|
|
goto exit;
|
|
|
|
status = RTW_PHL_STATUS_SUCCESS;
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_ecsa_start(
|
|
void *phl,
|
|
struct rtw_wifi_role_t *role,
|
|
struct rtw_phl_ecsa_param *param
|
|
)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *d = phlcom_to_drvpriv(phl_info->phl_com);
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
struct rtw_phl_ecsa_param *ecsa_param = &(ecsa_ctrl->ecsa_param);
|
|
|
|
if(ecsa_ctrl == NULL)
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
if(ecsa_ctrl->state != ECSA_STATE_NONE){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_, "%s: ECSA already started!\n",
|
|
__FUNCTION__);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
}
|
|
|
|
ecsa_ctrl->role = role;
|
|
_os_mem_cpy(d, ecsa_param, param, sizeof(struct rtw_phl_ecsa_param));
|
|
_phl_ecsa_dump_param(ecsa_param);
|
|
ecsa_ctrl->state = ECSA_STATE_WAIT_DELAY;
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_INFO_, "%s: ECSA start after %dms !\n",
|
|
__FUNCTION__, ecsa_ctrl->ecsa_param.delay_start_ms);
|
|
_os_set_timer(d, &ecsa_ctrl->timer, ecsa_ctrl->ecsa_param.delay_start_ms);
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_ecsa_cancel(
|
|
void *phl,
|
|
struct rtw_wifi_role_t *role
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *d = phlcom_to_drvpriv(phl_info->phl_com);
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
|
|
if(ecsa_ctrl == NULL){
|
|
status = RTW_PHL_STATUS_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
if(ecsa_ctrl->state == ECSA_STATE_NONE)
|
|
goto exit;
|
|
|
|
_os_cancel_timer(d, &ecsa_ctrl->timer);
|
|
|
|
_os_spinlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
if(ecsa_ctrl->state > ECSA_STATE_WAIT_DELAY){
|
|
status = phl_disp_eng_cancel_token_req(phl_info,
|
|
role->hw_band,
|
|
&ecsa_ctrl->req_hdl);
|
|
|
|
if(status != RTW_PHL_STATUS_SUCCESS){
|
|
PHL_TRACE(COMP_PHL_ECSA, _PHL_WARNING_,
|
|
"%s: ECSA cancel req fail!\n", __FUNCTION__);
|
|
}
|
|
|
|
}
|
|
else{
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
}
|
|
_os_spinunlock(d, &(ecsa_ctrl->lock), _bh, NULL);
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_ecsa_get_param(
|
|
void *phl,
|
|
struct rtw_phl_ecsa_param **param
|
|
)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
|
|
if(ecsa_ctrl == NULL)
|
|
goto exit;
|
|
|
|
*param = &ecsa_ctrl->ecsa_param;
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
phl_ecsa_ctrl_init(
|
|
struct phl_info_t *phl_info
|
|
)
|
|
{
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl = NULL;
|
|
|
|
ecsa_ctrl = _os_mem_alloc(drv_priv, sizeof(struct phl_ecsa_ctrl_t));
|
|
if (ecsa_ctrl == NULL) {
|
|
phl_info->ecsa_ctrl = NULL;
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
}
|
|
|
|
phl_info->ecsa_ctrl = ecsa_ctrl;
|
|
|
|
/* set default value */
|
|
ecsa_ctrl->state = ECSA_STATE_NONE;
|
|
ecsa_ctrl->phl_com = phl_info->phl_com;
|
|
ecsa_ctrl->role = NULL;
|
|
ecsa_ctrl->expected_tbtt_ms = 0;
|
|
|
|
_os_init_timer(drv_priv, &ecsa_ctrl->timer, _phl_ecsa_timer_callback,
|
|
ecsa_ctrl, "phl_ecsa_timer");
|
|
|
|
_os_spinlock_init(drv_priv, &(ecsa_ctrl->lock));
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
void
|
|
phl_ecsa_ctrl_deinit(
|
|
struct phl_info_t *phl_info
|
|
)
|
|
{
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)(phl_info->ecsa_ctrl);
|
|
|
|
if (ecsa_ctrl == NULL)
|
|
return;
|
|
_os_spinlock_free(drv_priv, &(ecsa_ctrl->lock));
|
|
_os_release_timer(drv_priv, &ecsa_ctrl->timer);
|
|
_os_mem_free(drv_priv, ecsa_ctrl, sizeof(struct phl_ecsa_ctrl_t));
|
|
|
|
phl_info->ecsa_ctrl = NULL;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_ecsa_init_ops(
|
|
void *phl,
|
|
struct rtw_phl_ecsa_ops *ops)
|
|
{
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
|
|
if(ecsa_ctrl == NULL)
|
|
goto exit;
|
|
|
|
ecsa_ctrl->ops.priv = ops->priv;
|
|
ecsa_ctrl->ops.update_beacon = ops->update_beacon;
|
|
ecsa_ctrl->ops.update_chan_info = ops->update_chan_info;
|
|
ecsa_ctrl->ops.check_ecsa_allow = ops->check_ecsa_allow;
|
|
ecsa_ctrl->ops.ecsa_complete = ops->ecsa_complete;
|
|
ecsa_ctrl->ops.check_tx_resume_allow = ops->check_tx_resume_allow;
|
|
status = RTW_PHL_STATUS_SUCCESS;
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
#ifdef CONFIG_PHL_ECSA_EXTEND_OPTION
|
|
void
|
|
rtw_phl_ecsa_extend_option_hdlr(
|
|
u32 extend_option,
|
|
struct rtw_phl_ecsa_param *param
|
|
)
|
|
{
|
|
if ((extend_option & ECSA_EX_OPTION_FORCE_BW20) &&
|
|
(param->new_chan_def.bw != CHANNEL_WIDTH_20)) {
|
|
/* force 20M mode, set attributes accordingly */
|
|
param->new_chan_def.bw = CHANNEL_WIDTH_20;
|
|
param->new_chan_def.center_ch = param->new_chan_def.chan;
|
|
param->new_chan_def.offset = CHAN_OFFSET_NO_EXT;
|
|
param->op_class = rtw_phl_get_operating_class(param->new_chan_def);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
rtw_phl_ecsa_check_allow(
|
|
void *phl,
|
|
struct rtw_wifi_role_t *role,
|
|
struct rtw_chan_def chan_def,
|
|
enum phl_ecsa_start_reason reason,
|
|
#ifdef CONFIG_PHL_ECSA_EXTEND_OPTION
|
|
u32 *extend_option,
|
|
#endif
|
|
u32 *delay_start_ms
|
|
)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct phl_ecsa_ctrl_t *ecsa_ctrl =
|
|
(struct phl_ecsa_ctrl_t *)phl_info->ecsa_ctrl;
|
|
struct rtw_phl_ecsa_ops *ops = &(ecsa_ctrl->ops);
|
|
bool ecsa_allow = false;
|
|
|
|
if(ops->check_ecsa_allow)
|
|
ecsa_allow = ops->check_ecsa_allow(ops->priv,
|
|
role,
|
|
chan_def,
|
|
reason,
|
|
#ifdef CONFIG_PHL_ECSA_EXTEND_OPTION
|
|
extend_option,
|
|
#endif
|
|
delay_start_ms);
|
|
return ecsa_allow;
|
|
}
|
|
#endif /* CONFIG_PHL_ECSA */ |