new/external/rkwifibt/drivers/rtl8852be/phl/phl_regulation_6g.c
2025-05-10 21:58:58 +08:00

332 lines
8.0 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.
*
*****************************************************************************/
#include "phl_headers.h"
#include "phl_chnlplan.h"
#include "phl_chnlplan_6g.h"
#include "phl_country.h"
extern const struct chdef_6ghz chdef6g[MAX_CHDEF_6GHZ];
extern const struct regulatory_domain_mapping_6g rdmap6[MAX_RD_MAP_NUM_6GHZ];
static void _get_6ghz_ch_info(const struct chdef_6ghz *chdef,
u8 group, u32 *ch, u32 *passive, u8 *max_num, u8 *ch_start)
{
switch (group) {
case FREQ_GROUP_6GHZ_UNII5:
*ch = ((chdef->support_ch_u5[2] << 16) |
(chdef->support_ch_u5[1] << 8) |
(chdef->support_ch_u5[0]));
*passive = ((chdef->passive_u5[2] << 16) |
(chdef->passive_u5[1] << 8) |
(chdef->passive_u5[0]));
*max_num = MAX_CH_NUM_UNII5;
*ch_start = 1;
break;
case FREQ_GROUP_6GHZ_UNII6:
*ch = chdef->support_ch_u6;
*passive = chdef->passive_u6;
*max_num = MAX_CH_NUM_UNII6;
*ch_start = 97;
break;
case FREQ_GROUP_6GHZ_UNII7:
*ch = ((chdef->support_ch_u7[2] << 16) |
(chdef->support_ch_u7[1] << 8) |
(chdef->support_ch_u7[0]));
*passive = ((chdef->passive_u7[2] << 16) |
(chdef->passive_u7[1] << 8) |
(chdef->passive_u7[0]));
*max_num = MAX_CH_NUM_UNII7;
*ch_start = 121;
break;
case FREQ_GROUP_6GHZ_UNII8:
*ch = ((chdef->support_ch_u8[1] << 8) |
(chdef->support_ch_u8[0]));
*passive = ((chdef->passive_u8[1] << 8) |
(chdef->passive_u8[0]));
*max_num = MAX_CH_NUM_UNII8;
*ch_start = 193;
break;
default:
*ch = 0;
*passive = 0;
*max_num = 0;
*ch_start = 0;
break;
}
}
static void _convert_ch6g(u8 unii_6g, struct rtw_regulation *rg,
u32 *ch_cnt, struct rtw_regulation_channel *rch,
u32 ch, u32 passive, u8 max_num, u8 ch_start)
{
u16 i = 0;
u32 shift = 0;
u8 property = 0;
u32 cnt = 0;
PHL_INFO("[REGU], convert 6 ghz unii-%d channels, from %d, ch=0x%x, passive = 0x%x \n",
unii_6g, ch_start, ch, passive);
for (i = 0; i < max_num; i++) {
shift = (1 << i);
if (ch & shift) {
property = 0;
rch[*ch_cnt].band = BAND_ON_6G;
rch[*ch_cnt].channel = (u8)(ch_start + (i * 4));
if (passive & shift)
property |= CH_PASSIVE;
if ((rch[*ch_cnt].channel % 16) == 5)
property |= CH_PSC;
rch[*ch_cnt].property = property;
PHL_INFO("[REGU], ch: %d%s%s \n",
rch[*ch_cnt].channel,
((property & CH_PASSIVE) ? ", passive" : ""),
((property & CH_PSC) ? ", psc" : ""));
(*ch_cnt)++;
cnt++;
}
}
PHL_INFO("[REGU], converted channels : %d\n", cnt);
}
static void _update_psc_group(struct rtw_regulation *rg)
{
u8 group = FREQ_GROUP_6GHZ_UNII5;
struct rtw_regulation_chplan_group *plan =
&rg->chplan[FREQ_GROUP_6GHZ_PSC];
struct rtw_regulation_chplan_group *src = NULL;
u32 i = 0, j = 0;
plan->cnt = 0;
for (i = 0; i < 4; i++) {
group = (u8)(i + FREQ_GROUP_6GHZ_UNII5);
src = &rg->chplan[group];
for (j = 0; j < src->cnt; j++) {
if (src->ch[j].property & CH_PSC) {
plan->ch[plan->cnt].band =
src->ch[j].band;
plan->ch[plan->cnt].channel =
src->ch[j].channel;
plan->ch[plan->cnt].property =
src->ch[j].property;
plan->cnt++;
}
}
}
}
static bool _chnlplan_update_6g(struct rtw_regulation *rg,
u8 regulation, u8 ch_idx)
{
const struct chdef_6ghz *chdef = NULL;
struct rtw_regulation_chplan_group *plan = NULL;
u8 group = FREQ_GROUP_6GHZ_UNII5;
u8 max_num = 0, ch_start = 0;
u16 i = 0;
u32 ch = 0, passive = 0;
u32 total = 0;
if (regulation >= REGULATION_MAX)
return false;
for (i = 0; i < MAX_CHDEF_6GHZ; i++) {
if (ch_idx == chdef6g[i].idx) {
chdef = &chdef6g[i];
break;
}
}
if (!chdef)
return false;
rg->ch_idx6g = ch_idx;
rg->regulation_6g = regulation;
for (i = 0; i < 4; i++) {
group = (u8)(i + FREQ_GROUP_6GHZ_UNII5);
plan = &rg->chplan[group];
plan->cnt = 0;
_get_6ghz_ch_info(chdef, group,
&ch, &passive, &max_num, &ch_start);
_convert_ch6g((u8)(i + 5), rg, &plan->cnt, plan->ch,
ch, passive, max_num, ch_start);
total += plan->cnt;
}
_update_psc_group(rg);
PHL_INFO("[REGU], 6 GHz, total channel = %d\n", total);
return true;
}
static u8 _domain_index_6g(u16 domain)
{
u8 i = 0;
for (i = 0; i < MAX_RD_MAP_NUM_6GHZ; i++) {
if (domain == rdmap6[i].domain_code) {
return i;
}
}
return MAX_RD_MAP_NUM_6GHZ;
}
static bool _regulatory_domain_update_6g(struct rtw_regulation *rg,
u16 domain, enum regulation_rsn reason)
{
u8 regulation = REGULATION_NA;
u8 ch_idx = 0, did = 0;
rg->domain_6g.reason = reason;
if (domain == RSVD_DOMAIN) {
rg->domain_6g.code = RSVD_DOMAIN;
return true;
} else {
/* Note: only valid domain index can reach here */
did = _domain_index_6g(domain);
rg->domain_6g.code = rdmap6[did].domain_code;
regulation = rdmap6[did].regulation;
ch_idx = rdmap6[did].ch_idx;
return _chnlplan_update_6g(rg, regulation, ch_idx);
}
}
static void _get_group_chplan_6g(struct rtw_regulation *rg,
struct rtw_regulation_chplan_group *group,
struct rtw_regulation_chplan *plan)
{
u32 i = 0;
for (i = 0; i < group->cnt; i++) {
if (group->ch[i].channel) {
plan->ch[plan->cnt].band =
group->ch[i].band;
plan->ch[plan->cnt].channel =
group->ch[i].channel;
plan->ch[plan->cnt].property =
group->ch[i].property;
plan->cnt++;
}
}
}
static void _history_log_6g(struct rtw_regulation *rg, u16 domain, u8 reason)
{
rg->history_6g[rg->history_cnt_6g].code = domain;
rg->history_6g[rg->history_cnt_6g].reason = reason;
rg->history_cnt_6g++;
if (rg->history_cnt_6g >= MAX_HISTORY_NUM)
rg->history_cnt_6g = 0;
}
void regu_get_chnlplan_6g(struct rtw_regulation *rg,
enum rtw_regulation_query type,
struct rtw_regulation_chplan *plan)
{
struct rtw_regulation_chplan_group *group = NULL;
/* 6ghz */
if (rg->capability & CAPABILITY_6GHZ) {
/* unii5 */
if (type == REGULQ_CHPLAN_6GHZ ||
type == REGULQ_CHPLAN_6GHZ_UNII5) {
group = &rg->chplan[FREQ_GROUP_6GHZ_UNII5];
_get_group_chplan_6g(rg, group, plan);
}
/* unii6 */
if (type == REGULQ_CHPLAN_6GHZ ||
type == REGULQ_CHPLAN_6GHZ_UNII6) {
group = &rg->chplan[FREQ_GROUP_6GHZ_UNII6];
_get_group_chplan_6g(rg, group, plan);
}
/* unii7 */
if (type == REGULQ_CHPLAN_6GHZ ||
type == REGULQ_CHPLAN_6GHZ_UNII7) {
group = &rg->chplan[FREQ_GROUP_6GHZ_UNII7];
_get_group_chplan_6g(rg, group, plan);
}
/* unii8 */
if (type == REGULQ_CHPLAN_6GHZ ||
type == REGULQ_CHPLAN_6GHZ_UNII8) {
group = &rg->chplan[FREQ_GROUP_6GHZ_UNII8];
_get_group_chplan_6g(rg, group, plan);
}
/* psc */
if (type == REGULQ_CHPLAN_FULL ||
type == REGULQ_CHPLAN_6GHZ_PSC) {
group = &rg->chplan[FREQ_GROUP_6GHZ_PSC];
_get_group_chplan_6g(rg, group, plan);
}
}
}
bool regu_valid_domain_6g(u16 domain)
{
if (domain == RSVD_DOMAIN)
return true;
if (_domain_index_6g(domain) >= MAX_RD_MAP_NUM_6GHZ)
return false;
return true;
}
bool regu_set_domain_6g(void *phl, u16 domain,
enum regulation_rsn reason)
{
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
struct rtw_regulation *rg = NULL;
void *d = NULL;
PHL_INFO("[REGU], set 6 ghz domain code = 0x%x, reason = 0x%x\n",
domain, reason);
if (!phl_info)
return false;
rg = &phl_info->regulation;
if (!rg->init)
return false;
if (!regu_valid_domain_6g(domain))
return false;
d = phl_to_drvpriv(phl_info);
_os_spinlock(d, &rg->lock, _bh, NULL);
_history_log_6g(rg, domain, reason);
if (_regulatory_domain_update_6g(rg, domain, reason))
rg->valid_6g = true;
else {
rg->valid_6g = false;
rg->invalid_cnt_6g++;
}
_os_spinunlock(d, &rg->lock, _bh, NULL);
PHL_INFO("[REGU], 6 ghz domain code valid = %d\n", rg->valid_6g);
return rg->valid_6g;
}