435 lines
13 KiB
C
435 lines
13 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2007 - 2022 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 _HAL_DFS_C_
|
|
|
|
#include <drv_types.h>
|
|
#include <hal_data.h>
|
|
|
|
/* be careful about the sychronization with phydm */
|
|
#define PHYDM_DFS_DOMAIN_NUM PHYDM_DFS_DOMAIN_ETSI + 1
|
|
#define PHYDM_DFS_DOMAIN_IS_UNKNOWN(domain) ((domain) == PHYDM_DFS_DOMAIN_UNKNOWN || (domain) >= PHYDM_DFS_DOMAIN_NUM)
|
|
|
|
#ifdef CONFIG_DFS_MASTER
|
|
struct dfs_rd_ctl_param {
|
|
/*
|
|
* DFS region domain set from core
|
|
* >=PHYDM_DFS_DOMAIN_NUM => not set (keep original)
|
|
* < PHYDM_DFS_DOMAIN_NUM => change domain (if needed) and ignore other parameters
|
|
*/
|
|
enum phydm_dfs_region_domain domain;
|
|
|
|
/*
|
|
* enable=true, cac=1 => enable radar detect and is under CAC
|
|
* enable=true, cac=0 => enalbe radar detect, in-service monitoring
|
|
* enable=true, cac<0 => enable radar detect w/o changing CAC status
|
|
* enable=false, cac=any => disable radar detect
|
|
*/
|
|
bool enable;
|
|
|
|
/*
|
|
* CAC status set from core
|
|
* < 0: not set (keep original)
|
|
* 0: not under CAC
|
|
* 1: under CAC
|
|
*/
|
|
s8 cac;
|
|
|
|
/*
|
|
* configuration for specific radar detect range (5G band is implicit), the value of sp_ch:
|
|
* < 0: not set (keep original)
|
|
* 0: all detectable range
|
|
* > 0: specific detect range (by ch, bw, offset)
|
|
*/
|
|
s16 sp_ch;
|
|
enum channel_width sp_bw;
|
|
enum chan_offset sp_offset;
|
|
};
|
|
|
|
static bool hal_bchbw_in_radar_domain(enum band_type band, u8 ch
|
|
, enum channel_width bw, enum chan_offset offset)
|
|
{
|
|
return band == BAND_ON_5G
|
|
&& ((ch >= 52 && ch <= 64) || (ch >= 100 && ch <= 144))
|
|
;
|
|
}
|
|
|
|
static bool hal_radar_detect_range_specified(struct rtw_dfs_t *dfs_info)
|
|
{
|
|
return dfs_info->sp_detect_range_hi != 0;
|
|
}
|
|
|
|
static bool hal_overlap_radar_detect_range(struct rtw_dfs_t *dfs_info
|
|
, enum band_type band, u8 ch, enum channel_width bw, enum chan_offset offset)
|
|
{
|
|
bool ret = false;
|
|
u32 hi = 0, lo = 0;
|
|
int i;
|
|
|
|
if (!rtw_bchbw_to_freq_range(band, ch, bw, offset, &hi, &lo)) {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
if (rtw_is_range_overlap(hi, lo, dfs_info->sp_detect_range_hi, dfs_info->sp_detect_range_lo))
|
|
ret = true;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static bool hal_should_radar_detect_enable_by_ch(struct hal_com_data *hal_data, u8 band_idx,
|
|
enum band_type band, u8 channel, enum channel_width bwmode, enum chan_offset offset)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
|
|
if (dfs_info->enable && !PHYDM_DFS_DOMAIN_IS_UNKNOWN(dfs_info->region_domain)
|
|
&& hal_bchbw_in_radar_domain(band, channel, bwmode, offset)
|
|
) {
|
|
if (!hal_radar_detect_range_specified(dfs_info)
|
|
|| hal_overlap_radar_detect_range(dfs_info, band, channel, bwmode, offset))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void hal_dfs_handle_pending_domain_change(struct hal_com_data *hal_data, u8 band_idx
|
|
, enum band_type band, u8 ch, enum channel_width bw, enum chan_offset offset)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
|
|
if (dfs_info->pending_domain_change) {
|
|
if (dfs_info->radar_detect_enabled)
|
|
phydm_radar_detect_disable(&hal_data->odmpriv);
|
|
odm_cmn_info_init(&hal_data->odmpriv, ODM_CMNINFO_DFS_REGION_DOMAIN
|
|
, hal_data->dfs_info.region_domain);
|
|
if (dfs_info->radar_detect_enabled)
|
|
phydm_radar_detect_enable(&hal_data->odmpriv);
|
|
|
|
dfs_info->pending_domain_change = false;
|
|
}
|
|
}
|
|
|
|
static int
|
|
hal_radar_detect_switch(struct hal_com_data *hal_data, u8 band_idx, bool enable)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
|
|
if (enable)
|
|
phydm_radar_detect_enable(&hal_data->odmpriv);
|
|
else
|
|
phydm_radar_detect_disable(&hal_data->odmpriv);
|
|
|
|
dfs_info->radar_detect_enabled = enable;
|
|
return _SUCCESS;
|
|
}
|
|
|
|
bool hal_is_radar_detect_enabled(struct hal_com_data *hal_data, u8 band_idx)
|
|
{
|
|
return hal_data->dfs_info.radar_detect_enabled;
|
|
}
|
|
|
|
static void hal_set_under_cac(struct hal_com_data *hal_data, u8 band_idx, bool under)
|
|
{
|
|
hal_data->dfs_info.under_cac = under;
|
|
}
|
|
|
|
bool hal_is_under_cac(struct hal_com_data *hal_data, u8 band_idx)
|
|
{
|
|
return hal_data->dfs_info.under_cac;
|
|
}
|
|
|
|
static int
|
|
hal_cac_tx_pause_switch(struct hal_com_data *hal_data, u8 band_idx, bool enable)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
|
|
if (rtw_hal_tx_pause(hal_data->adapter, PAUSE_RSON_DFS_CAC, enable) == _SUCCESS) {
|
|
dfs_info->cac_tx_paused = enable;
|
|
return _SUCCESS;
|
|
}
|
|
return _FAIL;
|
|
}
|
|
|
|
bool hal_is_cac_tx_paused(struct hal_com_data *hal_data, u8 band_idx)
|
|
{
|
|
return hal_data->dfs_info.cac_tx_paused;
|
|
}
|
|
|
|
void hal_dfs_rd_setting_before_ch_switch(struct hal_com_data *hal_data, u8 band_idx
|
|
, enum band_type band, u8 ch, enum channel_width bw, enum chan_offset offset, struct dfs_rd_ch_switch_ctx *ctx)
|
|
{
|
|
ctx->should_rd_en_on_new_ch = hal_should_radar_detect_enable_by_ch(hal_data
|
|
, band_idx, band, ch, bw, offset);
|
|
ctx->under_cac = hal_is_under_cac(hal_data, band_idx);
|
|
ctx->cac_tx_paused = hal_is_cac_tx_paused(hal_data, band_idx);
|
|
|
|
ctx->rd_enabled = hal_is_radar_detect_enabled(hal_data, band_idx);
|
|
|
|
if (!ctx->should_rd_en_on_new_ch && ctx->rd_enabled) {
|
|
/* turn off radar detect before channel setting (ex: leaving detection range) */
|
|
int rst = hal_radar_detect_switch(hal_data, band_idx, false);
|
|
|
|
if (rst == _SUCCESS)
|
|
RTW_INFO("[DFS] new ch=%d,%u,%d,%d disable radar detect\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] new ch=%d,%u,%d,%d disable radar detect failed\n", band, ch, bw, offset);
|
|
|
|
ctx->rd_enabled = hal_is_radar_detect_enabled(hal_data, band_idx);
|
|
}
|
|
|
|
if (ctx->should_rd_en_on_new_ch && ctx->under_cac && !ctx->cac_tx_paused) {
|
|
/* turn on CAC tx pause before channel setting (ex: entering detection range) */
|
|
int rst = hal_cac_tx_pause_switch(hal_data, band_idx, true);
|
|
|
|
if (rst == _SUCCESS)
|
|
RTW_INFO("[DFS] new ch=%d,%u,%d,%d enable CAC tx pause\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] new ch=%d,%u,%d,%d enable CAC tx pause failed\n", band, ch, bw, offset);
|
|
}
|
|
}
|
|
|
|
void hal_dfs_rd_setting_after_ch_switch(struct hal_com_data *hal_data, u8 band_idx
|
|
, enum band_type band, u8 ch, enum channel_width bw, enum chan_offset offset, struct dfs_rd_ch_switch_ctx *ctx)
|
|
{
|
|
if (ctx->should_rd_en_on_new_ch && !ctx->rd_enabled) {
|
|
/* turn on radar detect after channel setting (ex: entered radar detect range) */
|
|
int rst = hal_radar_detect_switch(hal_data, band_idx, true);
|
|
|
|
if (rst == _SUCCESS)
|
|
RTW_INFO("[DFS] new ch=%d,%u,%d,%d enable radar detect\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] new ch=%d,%u,%d,%d enable radar detect failed\n", band, ch, bw, offset);
|
|
}
|
|
|
|
if ((!ctx->should_rd_en_on_new_ch || !ctx->under_cac) && ctx->cac_tx_paused) {
|
|
/* turn off CAC tx pause after channel setting (ex: leaved detection range) */
|
|
int rst = hal_cac_tx_pause_switch(hal_data, band_idx, false);
|
|
|
|
if (rst == _SUCCESS)
|
|
RTW_INFO("[DFS] new ch=%d,%u,%d,%d disable CAC tx pause\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] new ch=%d,%u,%d,%d disable CAC tx pause failed\n", band, ch, bw, offset);
|
|
}
|
|
}
|
|
|
|
static int
|
|
hal_radar_detect_confs_apply(struct hal_com_data *hal_data, u8 band_idx)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
int rd_rst = _SUCCESS;
|
|
int cac_rst = _SUCCESS;
|
|
u8 band, ch, bw, offset;
|
|
bool should_rd_enable;
|
|
bool under_cac;
|
|
bool cac_tx_paused;
|
|
|
|
ch = rtw_get_oper_ch(hal_data->adapter);
|
|
bw = rtw_get_oper_bw(hal_data->adapter);
|
|
offset = rtw_get_oper_choffset(hal_data->adapter);
|
|
band = rtw_is_2g_ch(ch) ? BAND_ON_24G : BAND_ON_5G;
|
|
|
|
hal_dfs_handle_pending_domain_change(hal_data, band_idx
|
|
, band, ch, bw, offset);
|
|
|
|
should_rd_enable = hal_should_radar_detect_enable_by_ch(hal_data, band_idx
|
|
, band, ch, bw, offset);
|
|
under_cac = hal_is_under_cac(hal_data, band_idx);
|
|
cac_tx_paused = hal_is_cac_tx_paused(hal_data, band_idx);
|
|
|
|
if (!dfs_info->radar_detect_enabled) {
|
|
if (should_rd_enable) {
|
|
rd_rst = hal_radar_detect_switch(hal_data, band_idx, true);
|
|
if (rd_rst == _SUCCESS)
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d enable radar detect\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] ch=%d,%u,%d,%d enable radar detect failed\n", band, ch, bw, offset);
|
|
}
|
|
|
|
} else if (dfs_info->radar_detect_enabled) {
|
|
if (!should_rd_enable) {
|
|
rd_rst = hal_radar_detect_switch(hal_data, band_idx, false);
|
|
if (rd_rst == _SUCCESS)
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d disable radar detect\n", band, ch, bw, offset);
|
|
else
|
|
RTW_ERR("[DFS] ch=%d,%u,%d,%d disable radar detect failed\n", band, ch, bw, offset);
|
|
}
|
|
}
|
|
|
|
if (!cac_tx_paused) {
|
|
if (under_cac && dfs_info->radar_detect_enabled) {
|
|
cac_rst = hal_cac_tx_pause_switch(hal_data, band_idx, true);
|
|
if (cac_rst == _SUCCESS)
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d enable CAC tx pause\n", band, ch, bw, offset);
|
|
else
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d enable CAC tx pause failed\n", band, ch, bw, offset);
|
|
}
|
|
|
|
} else if (cac_tx_paused) {
|
|
if (!under_cac) {
|
|
/*
|
|
* Release CAC tx pause only when not under CAC
|
|
* Keep CAC tx pause when under CAC and radar detect is turned off by
|
|
* specifying new detect range which doesn't overlap current channel setting
|
|
* (ex: operating channel switching to new DFS channel)
|
|
*/
|
|
cac_rst = hal_cac_tx_pause_switch(hal_data, band_idx, false);
|
|
if (cac_rst == _SUCCESS)
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d disable CAC tx pause\n", band, ch, bw, offset);
|
|
else
|
|
RTW_INFO("[DFS] ch=%d,%u,%d,%d disable CAC tx pause failed\n", band, ch, bw, offset);
|
|
}
|
|
}
|
|
|
|
if (rd_rst == _SUCCESS && cac_rst == _SUCCESS)
|
|
return _SUCCESS;
|
|
return _FAIL;
|
|
}
|
|
|
|
static int
|
|
hal_dfs_rd_ctl_hdl(struct hal_com_data *hal_data, struct dfs_rd_ctl_param *rd_ctl_param)
|
|
{
|
|
struct rtw_dfs_t *dfs_info = &hal_data->dfs_info;
|
|
u8 band_idx = HW_BAND_0; /* multi band/phy capable? */
|
|
|
|
if (rd_ctl_param->domain < PHYDM_DFS_DOMAIN_NUM) {
|
|
if (rd_ctl_param->domain != dfs_info->region_domain) {
|
|
RTW_INFO("%s set domain to %d\n", __func__, rd_ctl_param->domain);
|
|
dfs_info->region_domain = rd_ctl_param->domain;
|
|
dfs_info->pending_domain_change = true;
|
|
}
|
|
goto apply;
|
|
}
|
|
|
|
if (rd_ctl_param->enable) {
|
|
if (!dfs_info->enable) {
|
|
RTW_INFO("%s enable\n", __func__);
|
|
dfs_info->enable = true;
|
|
}
|
|
if (rd_ctl_param->cac == 1) {
|
|
if (!hal_is_under_cac(hal_data, band_idx)) {
|
|
RTW_INFO("%s under CAC\n", __func__);
|
|
hal_set_under_cac(hal_data, band_idx, true);
|
|
}
|
|
} else if (rd_ctl_param->cac == 0) {
|
|
if (hal_is_under_cac(hal_data, band_idx)) {
|
|
RTW_INFO("%s CAC done\n", __func__);
|
|
hal_set_under_cac(hal_data, band_idx, false);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (dfs_info->enable) {
|
|
RTW_INFO("%s disable\n", __func__);
|
|
dfs_info->enable = false;
|
|
}
|
|
hal_set_under_cac(hal_data, band_idx, false);
|
|
}
|
|
|
|
if (rd_ctl_param->sp_ch > 0) {
|
|
u32 hi, lo;
|
|
|
|
if (rtw_bchbw_to_freq_range(BAND_ON_5G
|
|
, rd_ctl_param->sp_ch, rd_ctl_param->sp_bw, rd_ctl_param->sp_offset
|
|
, &hi, &lo)
|
|
) {
|
|
RTW_INFO("%s sp_ch:%u,%d,%d is set\n", __func__
|
|
, rd_ctl_param->sp_ch, rd_ctl_param->sp_bw, rd_ctl_param->sp_offset);
|
|
dfs_info->sp_detect_range_hi = hi;
|
|
dfs_info->sp_detect_range_lo = lo;
|
|
} else {
|
|
RTW_WARN("%s sp_ch:%u,%d,%d to freq range fail, all range applied\n", __func__
|
|
, rd_ctl_param->sp_ch, rd_ctl_param->sp_bw, rd_ctl_param->sp_offset);
|
|
dfs_info->sp_detect_range_hi = 0;
|
|
}
|
|
} else if (rd_ctl_param->sp_ch == 0) {
|
|
RTW_INFO("%s all range applied\n", __func__);
|
|
dfs_info->sp_detect_range_hi = 0;
|
|
}
|
|
|
|
apply:
|
|
/* apply new configs on cur channel */
|
|
return hal_radar_detect_confs_apply(hal_data, band_idx);
|
|
}
|
|
|
|
static int
|
|
_rtw_hal_dfs_rd_ctl(struct hal_com_data *hal_data, enum phl_band_idx hw_band
|
|
, enum rtw_dfs_regd domain, bool enable, s8 cac, s16 sp_ch, enum channel_width sp_bw, enum chan_offset sp_offset)
|
|
{
|
|
int ret;
|
|
struct dfs_rd_ctl_param param;
|
|
|
|
param.domain = domain;
|
|
param.enable = enable;
|
|
param.cac = cac;
|
|
param.sp_ch = sp_ch;
|
|
param.sp_bw = sp_bw;
|
|
param.sp_offset = sp_offset;
|
|
|
|
ret = hal_dfs_rd_ctl_hdl(hal_data, ¶m);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
rtw_hal_dfs_change_domain(struct hal_com_data *hal_data, enum phl_band_idx hw_band
|
|
, enum phydm_dfs_region_domain domain)
|
|
{
|
|
if (domain >= PHYDM_DFS_DOMAIN_NUM) {
|
|
RTW_WARN("%s(), invalid domain:%d\n", __func__, domain);
|
|
return _FAIL;
|
|
}
|
|
|
|
return _rtw_hal_dfs_rd_ctl(hal_data, hw_band
|
|
, domain /* change domain, other parameters will be ignored */
|
|
, false, 0, -1, 0, 0);
|
|
}
|
|
|
|
int
|
|
rtw_hal_dfs_rd_enable_all_range(struct hal_com_data *hal_data, enum phl_band_idx hw_band)
|
|
{
|
|
return _rtw_hal_dfs_rd_ctl(hal_data, hw_band
|
|
, PHYDM_DFS_DOMAIN_NUM
|
|
, true, -1 /* enable radar detect w/o changing CAC status */
|
|
, 0, 0, 0);
|
|
}
|
|
|
|
int
|
|
rtw_hal_dfs_rd_enable_with_sp_chbw(struct hal_com_data *hal_data, enum phl_band_idx hw_band
|
|
, bool cac, u8 sp_ch, enum channel_width sp_bw, enum chan_offset sp_offset)
|
|
{
|
|
return _rtw_hal_dfs_rd_ctl(hal_data, hw_band
|
|
, PHYDM_DFS_DOMAIN_NUM
|
|
, true, cac ? 1 : 0, sp_ch, sp_bw, sp_offset);
|
|
}
|
|
|
|
int
|
|
rtw_hal_dfs_rd_set_cac_status(struct hal_com_data *hal_data, enum phl_band_idx hw_band, bool cac)
|
|
{
|
|
return _rtw_hal_dfs_rd_ctl(hal_data, hw_band
|
|
, PHYDM_DFS_DOMAIN_NUM
|
|
, true /* CAC status only valid when radar detect enable */
|
|
, cac ? 1 : 0, -1, 0, 0);
|
|
}
|
|
|
|
int
|
|
rtw_hal_dfs_rd_disable(struct hal_com_data *hal_data, enum phl_band_idx hw_band)
|
|
{
|
|
return _rtw_hal_dfs_rd_ctl(hal_data, hw_band
|
|
, PHYDM_DFS_DOMAIN_NUM
|
|
, false, 0, -1, 0, 0);
|
|
}
|
|
#endif
|