android13/external/wifi_driver/rtl8852be/phl/phl_ecsa.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 */