mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

When the 'Disabled Subchannel Bitmap' within the EHT Operation element is changed, mac80211 parse and pass it to the driver. The driver is then updated with this puncturing bitmap to optimize bandwidth usage and prevent interference from degrading performance across the entire channel. Signed-off-by: Kuan-Chung Chen <damon.chen@realtek.com> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Link: https://patch.msgid.link/20250715035259.45061-4-pkshih@realtek.com
3439 lines
89 KiB
C
3439 lines
89 KiB
C
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
|
/* Copyright(c) 2020-2022 Realtek Corporation
|
|
*/
|
|
|
|
#include "chan.h"
|
|
#include "coex.h"
|
|
#include "debug.h"
|
|
#include "fw.h"
|
|
#include "mac.h"
|
|
#include "phy.h"
|
|
#include "ps.h"
|
|
#include "sar.h"
|
|
#include "util.h"
|
|
|
|
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx1,
|
|
enum rtw89_chanctx_idx idx2);
|
|
|
|
static enum rtw89_subband rtw89_get_subband_type(enum rtw89_band band,
|
|
u8 center_chan)
|
|
{
|
|
switch (band) {
|
|
default:
|
|
case RTW89_BAND_2G:
|
|
switch (center_chan) {
|
|
default:
|
|
case 1 ... 14:
|
|
return RTW89_CH_2G;
|
|
}
|
|
case RTW89_BAND_5G:
|
|
switch (center_chan) {
|
|
default:
|
|
case 36 ... 64:
|
|
return RTW89_CH_5G_BAND_1;
|
|
case 100 ... 144:
|
|
return RTW89_CH_5G_BAND_3;
|
|
case 149 ... 177:
|
|
return RTW89_CH_5G_BAND_4;
|
|
}
|
|
case RTW89_BAND_6G:
|
|
switch (center_chan) {
|
|
default:
|
|
case 1 ... 29:
|
|
return RTW89_CH_6G_BAND_IDX0;
|
|
case 33 ... 61:
|
|
return RTW89_CH_6G_BAND_IDX1;
|
|
case 65 ... 93:
|
|
return RTW89_CH_6G_BAND_IDX2;
|
|
case 97 ... 125:
|
|
return RTW89_CH_6G_BAND_IDX3;
|
|
case 129 ... 157:
|
|
return RTW89_CH_6G_BAND_IDX4;
|
|
case 161 ... 189:
|
|
return RTW89_CH_6G_BAND_IDX5;
|
|
case 193 ... 221:
|
|
return RTW89_CH_6G_BAND_IDX6;
|
|
case 225 ... 253:
|
|
return RTW89_CH_6G_BAND_IDX7;
|
|
}
|
|
}
|
|
}
|
|
|
|
static enum rtw89_sc_offset rtw89_get_primary_chan_idx(enum rtw89_bandwidth bw,
|
|
u32 center_freq,
|
|
u32 primary_freq)
|
|
{
|
|
u8 primary_chan_idx;
|
|
u32 offset;
|
|
|
|
switch (bw) {
|
|
default:
|
|
case RTW89_CHANNEL_WIDTH_20:
|
|
primary_chan_idx = RTW89_SC_DONT_CARE;
|
|
break;
|
|
case RTW89_CHANNEL_WIDTH_40:
|
|
if (primary_freq > center_freq)
|
|
primary_chan_idx = RTW89_SC_20_UPPER;
|
|
else
|
|
primary_chan_idx = RTW89_SC_20_LOWER;
|
|
break;
|
|
case RTW89_CHANNEL_WIDTH_80:
|
|
case RTW89_CHANNEL_WIDTH_160:
|
|
if (primary_freq > center_freq) {
|
|
offset = (primary_freq - center_freq - 10) / 20;
|
|
primary_chan_idx = RTW89_SC_20_UPPER + offset * 2;
|
|
} else {
|
|
offset = (center_freq - primary_freq - 10) / 20;
|
|
primary_chan_idx = RTW89_SC_20_LOWER + offset * 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return primary_chan_idx;
|
|
}
|
|
|
|
static u8 rtw89_get_primary_sb_idx(u8 central_ch, u8 pri_ch,
|
|
enum rtw89_bandwidth bw)
|
|
{
|
|
static const u8 prisb_cal_ofst[RTW89_CHANNEL_WIDTH_ORDINARY_NUM] = {
|
|
0, 2, 6, 14, 30
|
|
};
|
|
|
|
if (bw >= RTW89_CHANNEL_WIDTH_ORDINARY_NUM)
|
|
return 0;
|
|
|
|
return (prisb_cal_ofst[bw] + pri_ch - central_ch) / 4;
|
|
}
|
|
|
|
void rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan,
|
|
enum rtw89_band band, enum rtw89_bandwidth bandwidth)
|
|
{
|
|
enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
|
|
u32 center_freq, primary_freq;
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
chan->channel = center_chan;
|
|
chan->primary_channel = primary_chan;
|
|
chan->band_type = band;
|
|
chan->band_width = bandwidth;
|
|
|
|
center_freq = ieee80211_channel_to_frequency(center_chan, nl_band);
|
|
primary_freq = ieee80211_channel_to_frequency(primary_chan, nl_band);
|
|
|
|
chan->freq = center_freq;
|
|
chan->subband_type = rtw89_get_subband_type(band, center_chan);
|
|
chan->pri_ch_idx = rtw89_get_primary_chan_idx(bandwidth, center_freq,
|
|
primary_freq);
|
|
chan->pri_sb_idx = rtw89_get_primary_sb_idx(center_chan, primary_chan,
|
|
bandwidth);
|
|
}
|
|
|
|
static void _rtw89_chan_update_punctured(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
const struct cfg80211_chan_def *chandef)
|
|
{
|
|
struct ieee80211_bss_conf *bss_conf;
|
|
|
|
if (rtwvif_link->wifi_role != RTW89_WIFI_ROLE_STATION &&
|
|
rtwvif_link->wifi_role != RTW89_WIFI_ROLE_P2P_CLIENT)
|
|
return;
|
|
|
|
rcu_read_lock();
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
if (!bss_conf->eht_support) {
|
|
rcu_read_unlock();
|
|
return;
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
rtw89_chip_h2c_punctured_cmac_tbl(rtwdev, rtwvif_link, chandef->punctured);
|
|
}
|
|
|
|
static void rtw89_chan_update_punctured(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx,
|
|
const struct cfg80211_chan_def *chandef)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link;
|
|
struct rtw89_vif *rtwvif;
|
|
unsigned int link_id;
|
|
|
|
rtw89_for_each_rtwvif(rtwdev, rtwvif) {
|
|
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) {
|
|
if (!rtwvif_link->chanctx_assigned ||
|
|
rtwvif_link->chanctx_idx != idx)
|
|
continue;
|
|
|
|
_rtw89_chan_update_punctured(rtwdev, rtwvif_link, chandef);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx,
|
|
const struct rtw89_chan *new)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_chan *chan = &hal->chanctx[idx].chan;
|
|
struct rtw89_chan_rcd *rcd = &hal->chanctx[idx].rcd;
|
|
bool band_changed;
|
|
|
|
rcd->prev_primary_channel = chan->primary_channel;
|
|
rcd->prev_band_type = chan->band_type;
|
|
band_changed = new->band_type != chan->band_type;
|
|
rcd->band_changed = band_changed;
|
|
|
|
*chan = *new;
|
|
return band_changed;
|
|
}
|
|
|
|
int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev,
|
|
int (*iterator)(const struct rtw89_chan *chan,
|
|
void *data),
|
|
void *data)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
const struct rtw89_chan *chan;
|
|
int ret;
|
|
u8 idx;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {
|
|
chan = rtw89_chan_get(rtwdev, idx);
|
|
ret = iterator(chan, data);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx,
|
|
const struct cfg80211_chan_def *chandef)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
hal->chanctx[idx].chandef = *chandef;
|
|
}
|
|
|
|
void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx,
|
|
const struct cfg80211_chan_def *chandef)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
if (!chandef) {
|
|
clear_bit(idx, hal->entity_map);
|
|
return;
|
|
}
|
|
|
|
__rtw89_config_entity_chandef(rtwdev, idx, chandef);
|
|
set_bit(idx, hal->entity_map);
|
|
}
|
|
|
|
void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
const struct cfg80211_chan_def *chandef)
|
|
{
|
|
enum rtw89_chanctx_idx idx = rtwvif_link->chanctx_idx;
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_chanctx_idx cur;
|
|
|
|
if (chandef) {
|
|
cur = atomic_cmpxchg(&hal->roc_chanctx_idx,
|
|
RTW89_CHANCTX_IDLE, idx);
|
|
if (cur != RTW89_CHANCTX_IDLE) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_TXRX,
|
|
"ROC still processing on entity %d\n", idx);
|
|
return;
|
|
}
|
|
|
|
hal->roc_chandef = *chandef;
|
|
hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link);
|
|
} else {
|
|
cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx,
|
|
RTW89_CHANCTX_IDLE);
|
|
if (cur == idx)
|
|
return;
|
|
|
|
if (cur == RTW89_CHANCTX_IDLE)
|
|
rtw89_debug(rtwdev, RTW89_DBG_TXRX,
|
|
"ROC already finished on entity %d\n", idx);
|
|
else
|
|
rtw89_debug(rtwdev, RTW89_DBG_TXRX,
|
|
"ROC is processing on entity %d\n", cur);
|
|
}
|
|
}
|
|
|
|
static void rtw89_config_default_chandef(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct cfg80211_chan_def chandef = {0};
|
|
|
|
rtw89_get_default_chandef(&chandef);
|
|
__rtw89_config_entity_chandef(rtwdev, RTW89_CHANCTX_0, &chandef);
|
|
}
|
|
|
|
void rtw89_entity_init(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
|
|
|
hal->entity_pause = false;
|
|
bitmap_zero(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
|
bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES);
|
|
atomic_set(&hal->roc_chanctx_idx, RTW89_CHANCTX_IDLE);
|
|
|
|
INIT_LIST_HEAD(&mgnt->active_list);
|
|
|
|
rtw89_config_default_chandef(rtwdev);
|
|
}
|
|
|
|
static bool rtw89_vif_is_active_role(struct rtw89_vif *rtwvif)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link;
|
|
unsigned int link_id;
|
|
|
|
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
|
|
if (rtwvif_link->chanctx_assigned)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void rtw89_entity_calculate_weight(struct rtw89_dev *rtwdev,
|
|
struct rtw89_entity_weight *w)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
const struct rtw89_chanctx_cfg *cfg;
|
|
struct rtw89_vif *rtwvif;
|
|
int idx;
|
|
|
|
w->registered_chanctxs = bitmap_weight(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
|
|
|
for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {
|
|
cfg = hal->chanctx[idx].cfg;
|
|
if (!cfg) {
|
|
/* doesn't run with chanctx ops; one channel at most */
|
|
w->active_chanctxs = 1;
|
|
break;
|
|
}
|
|
|
|
if (cfg->ref_count > 0)
|
|
w->active_chanctxs++;
|
|
}
|
|
|
|
rtw89_for_each_rtwvif(rtwdev, rtwvif) {
|
|
if (rtw89_vif_is_active_role(rtwvif))
|
|
w->active_roles++;
|
|
}
|
|
}
|
|
|
|
static void rtw89_normalize_link_chanctx(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link)
|
|
{
|
|
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
|
|
struct rtw89_vif_link *cur;
|
|
|
|
if (unlikely(!rtwvif_link->chanctx_assigned))
|
|
return;
|
|
|
|
cur = rtw89_vif_get_link_inst(rtwvif, 0);
|
|
if (!cur || !cur->chanctx_assigned)
|
|
return;
|
|
|
|
if (cur == rtwvif_link)
|
|
return;
|
|
|
|
rtw89_swap_chanctx(rtwdev, rtwvif_link->chanctx_idx, cur->chanctx_idx);
|
|
}
|
|
|
|
const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
|
|
const char *caller_message,
|
|
u8 link_index)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
|
enum rtw89_chanctx_idx chanctx_idx;
|
|
enum rtw89_chanctx_idx roc_idx;
|
|
enum rtw89_entity_mode mode;
|
|
u8 role_index;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) {
|
|
WARN(1, "link index %u is invalid (max link inst num: %d)\n",
|
|
link_index, __RTW89_MLD_MAX_LINK_NUM);
|
|
goto dflt;
|
|
}
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
case RTW89_ENTITY_MODE_SCC_OR_SMLD:
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
role_index = 0;
|
|
break;
|
|
case RTW89_ENTITY_MODE_MCC_PREPARE:
|
|
role_index = 1;
|
|
break;
|
|
default:
|
|
WARN(1, "Invalid ent mode: %d\n", mode);
|
|
goto dflt;
|
|
}
|
|
|
|
chanctx_idx = mgnt->chanctx_tbl[role_index][link_index];
|
|
if (chanctx_idx == RTW89_CHANCTX_IDLE)
|
|
goto dflt;
|
|
|
|
roc_idx = atomic_read(&hal->roc_chanctx_idx);
|
|
if (roc_idx != RTW89_CHANCTX_IDLE) {
|
|
/* ROC is ongoing (given ROC runs on @hal->roc_link_index).
|
|
* If @link_index is the same, get the ongoing ROC chanctx.
|
|
*/
|
|
if (link_index == hal->roc_link_index)
|
|
chanctx_idx = roc_idx;
|
|
}
|
|
|
|
return rtw89_chan_get(rtwdev, chanctx_idx);
|
|
|
|
dflt:
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"%s (%s): prefetch NULL on link index %u\n",
|
|
__func__, caller_message ?: "", link_index);
|
|
|
|
return rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
|
|
}
|
|
EXPORT_SYMBOL(__rtw89_mgnt_chan_get);
|
|
|
|
static enum rtw89_mlo_dbcc_mode
|
|
rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
|
|
{
|
|
if (rtwdev->chip->chip_gen != RTW89_CHIP_BE)
|
|
return MLO_DBCC_NOT_SUPPORT;
|
|
|
|
switch (active_hws) {
|
|
case BIT(0):
|
|
return MLO_2_PLUS_0_1RF;
|
|
case BIT(1):
|
|
return MLO_0_PLUS_2_1RF;
|
|
case BIT(0) | BIT(1):
|
|
default:
|
|
return MLO_1_PLUS_1_1RF;
|
|
}
|
|
}
|
|
|
|
static
|
|
void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
|
|
{
|
|
enum rtw89_mlo_dbcc_mode mode;
|
|
|
|
mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws);
|
|
rtwdev->mlo_dbcc_mode = mode;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_STATE, "recalc mlo dbcc mode to %d\n", mode);
|
|
}
|
|
|
|
static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
|
struct rtw89_vif_link *link;
|
|
struct rtw89_vif *role;
|
|
u8 active_hws = 0;
|
|
u8 pos = 0;
|
|
int i, j;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++)
|
|
mgnt->active_roles[i] = NULL;
|
|
|
|
for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++) {
|
|
for (j = 0; j < __RTW89_MLD_MAX_LINK_NUM; j++)
|
|
mgnt->chanctx_tbl[i][j] = RTW89_CHANCTX_IDLE;
|
|
}
|
|
|
|
/* To be consistent with legacy behavior, expect the first active role
|
|
* which uses RTW89_CHANCTX_0 to put at position 0, and make its first
|
|
* link instance take RTW89_CHANCTX_0. (normalizing)
|
|
*/
|
|
list_for_each_entry(role, &mgnt->active_list, mgnt_entry) {
|
|
for (i = 0; i < role->links_inst_valid_num; i++) {
|
|
link = rtw89_vif_get_link_inst(role, i);
|
|
if (!link || !link->chanctx_assigned)
|
|
continue;
|
|
|
|
if (link->chanctx_idx == RTW89_CHANCTX_0) {
|
|
rtw89_normalize_link_chanctx(rtwdev, link);
|
|
|
|
list_del(&role->mgnt_entry);
|
|
list_add(&role->mgnt_entry, &mgnt->active_list);
|
|
goto fill;
|
|
}
|
|
}
|
|
}
|
|
|
|
fill:
|
|
list_for_each_entry(role, &mgnt->active_list, mgnt_entry) {
|
|
if (unlikely(pos >= RTW89_MAX_INTERFACE_NUM)) {
|
|
rtw89_warn(rtwdev,
|
|
"%s: active roles are over max iface num\n",
|
|
__func__);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < role->links_inst_valid_num; i++) {
|
|
link = rtw89_vif_get_link_inst(role, i);
|
|
if (!link || !link->chanctx_assigned)
|
|
continue;
|
|
|
|
mgnt->chanctx_tbl[pos][i] = link->chanctx_idx;
|
|
active_hws |= BIT(i);
|
|
}
|
|
|
|
mgnt->active_roles[pos++] = role;
|
|
}
|
|
|
|
rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws);
|
|
}
|
|
|
|
enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
|
|
{
|
|
DECLARE_BITMAP(recalc_map, NUM_OF_RTW89_CHANCTX) = {};
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
const struct cfg80211_chan_def *chandef;
|
|
struct rtw89_entity_weight w = {};
|
|
enum rtw89_entity_mode mode;
|
|
struct rtw89_chan chan;
|
|
u8 idx;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
|
|
|
rtw89_entity_calculate_weight(rtwdev, &w);
|
|
switch (w.active_chanctxs) {
|
|
default:
|
|
rtw89_warn(rtwdev, "unknown ent chanctxs weight: %d\n",
|
|
w.active_chanctxs);
|
|
bitmap_zero(recalc_map, NUM_OF_RTW89_CHANCTX);
|
|
fallthrough;
|
|
case 0:
|
|
if (!w.registered_chanctxs)
|
|
rtw89_config_default_chandef(rtwdev);
|
|
set_bit(RTW89_CHANCTX_0, recalc_map);
|
|
fallthrough;
|
|
case 1:
|
|
mode = RTW89_ENTITY_MODE_SCC_OR_SMLD;
|
|
break;
|
|
case 2 ... NUM_OF_RTW89_CHANCTX:
|
|
if (w.active_roles == 1) {
|
|
mode = RTW89_ENTITY_MODE_SCC_OR_SMLD;
|
|
break;
|
|
}
|
|
|
|
if (w.active_roles != NUM_OF_RTW89_MCC_ROLES) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"unhandled ent: %d chanctxs %d roles\n",
|
|
w.active_chanctxs, w.active_roles);
|
|
return RTW89_ENTITY_MODE_UNHANDLED;
|
|
}
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
if (mode == RTW89_ENTITY_MODE_MCC)
|
|
break;
|
|
|
|
mode = RTW89_ENTITY_MODE_MCC_PREPARE;
|
|
break;
|
|
}
|
|
|
|
for_each_set_bit(idx, recalc_map, NUM_OF_RTW89_CHANCTX) {
|
|
chandef = rtw89_chandef_get(rtwdev, idx);
|
|
rtw89_get_channel_params(chandef, &chan);
|
|
if (chan.channel == 0) {
|
|
WARN(1, "Invalid channel on chanctx %d\n", idx);
|
|
return RTW89_ENTITY_MODE_INVALID;
|
|
}
|
|
|
|
rtw89_assign_entity_chan(rtwdev, idx, &chan);
|
|
}
|
|
|
|
rtw89_entity_recalc_mgnt_roles(rtwdev);
|
|
|
|
if (hal->entity_pause)
|
|
return rtw89_get_entity_mode(rtwdev);
|
|
|
|
rtw89_set_entity_mode(rtwdev, mode);
|
|
return mode;
|
|
}
|
|
|
|
static void rtw89_chanctx_notify(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_state state)
|
|
{
|
|
const struct rtw89_chip_info *chip = rtwdev->chip;
|
|
const struct rtw89_chanctx_listener *listener = chip->chanctx_listener;
|
|
int i;
|
|
|
|
if (!listener)
|
|
return;
|
|
|
|
for (i = 0; i < NUM_OF_RTW89_CHANCTX_CALLBACKS; i++) {
|
|
if (!listener->callbacks[i])
|
|
continue;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"chanctx notify listener: cb %d, state %d\n",
|
|
i, state);
|
|
|
|
listener->callbacks[i](rtwdev, state);
|
|
}
|
|
}
|
|
|
|
static bool rtw89_concurrent_via_mrc(struct rtw89_dev *rtwdev)
|
|
{
|
|
enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen;
|
|
|
|
return chip_gen == RTW89_CHIP_BE;
|
|
}
|
|
|
|
/* This function centrally manages how MCC roles are sorted and iterated.
|
|
* And, it guarantees that ordered_idx is less than NUM_OF_RTW89_MCC_ROLES.
|
|
* So, if data needs to pass an array for ordered_idx, the array can declare
|
|
* with NUM_OF_RTW89_MCC_ROLES. Besides, the entire iteration will stop
|
|
* immediately as long as iterator returns a non-zero value.
|
|
*/
|
|
static
|
|
int rtw89_iterate_mcc_roles(struct rtw89_dev *rtwdev,
|
|
int (*iterator)(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data),
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role * const roles[] = {
|
|
&mcc->role_ref,
|
|
&mcc->role_aux,
|
|
};
|
|
unsigned int idx;
|
|
int ret;
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(roles) != NUM_OF_RTW89_MCC_ROLES);
|
|
|
|
for (idx = 0; idx < NUM_OF_RTW89_MCC_ROLES; idx++) {
|
|
ret = iterator(rtwdev, roles[idx], idx, data);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *role, u64 tsf)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link = role->rtwvif_link;
|
|
u32 bcn_intvl_us = ieee80211_tu_to_usec(role->beacon_interval);
|
|
u64 sync_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);
|
|
u32 remainder;
|
|
|
|
if (role->is_go) {
|
|
sync_tsf = 0;
|
|
} else if (tsf < sync_tsf) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC get tbtt ofst: tsf might not update yet\n");
|
|
sync_tsf = 0;
|
|
}
|
|
|
|
div_u64_rem(tsf - sync_tsf, bcn_intvl_us, &remainder);
|
|
|
|
return remainder;
|
|
}
|
|
|
|
static int __mcc_fw_req_tsf(struct rtw89_dev *rtwdev, u64 *tsf_ref, u64 *tsf_aux)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mac_mcc_tsf_rpt rpt = {};
|
|
struct rtw89_fw_mcc_tsf_req req = {};
|
|
int ret;
|
|
|
|
req.group = mcc->group;
|
|
req.macid_x = ref->rtwvif_link->mac_id;
|
|
req.macid_y = aux->rtwvif_link->mac_id;
|
|
ret = rtw89_fw_h2c_mcc_req_tsf(rtwdev, &req, &rpt);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to request tsf: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
*tsf_ref = (u64)rpt.tsf_x_high << 32 | rpt.tsf_x_low;
|
|
*tsf_aux = (u64)rpt.tsf_y_high << 32 | rpt.tsf_y_low;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __mrc_fw_req_tsf(struct rtw89_dev *rtwdev, u64 *tsf_ref, u64 *tsf_aux)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_fw_mrc_req_tsf_arg arg = {};
|
|
struct rtw89_mac_mrc_tsf_rpt rpt = {};
|
|
int ret;
|
|
|
|
BUILD_BUG_ON(RTW89_MAC_MRC_MAX_REQ_TSF_NUM < NUM_OF_RTW89_MCC_ROLES);
|
|
|
|
arg.num = 2;
|
|
arg.infos[0].band = ref->rtwvif_link->mac_idx;
|
|
arg.infos[0].port = ref->rtwvif_link->port;
|
|
arg.infos[1].band = aux->rtwvif_link->mac_idx;
|
|
arg.infos[1].port = aux->rtwvif_link->port;
|
|
|
|
ret = rtw89_fw_h2c_mrc_req_tsf(rtwdev, &arg, &rpt);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to request tsf: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
*tsf_ref = rpt.tsfs[0];
|
|
*tsf_aux = rpt.tsfs[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 rtw89_mcc_get_bcn_ofst(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval);
|
|
u32 tbtt_ofst_ref, tbtt_ofst_aux;
|
|
u64 tsf_ref, tsf_aux;
|
|
int ret;
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_req_tsf(rtwdev, &tsf_ref, &tsf_aux);
|
|
else
|
|
ret = __mcc_fw_req_tsf(rtwdev, &tsf_ref, &tsf_aux);
|
|
|
|
if (ret)
|
|
return RTW89_MCC_DFLT_BCN_OFST_TIME;
|
|
|
|
tbtt_ofst_ref = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf_ref);
|
|
tbtt_ofst_aux = rtw89_mcc_get_tbtt_ofst(rtwdev, aux, tsf_aux);
|
|
|
|
while (tbtt_ofst_ref < tbtt_ofst_aux)
|
|
tbtt_ofst_ref += bcn_intvl_ref_us;
|
|
|
|
return (tbtt_ofst_ref - tbtt_ofst_aux) / 1024;
|
|
}
|
|
|
|
static
|
|
void rtw89_mcc_role_fw_macid_bitmap_set_bit(struct rtw89_mcc_role *mcc_role,
|
|
unsigned int bit)
|
|
{
|
|
unsigned int idx = bit / 8;
|
|
unsigned int pos = bit % 8;
|
|
|
|
if (idx >= ARRAY_SIZE(mcc_role->macid_bitmap))
|
|
return;
|
|
|
|
mcc_role->macid_bitmap[idx] |= BIT(pos);
|
|
}
|
|
|
|
static
|
|
u32 rtw89_mcc_role_fw_macid_bitmap_to_u32(struct rtw89_mcc_role *mcc_role)
|
|
{
|
|
unsigned int macid;
|
|
unsigned int i, j;
|
|
u32 bitmap = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mcc_role->macid_bitmap); i++) {
|
|
for (j = 0; j < 8; j++) {
|
|
macid = i * 8 + j;
|
|
if (macid >= 32)
|
|
goto out;
|
|
|
|
if (mcc_role->macid_bitmap[i] & BIT(j))
|
|
bitmap |= BIT(macid);
|
|
}
|
|
}
|
|
|
|
out:
|
|
return bitmap;
|
|
}
|
|
|
|
static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta)
|
|
{
|
|
struct rtw89_mcc_role *mcc_role = data;
|
|
struct rtw89_vif *target = mcc_role->rtwvif_link->rtwvif;
|
|
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
|
|
struct rtw89_vif *rtwvif = rtwsta->rtwvif;
|
|
u8 macid;
|
|
|
|
if (rtwvif != target)
|
|
return;
|
|
|
|
macid = rtw89_sta_get_main_macid(rtwsta);
|
|
rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, macid);
|
|
}
|
|
|
|
static void rtw89_mcc_fill_role_macid_bitmap(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link = mcc_role->rtwvif_link;
|
|
|
|
rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, rtwvif_link->mac_id);
|
|
ieee80211_iterate_stations_atomic(rtwdev->hw,
|
|
rtw89_mcc_role_macid_sta_iter,
|
|
mcc_role);
|
|
}
|
|
|
|
static void rtw89_mcc_fill_role_policy(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role)
|
|
{
|
|
struct rtw89_mcc_policy *policy = &mcc_role->policy;
|
|
|
|
policy->c2h_rpt = RTW89_FW_MCC_C2H_RPT_ALL;
|
|
policy->tx_null_early = RTW89_MCC_DFLT_TX_NULL_EARLY;
|
|
policy->in_curr_ch = false;
|
|
policy->dis_sw_retry = true;
|
|
policy->sw_retry_count = false;
|
|
|
|
if (mcc_role->is_go)
|
|
policy->dis_tx_null = true;
|
|
else
|
|
policy->dis_tx_null = false;
|
|
}
|
|
|
|
static void rtw89_mcc_fill_role_limit(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link = mcc_role->rtwvif_link;
|
|
struct ieee80211_p2p_noa_desc *noa_desc;
|
|
struct ieee80211_bss_conf *bss_conf;
|
|
u32 bcn_intvl_us = ieee80211_tu_to_usec(mcc_role->beacon_interval);
|
|
u32 max_toa_us, max_tob_us, max_dur_us;
|
|
u32 start_time, interval, duration;
|
|
u64 tsf, tsf_lmt;
|
|
int ret;
|
|
int i;
|
|
|
|
if (!mcc_role->is_gc)
|
|
return;
|
|
|
|
rtw89_p2p_noa_once_recalc(rtwvif_link);
|
|
|
|
rcu_read_lock();
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
|
|
/* find the first periodic NoA */
|
|
for (i = 0; i < RTW89_P2P_MAX_NOA_NUM; i++) {
|
|
noa_desc = &bss_conf->p2p_noa_attr.desc[i];
|
|
if (noa_desc->count == 255)
|
|
goto fill;
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
return;
|
|
|
|
fill:
|
|
start_time = le32_to_cpu(noa_desc->start_time);
|
|
interval = le32_to_cpu(noa_desc->interval);
|
|
duration = le32_to_cpu(noa_desc->duration);
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (interval != bcn_intvl_us) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC role limit: mismatch interval: %d vs. %d\n",
|
|
interval, bcn_intvl_us);
|
|
return;
|
|
}
|
|
|
|
ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf);
|
|
if (ret) {
|
|
rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret);
|
|
return;
|
|
}
|
|
|
|
tsf_lmt = (tsf & GENMASK_ULL(63, 32)) | start_time;
|
|
if (tsf_lmt < tsf)
|
|
tsf_lmt += roundup_u64(tsf - tsf_lmt, interval);
|
|
|
|
max_toa_us = rtw89_mcc_get_tbtt_ofst(rtwdev, mcc_role, tsf_lmt);
|
|
max_dur_us = interval - duration;
|
|
max_tob_us = max_dur_us - max_toa_us;
|
|
|
|
if (!max_toa_us || !max_tob_us) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC role limit: hit boundary\n");
|
|
return;
|
|
}
|
|
|
|
if (max_dur_us < max_toa_us) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC role limit: insufficient duration\n");
|
|
return;
|
|
}
|
|
|
|
mcc_role->limit.max_toa = max_toa_us / 1024;
|
|
mcc_role->limit.max_tob = max_tob_us / 1024;
|
|
mcc_role->limit.max_dur = mcc_role->limit.max_toa + mcc_role->limit.max_tob;
|
|
mcc_role->limit.enable = true;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC role limit: max_toa %d, max_tob %d, max_dur %d\n",
|
|
mcc_role->limit.max_toa, mcc_role->limit.max_tob,
|
|
mcc_role->limit.max_dur);
|
|
}
|
|
|
|
static int rtw89_mcc_fill_role(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
struct rtw89_mcc_role *role)
|
|
{
|
|
struct ieee80211_bss_conf *bss_conf;
|
|
const struct rtw89_chan *chan;
|
|
|
|
memset(role, 0, sizeof(*role));
|
|
role->rtwvif_link = rtwvif_link;
|
|
|
|
rcu_read_lock();
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
role->beacon_interval = bss_conf->beacon_int;
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (!role->beacon_interval) {
|
|
rtw89_warn(rtwdev,
|
|
"cannot handle MCC role without beacon interval\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
role->duration = role->beacon_interval / 2;
|
|
|
|
chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx);
|
|
role->is_2ghz = chan->band_type == RTW89_BAND_2G;
|
|
role->is_go = rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_GO;
|
|
role->is_gc = rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT;
|
|
|
|
rtw89_mcc_fill_role_macid_bitmap(rtwdev, role);
|
|
rtw89_mcc_fill_role_policy(rtwdev, role);
|
|
rtw89_mcc_fill_role_limit(rtwdev, role);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC role: bcn_intvl %d, is_2ghz %d, is_go %d, is_gc %d\n",
|
|
role->beacon_interval, role->is_2ghz, role->is_go, role->is_gc);
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_fill_bt_role(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role;
|
|
|
|
memset(bt_role, 0, sizeof(*bt_role));
|
|
bt_role->duration = rtw89_coex_query_bt_req_len(rtwdev, RTW89_PHY_0);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC bt role: dur %d\n",
|
|
bt_role->duration);
|
|
}
|
|
|
|
struct rtw89_mcc_fill_role_selector {
|
|
struct rtw89_vif_link *bind_vif[NUM_OF_RTW89_CHANCTX];
|
|
};
|
|
|
|
static_assert((u8)NUM_OF_RTW89_CHANCTX >= NUM_OF_RTW89_MCC_ROLES);
|
|
static_assert(RTW89_MAX_INTERFACE_NUM >= NUM_OF_RTW89_MCC_ROLES);
|
|
|
|
static int rtw89_mcc_fill_role_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_fill_role_selector *sel = data;
|
|
struct rtw89_vif_link *role_vif = sel->bind_vif[ordered_idx];
|
|
int ret;
|
|
|
|
if (!role_vif) {
|
|
rtw89_warn(rtwdev, "cannot handle MCC without role[%d]\n",
|
|
ordered_idx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC fill role[%d] with vif <macid %d>\n",
|
|
ordered_idx, role_vif->mac_id);
|
|
|
|
ret = rtw89_mcc_fill_role(rtwdev, role_vif, mcc_role);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
|
struct rtw89_mcc_fill_role_selector sel = {};
|
|
struct rtw89_vif_link *rtwvif_link;
|
|
struct rtw89_vif *rtwvif;
|
|
int ret;
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_OF_RTW89_MCC_ROLES; i++) {
|
|
rtwvif = mgnt->active_roles[i];
|
|
if (!rtwvif)
|
|
break;
|
|
|
|
rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0);
|
|
if (unlikely(!rtwvif_link)) {
|
|
rtw89_err(rtwdev, "mcc fill roles: find no link on HW-0\n");
|
|
continue;
|
|
}
|
|
|
|
sel.bind_vif[i] = rtwvif_link;
|
|
rtw89_p2p_disable_all_noa(rtwdev, rtwvif_link, NULL);
|
|
}
|
|
|
|
ret = rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_fill_role_iterator, &sel);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rtw89_mcc_fill_bt_role(rtwdev);
|
|
return 0;
|
|
}
|
|
|
|
static bool rtw89_mcc_can_courtesy(const struct rtw89_mcc_role *provider,
|
|
const struct rtw89_mcc_role *receiver)
|
|
{
|
|
if (provider->is_go || receiver->is_gc)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
|
|
const struct rtw89_mcc_pattern *new)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_pattern *pattern = &config->pattern;
|
|
struct rtw89_mcc_courtesy_cfg *crtz;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC assign pattern: ref {%d | %d}, aux {%d | %d}\n",
|
|
new->tob_ref, new->toa_ref, new->tob_aux, new->toa_aux);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC pattern plan: %d\n", new->plan);
|
|
|
|
*pattern = *new;
|
|
memset(&pattern->courtesy, 0, sizeof(pattern->courtesy));
|
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, aux) && aux->is_gc)
|
|
aux->ignore_bcn = true;
|
|
else
|
|
aux->ignore_bcn = false;
|
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) {
|
|
crtz = &pattern->courtesy.ref;
|
|
ref->crtz = crtz;
|
|
|
|
crtz->macid_tgt = aux->rtwvif_link->mac_id;
|
|
crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC courtesy ref: tgt %d, slot %d\n",
|
|
crtz->macid_tgt, crtz->slot_num);
|
|
} else {
|
|
ref->crtz = NULL;
|
|
}
|
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, ref) && ref->is_gc)
|
|
ref->ignore_bcn = true;
|
|
else
|
|
ref->ignore_bcn = false;
|
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) {
|
|
crtz = &pattern->courtesy.aux;
|
|
aux->crtz = crtz;
|
|
|
|
crtz->macid_tgt = ref->rtwvif_link->mac_id;
|
|
crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC courtesy aux: tgt %d, slot %d\n",
|
|
crtz->macid_tgt, crtz->slot_num);
|
|
} else {
|
|
aux->crtz = NULL;
|
|
}
|
|
}
|
|
|
|
/* The follow-up roughly shows the relationship between the parameters
|
|
* for pattern calculation.
|
|
*
|
|
* |< duration ref >| (if mid bt) |< duration aux >|
|
|
* |< tob ref >|< toa ref >| ... |< tob aux >|< toa aux >|
|
|
* V V
|
|
* tbtt ref tbtt aux
|
|
* |< beacon offset >|
|
|
*
|
|
* In loose pattern calculation, we only ensure at least tob_ref and
|
|
* toa_ref have positive results. If tob_aux or toa_aux is negative
|
|
* unfortunately, FW will be notified to handle it with courtesy
|
|
* mechanism.
|
|
*/
|
|
static void __rtw89_mcc_calc_pattern_loose(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_pattern *ptrn,
|
|
bool hdl_bt)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 mcc_intvl = config->mcc_interval;
|
|
u16 bcn_ofst = config->beacon_offset;
|
|
u16 bt_dur_in_mid = 0;
|
|
u16 max_bcn_ofst;
|
|
s16 upper, lower;
|
|
u16 res;
|
|
|
|
*ptrn = (typeof(*ptrn)){
|
|
.plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT,
|
|
};
|
|
|
|
if (!hdl_bt)
|
|
goto calc;
|
|
|
|
max_bcn_ofst = ref->duration + aux->duration;
|
|
if (ref->limit.enable)
|
|
max_bcn_ofst = min_t(u16, max_bcn_ofst,
|
|
ref->limit.max_toa + aux->duration);
|
|
else if (aux->limit.enable)
|
|
max_bcn_ofst = min_t(u16, max_bcn_ofst,
|
|
ref->duration + aux->limit.max_tob);
|
|
|
|
if (bcn_ofst > max_bcn_ofst && bcn_ofst >= mcc->bt_role.duration) {
|
|
bt_dur_in_mid = mcc->bt_role.duration;
|
|
ptrn->plan = RTW89_MCC_PLAN_MID_BT;
|
|
}
|
|
|
|
calc:
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_ls: plan %d, bcn_ofst %d\n",
|
|
ptrn->plan, bcn_ofst);
|
|
|
|
res = bcn_ofst - bt_dur_in_mid;
|
|
upper = min_t(s16, ref->duration, res);
|
|
lower = max_t(s16, 0, ref->duration - (mcc_intvl - bcn_ofst));
|
|
|
|
if (ref->limit.enable) {
|
|
upper = min_t(s16, upper, ref->limit.max_toa);
|
|
lower = max_t(s16, lower, ref->duration - ref->limit.max_tob);
|
|
} else if (aux->limit.enable) {
|
|
upper = min_t(s16, upper,
|
|
res - (aux->duration - aux->limit.max_toa));
|
|
lower = max_t(s16, lower, res - aux->limit.max_tob);
|
|
}
|
|
|
|
if (lower < upper)
|
|
ptrn->toa_ref = (upper + lower) / 2;
|
|
else
|
|
ptrn->toa_ref = lower;
|
|
|
|
ptrn->tob_ref = ref->duration - ptrn->toa_ref;
|
|
ptrn->tob_aux = res - ptrn->toa_ref;
|
|
ptrn->toa_aux = aux->duration - ptrn->tob_aux;
|
|
}
|
|
|
|
/* In strict pattern calculation, we consider timing that might need
|
|
* for HW stuffs, i.e. min_tob and min_toa.
|
|
*/
|
|
static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_pattern *ptrn)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 min_tob = RTW89_MCC_EARLY_RX_BCN_TIME + RTW89_MCC_SWITCH_CH_TIME;
|
|
u16 min_toa = RTW89_MCC_MIN_RX_BCN_TIME;
|
|
u16 bcn_ofst = config->beacon_offset;
|
|
s16 upper_toa_ref, lower_toa_ref;
|
|
s16 upper_tob_aux, lower_tob_aux;
|
|
u16 bt_dur_in_mid;
|
|
s16 res;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: plan %d, bcn_ofst %d\n",
|
|
ptrn->plan, bcn_ofst);
|
|
|
|
if (ptrn->plan == RTW89_MCC_PLAN_MID_BT)
|
|
bt_dur_in_mid = mcc->bt_role.duration;
|
|
else
|
|
bt_dur_in_mid = 0;
|
|
|
|
if (ref->duration < min_tob + min_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: not meet ref dur cond\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (aux->duration < min_tob + min_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: not meet aux dur cond\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
res = bcn_ofst - min_toa - min_tob - bt_dur_in_mid;
|
|
if (res < 0) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: not meet bcn_ofst cond\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
upper_toa_ref = min_t(s16, min_toa + res, ref->duration - min_tob);
|
|
lower_toa_ref = min_toa;
|
|
upper_tob_aux = min_t(s16, min_tob + res, aux->duration - min_toa);
|
|
lower_tob_aux = min_tob;
|
|
|
|
if (ref->limit.enable) {
|
|
if (min_tob > ref->limit.max_tob || min_toa > ref->limit.max_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: conflict ref limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
upper_toa_ref = min_t(s16, upper_toa_ref, ref->limit.max_toa);
|
|
lower_toa_ref = max_t(s16, lower_toa_ref,
|
|
ref->duration - ref->limit.max_tob);
|
|
} else if (aux->limit.enable) {
|
|
if (min_tob > aux->limit.max_tob || min_toa > aux->limit.max_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: conflict aux limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
upper_tob_aux = min_t(s16, upper_tob_aux, aux->limit.max_tob);
|
|
lower_tob_aux = max_t(s16, lower_tob_aux,
|
|
aux->duration - aux->limit.max_toa);
|
|
}
|
|
|
|
upper_toa_ref = min_t(s16, upper_toa_ref,
|
|
bcn_ofst - bt_dur_in_mid - lower_tob_aux);
|
|
lower_toa_ref = max_t(s16, lower_toa_ref,
|
|
bcn_ofst - bt_dur_in_mid - upper_tob_aux);
|
|
if (lower_toa_ref > upper_toa_ref) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st: conflict boundary\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ptrn->toa_ref = (upper_toa_ref + lower_toa_ref) / 2;
|
|
ptrn->tob_ref = ref->duration - ptrn->toa_ref;
|
|
ptrn->tob_aux = bcn_ofst - ptrn->toa_ref - bt_dur_in_mid;
|
|
ptrn->toa_aux = aux->duration - ptrn->tob_aux;
|
|
return 0;
|
|
}
|
|
|
|
static void __rtw89_mcc_fill_ptrn_anchor_ref(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_pattern *ptrn,
|
|
bool small_bcn_ofst)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 bcn_ofst = config->beacon_offset;
|
|
u16 ref_tob;
|
|
u16 ref_toa;
|
|
|
|
if (ref->limit.enable) {
|
|
ref_tob = ref->limit.max_tob;
|
|
ref_toa = ref->limit.max_toa;
|
|
} else {
|
|
ref_tob = ref->duration / 2;
|
|
ref_toa = ref->duration / 2;
|
|
}
|
|
|
|
if (small_bcn_ofst) {
|
|
ptrn->toa_ref = ref_toa;
|
|
ptrn->tob_ref = ref->duration - ptrn->toa_ref;
|
|
} else {
|
|
ptrn->tob_ref = ref_tob;
|
|
ptrn->toa_ref = ref->duration - ptrn->tob_ref;
|
|
}
|
|
|
|
ptrn->tob_aux = bcn_ofst - ptrn->toa_ref;
|
|
ptrn->toa_aux = aux->duration - ptrn->tob_aux;
|
|
}
|
|
|
|
static void __rtw89_mcc_fill_ptrn_anchor_aux(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_pattern *ptrn,
|
|
bool small_bcn_ofst)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 bcn_ofst = config->beacon_offset;
|
|
u16 aux_tob;
|
|
u16 aux_toa;
|
|
|
|
if (aux->limit.enable) {
|
|
aux_tob = aux->limit.max_tob;
|
|
aux_toa = aux->limit.max_toa;
|
|
} else {
|
|
aux_tob = aux->duration / 2;
|
|
aux_toa = aux->duration / 2;
|
|
}
|
|
|
|
if (small_bcn_ofst) {
|
|
ptrn->tob_aux = aux_tob;
|
|
ptrn->toa_aux = aux->duration - ptrn->tob_aux;
|
|
} else {
|
|
ptrn->toa_aux = aux_toa;
|
|
ptrn->tob_aux = aux->duration - ptrn->toa_aux;
|
|
}
|
|
|
|
ptrn->toa_ref = bcn_ofst - ptrn->tob_aux;
|
|
ptrn->tob_ref = ref->duration - ptrn->toa_ref;
|
|
}
|
|
|
|
static int __rtw89_mcc_calc_pattern_anchor(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_pattern *ptrn,
|
|
bool hdl_bt)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 mcc_intvl = config->mcc_interval;
|
|
u16 bcn_ofst = config->beacon_offset;
|
|
bool small_bcn_ofst;
|
|
|
|
if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME)
|
|
small_bcn_ofst = true;
|
|
else if (bcn_ofst < aux->duration - aux->limit.max_toa)
|
|
small_bcn_ofst = true;
|
|
else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME)
|
|
small_bcn_ofst = false;
|
|
else
|
|
return -EPERM;
|
|
|
|
*ptrn = (typeof(*ptrn)){
|
|
.plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT,
|
|
};
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_ac: plan %d, bcn_ofst %d\n",
|
|
ptrn->plan, bcn_ofst);
|
|
|
|
if (ref->is_go || ref->is_gc)
|
|
__rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst);
|
|
else if (aux->is_go || aux->is_gc)
|
|
__rtw89_mcc_fill_ptrn_anchor_aux(rtwdev, ptrn, small_bcn_ofst);
|
|
else
|
|
__rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
bool sel_plan[NUM_OF_RTW89_MCC_PLAN] = {};
|
|
struct rtw89_mcc_pattern ptrn;
|
|
int ret;
|
|
int i;
|
|
|
|
if (ref->limit.enable && aux->limit.enable) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn: not support dual limited roles\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ref->limit.enable &&
|
|
ref->duration > ref->limit.max_tob + ref->limit.max_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn: not fit ref limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (aux->limit.enable &&
|
|
aux->duration > aux->limit.max_tob + aux->limit.max_toa) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn: not fit aux limit\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (hdl_bt) {
|
|
sel_plan[RTW89_MCC_PLAN_TAIL_BT] = true;
|
|
sel_plan[RTW89_MCC_PLAN_MID_BT] = true;
|
|
} else {
|
|
sel_plan[RTW89_MCC_PLAN_NO_BT] = true;
|
|
}
|
|
|
|
for (i = 0; i < NUM_OF_RTW89_MCC_PLAN; i++) {
|
|
if (!sel_plan[i])
|
|
continue;
|
|
|
|
ptrn = (typeof(ptrn)){
|
|
.plan = i,
|
|
};
|
|
|
|
ret = __rtw89_mcc_calc_pattern_strict(rtwdev, &ptrn);
|
|
if (ret)
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC calc ptrn_st with plan %d: fail\n", i);
|
|
else
|
|
goto done;
|
|
}
|
|
|
|
ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt);
|
|
if (!ret)
|
|
goto done;
|
|
|
|
__rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt);
|
|
|
|
done:
|
|
rtw89_mcc_assign_pattern(rtwdev, &ptrn);
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_set_default_pattern(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_pattern tmp = {};
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC use default pattern unexpectedly\n");
|
|
|
|
tmp.plan = RTW89_MCC_PLAN_NO_BT;
|
|
tmp.tob_ref = ref->duration / 2;
|
|
tmp.toa_ref = ref->duration - tmp.tob_ref;
|
|
tmp.tob_aux = aux->duration / 2;
|
|
tmp.toa_aux = aux->duration - tmp.tob_aux;
|
|
|
|
rtw89_mcc_assign_pattern(rtwdev, &tmp);
|
|
}
|
|
|
|
static void rtw89_mcc_set_duration_go_sta(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *role_go,
|
|
struct rtw89_mcc_role *role_sta)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 mcc_intvl = config->mcc_interval;
|
|
u16 dur_go, dur_sta;
|
|
|
|
dur_go = clamp_t(u16, role_go->duration, RTW89_MCC_MIN_GO_DURATION,
|
|
mcc_intvl - RTW89_MCC_MIN_STA_DURATION);
|
|
if (role_go->limit.enable)
|
|
dur_go = min(dur_go, role_go->limit.max_dur);
|
|
dur_sta = mcc_intvl - dur_go;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC set dur: (go, sta) {%d, %d} -> {%d, %d}\n",
|
|
role_go->duration, role_sta->duration, dur_go, dur_sta);
|
|
|
|
role_go->duration = dur_go;
|
|
role_sta->duration = dur_sta;
|
|
}
|
|
|
|
static void rtw89_mcc_set_duration_gc_sta(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 mcc_intvl = config->mcc_interval;
|
|
u16 dur_ref, dur_aux;
|
|
|
|
if (ref->duration < RTW89_MCC_MIN_STA_DURATION) {
|
|
dur_ref = RTW89_MCC_MIN_STA_DURATION;
|
|
dur_aux = mcc_intvl - dur_ref;
|
|
} else if (aux->duration < RTW89_MCC_MIN_STA_DURATION) {
|
|
dur_aux = RTW89_MCC_MIN_STA_DURATION;
|
|
dur_ref = mcc_intvl - dur_aux;
|
|
} else {
|
|
dur_ref = ref->duration;
|
|
dur_aux = mcc_intvl - dur_ref;
|
|
}
|
|
|
|
if (ref->limit.enable) {
|
|
dur_ref = min(dur_ref, ref->limit.max_dur);
|
|
dur_aux = mcc_intvl - dur_ref;
|
|
} else if (aux->limit.enable) {
|
|
dur_aux = min(dur_aux, aux->limit.max_dur);
|
|
dur_ref = mcc_intvl - dur_aux;
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC set dur: (ref, aux) {%d ~ %d} -> {%d ~ %d}\n",
|
|
ref->duration, aux->duration, dur_ref, dur_aux);
|
|
|
|
ref->duration = dur_ref;
|
|
aux->duration = dur_aux;
|
|
}
|
|
|
|
struct rtw89_mcc_mod_dur_data {
|
|
u16 available;
|
|
struct {
|
|
u16 dur;
|
|
u16 room;
|
|
} parm[NUM_OF_RTW89_MCC_ROLES];
|
|
};
|
|
|
|
static int rtw89_mcc_mod_dur_get_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_mod_dur_data *p = data;
|
|
u16 min;
|
|
|
|
p->parm[ordered_idx].dur = mcc_role->duration;
|
|
|
|
if (mcc_role->is_go)
|
|
min = RTW89_MCC_MIN_GO_DURATION;
|
|
else
|
|
min = RTW89_MCC_MIN_STA_DURATION;
|
|
|
|
p->parm[ordered_idx].room = max_t(s32, p->parm[ordered_idx].dur - min, 0);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: chk role[%u]: dur %u, min %u, room %u\n",
|
|
ordered_idx, p->parm[ordered_idx].dur, min,
|
|
p->parm[ordered_idx].room);
|
|
|
|
p->available += p->parm[ordered_idx].room;
|
|
return 0;
|
|
}
|
|
|
|
static int rtw89_mcc_mod_dur_put_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_mod_dur_data *p = data;
|
|
|
|
mcc_role->duration = p->parm[ordered_idx].dur;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: set role[%u]: dur %u\n",
|
|
ordered_idx, p->parm[ordered_idx].dur);
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_mod_duration_dual_2ghz_with_bt(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_mod_dur_data data = {};
|
|
u16 mcc_intvl = config->mcc_interval;
|
|
u16 bt_dur = mcc->bt_role.duration;
|
|
u16 wifi_dur;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur (dual 2ghz): mcc_intvl %u, raw bt_dur %u\n",
|
|
mcc_intvl, bt_dur);
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_mod_dur_get_iterator, &data);
|
|
|
|
bt_dur = clamp_t(u16, bt_dur, 1, data.available / 3);
|
|
wifi_dur = mcc_intvl - bt_dur;
|
|
|
|
if (data.parm[0].room <= data.parm[1].room) {
|
|
data.parm[0].dur -= min_t(u16, bt_dur / 2, data.parm[0].room);
|
|
data.parm[1].dur = wifi_dur - data.parm[0].dur;
|
|
} else {
|
|
data.parm[1].dur -= min_t(u16, bt_dur / 2, data.parm[1].room);
|
|
data.parm[0].dur = wifi_dur - data.parm[1].dur;
|
|
}
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_mod_dur_put_iterator, &data);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC mod dur: set bt: dur %u\n", bt_dur);
|
|
mcc->bt_role.duration = bt_dur;
|
|
}
|
|
|
|
static
|
|
void rtw89_mcc_mod_duration_diff_band_with_bt(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *role_2ghz,
|
|
struct rtw89_mcc_role *role_non_2ghz)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u16 dur_2ghz, dur_non_2ghz;
|
|
u16 bt_dur, mcc_intvl;
|
|
|
|
dur_2ghz = role_2ghz->duration;
|
|
dur_non_2ghz = role_non_2ghz->duration;
|
|
mcc_intvl = config->mcc_interval;
|
|
bt_dur = mcc->bt_role.duration;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur (diff band): mcc_intvl %u, bt_dur %u\n",
|
|
mcc_intvl, bt_dur);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: check dur_2ghz %u, dur_non_2ghz %u\n",
|
|
dur_2ghz, dur_non_2ghz);
|
|
|
|
if (dur_non_2ghz >= bt_dur) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: dur_non_2ghz is enough for bt\n");
|
|
return;
|
|
}
|
|
|
|
dur_non_2ghz = bt_dur;
|
|
dur_2ghz = mcc_intvl - dur_non_2ghz;
|
|
|
|
if (role_non_2ghz->limit.enable) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: dur_non_2ghz is limited with max %u\n",
|
|
role_non_2ghz->limit.max_dur);
|
|
|
|
dur_non_2ghz = min(dur_non_2ghz, role_non_2ghz->limit.max_dur);
|
|
dur_2ghz = mcc_intvl - dur_non_2ghz;
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC mod dur: set dur_2ghz %u, dur_non_2ghz %u\n",
|
|
dur_2ghz, dur_non_2ghz);
|
|
|
|
role_2ghz->duration = dur_2ghz;
|
|
role_non_2ghz->duration = dur_non_2ghz;
|
|
}
|
|
|
|
static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role;
|
|
|
|
if (!bt_role->duration)
|
|
return false;
|
|
|
|
if (ref->is_2ghz && aux->is_2ghz) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC dual roles are on 2GHz; consider BT duration\n");
|
|
|
|
rtw89_mcc_mod_duration_dual_2ghz_with_bt(rtwdev);
|
|
return true;
|
|
}
|
|
|
|
if (!ref->is_2ghz && !aux->is_2ghz) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC dual roles are not on 2GHz; ignore BT duration\n");
|
|
return false;
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC one role is on 2GHz; modify another for BT duration\n");
|
|
|
|
if (ref->is_2ghz)
|
|
rtw89_mcc_mod_duration_diff_band_with_bt(rtwdev, ref, aux);
|
|
else
|
|
rtw89_mcc_mod_duration_diff_band_with_bt(rtwdev, aux, ref);
|
|
|
|
return false;
|
|
}
|
|
|
|
void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work)
|
|
{
|
|
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
|
|
mcc_prepare_done_work.work);
|
|
|
|
lockdep_assert_wiphy(wiphy);
|
|
|
|
ieee80211_wake_queues(rtwdev->hw);
|
|
}
|
|
|
|
static void rtw89_mcc_prepare(struct rtw89_dev *rtwdev, bool start)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
|
|
if (start) {
|
|
ieee80211_stop_queues(rtwdev->hw);
|
|
|
|
wiphy_delayed_work_queue(rtwdev->hw->wiphy,
|
|
&rtwdev->mcc_prepare_done_work,
|
|
usecs_to_jiffies(config->prepare_delay));
|
|
} else {
|
|
wiphy_delayed_work_queue(rtwdev->hw->wiphy,
|
|
&rtwdev->mcc_prepare_done_work, 0);
|
|
wiphy_delayed_work_flush(rtwdev->hw->wiphy,
|
|
&rtwdev->mcc_prepare_done_work);
|
|
}
|
|
}
|
|
|
|
static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval);
|
|
s32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref);
|
|
u64 tsf, start_tsf;
|
|
u32 cur_tbtt_ofst;
|
|
u64 min_time;
|
|
u64 tsf_aux;
|
|
int ret;
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_req_tsf(rtwdev, &tsf, &tsf_aux);
|
|
else
|
|
ret = __mcc_fw_req_tsf(rtwdev, &tsf, &tsf_aux);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
min_time = tsf;
|
|
if (ref->is_go || aux->is_go)
|
|
min_time += ieee80211_tu_to_usec(RTW89_MCC_SHORT_TRIGGER_TIME);
|
|
else
|
|
min_time += ieee80211_tu_to_usec(RTW89_MCC_LONG_TRIGGER_TIME);
|
|
|
|
cur_tbtt_ofst = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf);
|
|
start_tsf = tsf - cur_tbtt_ofst + bcn_intvl_ref_us - tob_ref_us;
|
|
if (start_tsf < min_time)
|
|
start_tsf += roundup_u64(min_time - start_tsf, bcn_intvl_ref_us);
|
|
|
|
config->start_tsf = start_tsf;
|
|
config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf;
|
|
config->prepare_delay = start_tsf - tsf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
bool hdl_bt;
|
|
int ret;
|
|
|
|
memset(config, 0, sizeof(*config));
|
|
|
|
switch (mcc->mode) {
|
|
case RTW89_MCC_MODE_GO_STA:
|
|
config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev);
|
|
if (ref->is_go) {
|
|
config->mcc_interval = ref->beacon_interval;
|
|
rtw89_mcc_set_duration_go_sta(rtwdev, ref, aux);
|
|
} else {
|
|
config->mcc_interval = aux->beacon_interval;
|
|
rtw89_mcc_set_duration_go_sta(rtwdev, aux, ref);
|
|
}
|
|
break;
|
|
case RTW89_MCC_MODE_GC_STA:
|
|
config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev);
|
|
config->mcc_interval = ref->beacon_interval;
|
|
rtw89_mcc_set_duration_gc_sta(rtwdev);
|
|
break;
|
|
default:
|
|
rtw89_warn(rtwdev, "MCC unknown mode: %d\n", mcc->mode);
|
|
return -EFAULT;
|
|
}
|
|
|
|
hdl_bt = rtw89_mcc_duration_decision_on_bt(rtwdev);
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC handle bt: %d\n", hdl_bt);
|
|
|
|
ret = rtw89_mcc_calc_pattern(rtwdev, hdl_bt);
|
|
if (!ret)
|
|
goto bottom;
|
|
|
|
rtw89_mcc_set_default_pattern(rtwdev);
|
|
|
|
bottom:
|
|
return rtw89_mcc_fill_start_tsf(rtwdev);
|
|
}
|
|
|
|
static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role)
|
|
{
|
|
const struct rtw89_mcc_courtesy_cfg *crtz = role->crtz;
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_policy *policy = &role->policy;
|
|
struct rtw89_fw_mcc_add_req req = {};
|
|
const struct rtw89_chan *chan;
|
|
int ret;
|
|
|
|
chan = rtw89_chan_get(rtwdev, role->rtwvif_link->chanctx_idx);
|
|
req.central_ch_seg0 = chan->channel;
|
|
req.primary_ch = chan->primary_channel;
|
|
req.bandwidth = chan->band_width;
|
|
req.ch_band_type = chan->band_type;
|
|
|
|
req.macid = role->rtwvif_link->mac_id;
|
|
req.group = mcc->group;
|
|
req.c2h_rpt = policy->c2h_rpt;
|
|
req.tx_null_early = policy->tx_null_early;
|
|
req.dis_tx_null = policy->dis_tx_null;
|
|
req.in_curr_ch = policy->in_curr_ch;
|
|
req.sw_retry_count = policy->sw_retry_count;
|
|
req.dis_sw_retry = policy->dis_sw_retry;
|
|
req.duration = role->duration;
|
|
req.btc_in_2g = false;
|
|
|
|
if (crtz) {
|
|
req.courtesy_target = crtz->macid_tgt;
|
|
req.courtesy_num = crtz->slot_num;
|
|
req.courtesy_en = true;
|
|
}
|
|
|
|
ret = rtw89_fw_h2c_add_mcc(rtwdev, &req);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to add wifi role: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = rtw89_fw_h2c_mcc_macid_bitmap(rtwdev, mcc->group,
|
|
role->rtwvif_link->mac_id,
|
|
role->macid_bitmap);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to set macid bitmap: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void __mrc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role,
|
|
struct rtw89_fw_mrc_add_arg *arg, u8 slot_idx)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_policy *policy = &role->policy;
|
|
struct rtw89_fw_mrc_add_slot_arg *slot_arg;
|
|
const struct rtw89_chan *chan;
|
|
|
|
slot_arg = &arg->slots[slot_idx];
|
|
role->slot_idx = slot_idx;
|
|
|
|
slot_arg->duration = role->duration;
|
|
slot_arg->role_num = 1;
|
|
|
|
chan = rtw89_chan_get(rtwdev, role->rtwvif_link->chanctx_idx);
|
|
|
|
slot_arg->roles[0].role_type = RTW89_H2C_MRC_ROLE_WIFI;
|
|
slot_arg->roles[0].is_master = role == ref;
|
|
slot_arg->roles[0].band = chan->band_type;
|
|
slot_arg->roles[0].bw = chan->band_width;
|
|
slot_arg->roles[0].central_ch = chan->channel;
|
|
slot_arg->roles[0].primary_ch = chan->primary_channel;
|
|
slot_arg->roles[0].en_tx_null = !policy->dis_tx_null;
|
|
slot_arg->roles[0].null_early = policy->tx_null_early;
|
|
slot_arg->roles[0].macid = role->rtwvif_link->mac_id;
|
|
slot_arg->roles[0].macid_main_bitmap =
|
|
rtw89_mcc_role_fw_macid_bitmap_to_u32(role);
|
|
}
|
|
|
|
static int __mcc_fw_add_bt_role(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role;
|
|
struct rtw89_fw_mcc_add_req req = {};
|
|
int ret;
|
|
|
|
req.group = mcc->group;
|
|
req.duration = bt_role->duration;
|
|
req.btc_in_2g = true;
|
|
|
|
ret = rtw89_fw_h2c_add_mcc(rtwdev, &req);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to add bt role: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void __mrc_fw_add_bt_role(struct rtw89_dev *rtwdev,
|
|
struct rtw89_fw_mrc_add_arg *arg, u8 slot_idx)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_bt_role *bt_role = &mcc->bt_role;
|
|
struct rtw89_fw_mrc_add_slot_arg *slot_arg = &arg->slots[slot_idx];
|
|
|
|
slot_arg->duration = bt_role->duration;
|
|
slot_arg->role_num = 1;
|
|
|
|
slot_arg->roles[0].role_type = RTW89_H2C_MRC_ROLE_BT;
|
|
}
|
|
|
|
static int __mcc_fw_start(struct rtw89_dev *rtwdev, bool replace)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_pattern *pattern = &config->pattern;
|
|
struct rtw89_mcc_sync *sync = &config->sync;
|
|
struct rtw89_fw_mcc_start_req req = {};
|
|
int ret;
|
|
|
|
if (replace) {
|
|
req.old_group = mcc->group;
|
|
req.old_group_action = RTW89_FW_MCC_OLD_GROUP_ACT_REPLACE;
|
|
mcc->group = RTW89_MCC_NEXT_GROUP(mcc->group);
|
|
}
|
|
|
|
req.group = mcc->group;
|
|
|
|
switch (pattern->plan) {
|
|
case RTW89_MCC_PLAN_TAIL_BT:
|
|
ret = __mcc_fw_add_role(rtwdev, ref);
|
|
if (ret)
|
|
return ret;
|
|
ret = __mcc_fw_add_role(rtwdev, aux);
|
|
if (ret)
|
|
return ret;
|
|
ret = __mcc_fw_add_bt_role(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
req.btc_in_group = true;
|
|
break;
|
|
case RTW89_MCC_PLAN_MID_BT:
|
|
ret = __mcc_fw_add_role(rtwdev, ref);
|
|
if (ret)
|
|
return ret;
|
|
ret = __mcc_fw_add_bt_role(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
ret = __mcc_fw_add_role(rtwdev, aux);
|
|
if (ret)
|
|
return ret;
|
|
|
|
req.btc_in_group = true;
|
|
break;
|
|
case RTW89_MCC_PLAN_NO_BT:
|
|
ret = __mcc_fw_add_role(rtwdev, ref);
|
|
if (ret)
|
|
return ret;
|
|
ret = __mcc_fw_add_role(rtwdev, aux);
|
|
if (ret)
|
|
return ret;
|
|
|
|
req.btc_in_group = false;
|
|
break;
|
|
default:
|
|
rtw89_warn(rtwdev, "MCC unknown plan: %d\n", pattern->plan);
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (sync->enable) {
|
|
ret = rtw89_fw_h2c_mcc_sync(rtwdev, req.group, sync->macid_src,
|
|
sync->macid_tgt, sync->offset);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to trigger sync: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
req.macid = ref->rtwvif_link->mac_id;
|
|
req.tsf_high = config->start_tsf >> 32;
|
|
req.tsf_low = config->start_tsf;
|
|
|
|
ret = rtw89_fw_h2c_start_mcc(rtwdev, &req);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to trigger start: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __mrc_fw_add_courtesy(struct rtw89_dev *rtwdev,
|
|
struct rtw89_fw_mrc_add_arg *arg)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_fw_mrc_add_slot_arg *slot_arg_src;
|
|
|
|
if (ref->crtz) {
|
|
slot_arg_src = &arg->slots[ref->slot_idx];
|
|
|
|
slot_arg_src->courtesy_target = aux->slot_idx;
|
|
slot_arg_src->courtesy_period = ref->crtz->slot_num;
|
|
slot_arg_src->courtesy_en = true;
|
|
}
|
|
|
|
if (aux->crtz) {
|
|
slot_arg_src = &arg->slots[aux->slot_idx];
|
|
|
|
slot_arg_src->courtesy_target = ref->slot_idx;
|
|
slot_arg_src->courtesy_period = aux->crtz->slot_num;
|
|
slot_arg_src->courtesy_en = true;
|
|
}
|
|
}
|
|
|
|
static int __mrc_fw_start(struct rtw89_dev *rtwdev, bool replace)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_pattern *pattern = &config->pattern;
|
|
struct rtw89_mcc_sync *sync = &config->sync;
|
|
struct rtw89_fw_mrc_start_arg start_arg = {};
|
|
struct rtw89_fw_mrc_add_arg add_arg = {};
|
|
int ret;
|
|
|
|
BUILD_BUG_ON(RTW89_MAC_MRC_MAX_ADD_SLOT_NUM <
|
|
NUM_OF_RTW89_MCC_ROLES + 1 /* bt role */);
|
|
|
|
if (replace) {
|
|
start_arg.old_sch_idx = mcc->group;
|
|
start_arg.action = RTW89_H2C_MRC_START_ACTION_REPLACE_OLD;
|
|
mcc->group = RTW89_MCC_NEXT_GROUP(mcc->group);
|
|
}
|
|
|
|
add_arg.sch_idx = mcc->group;
|
|
add_arg.sch_type = RTW89_H2C_MRC_SCH_BAND0_ONLY;
|
|
|
|
switch (pattern->plan) {
|
|
case RTW89_MCC_PLAN_TAIL_BT:
|
|
__mrc_fw_add_role(rtwdev, ref, &add_arg, 0);
|
|
__mrc_fw_add_role(rtwdev, aux, &add_arg, 1);
|
|
__mrc_fw_add_bt_role(rtwdev, &add_arg, 2);
|
|
|
|
add_arg.slot_num = 3;
|
|
add_arg.btc_in_sch = true;
|
|
break;
|
|
case RTW89_MCC_PLAN_MID_BT:
|
|
__mrc_fw_add_role(rtwdev, ref, &add_arg, 0);
|
|
__mrc_fw_add_bt_role(rtwdev, &add_arg, 1);
|
|
__mrc_fw_add_role(rtwdev, aux, &add_arg, 2);
|
|
|
|
add_arg.slot_num = 3;
|
|
add_arg.btc_in_sch = true;
|
|
break;
|
|
case RTW89_MCC_PLAN_NO_BT:
|
|
__mrc_fw_add_role(rtwdev, ref, &add_arg, 0);
|
|
__mrc_fw_add_role(rtwdev, aux, &add_arg, 1);
|
|
|
|
add_arg.slot_num = 2;
|
|
add_arg.btc_in_sch = false;
|
|
break;
|
|
default:
|
|
rtw89_warn(rtwdev, "MCC unknown plan: %d\n", pattern->plan);
|
|
return -EFAULT;
|
|
}
|
|
|
|
__mrc_fw_add_courtesy(rtwdev, &add_arg);
|
|
|
|
ret = rtw89_fw_h2c_mrc_add(rtwdev, &add_arg);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to trigger add: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (sync->enable) {
|
|
struct rtw89_fw_mrc_sync_arg sync_arg = {
|
|
.offset = sync->offset,
|
|
.src = {
|
|
.band = sync->band_src,
|
|
.port = sync->port_src,
|
|
},
|
|
.dest = {
|
|
.band = sync->band_tgt,
|
|
.port = sync->port_tgt,
|
|
},
|
|
};
|
|
|
|
ret = rtw89_fw_h2c_mrc_sync(rtwdev, &sync_arg);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to trigger sync: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
start_arg.sch_idx = mcc->group;
|
|
start_arg.start_tsf = config->start_tsf;
|
|
|
|
ret = rtw89_fw_h2c_mrc_start(rtwdev, &start_arg);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to trigger start: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __mcc_fw_set_duration_no_bt(struct rtw89_dev *rtwdev, bool sync_changed)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_sync *sync = &config->sync;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_fw_mcc_duration req = {
|
|
.group = mcc->group,
|
|
.btc_in_group = false,
|
|
.start_macid = ref->rtwvif_link->mac_id,
|
|
.macid_x = ref->rtwvif_link->mac_id,
|
|
.macid_y = aux->rtwvif_link->mac_id,
|
|
.duration_x = ref->duration,
|
|
.duration_y = aux->duration,
|
|
.start_tsf_high = config->start_tsf >> 32,
|
|
.start_tsf_low = config->start_tsf,
|
|
};
|
|
int ret;
|
|
|
|
ret = rtw89_fw_h2c_mcc_set_duration(rtwdev, &req);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to set duration: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!sync->enable || !sync_changed)
|
|
return 0;
|
|
|
|
ret = rtw89_fw_h2c_mcc_sync(rtwdev, mcc->group, sync->macid_src,
|
|
sync->macid_tgt, sync->offset);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to trigger sync: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __mrc_fw_set_duration_no_bt(struct rtw89_dev *rtwdev, bool sync_changed)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_sync *sync = &config->sync;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_fw_mrc_upd_duration_arg dur_arg = {
|
|
.sch_idx = mcc->group,
|
|
.start_tsf = config->start_tsf,
|
|
.slot_num = 2,
|
|
.slots[0] = {
|
|
.slot_idx = ref->slot_idx,
|
|
.duration = ref->duration,
|
|
},
|
|
.slots[1] = {
|
|
.slot_idx = aux->slot_idx,
|
|
.duration = aux->duration,
|
|
},
|
|
};
|
|
struct rtw89_fw_mrc_sync_arg sync_arg = {
|
|
.offset = sync->offset,
|
|
.src = {
|
|
.band = sync->band_src,
|
|
.port = sync->port_src,
|
|
},
|
|
.dest = {
|
|
.band = sync->band_tgt,
|
|
.port = sync->port_tgt,
|
|
},
|
|
|
|
};
|
|
int ret;
|
|
|
|
ret = rtw89_fw_h2c_mrc_upd_duration(rtwdev, &dur_arg);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to update duration: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!sync->enable || !sync_changed)
|
|
return 0;
|
|
|
|
ret = rtw89_fw_h2c_mrc_sync(rtwdev, &sync_arg);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to trigger sync: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct ieee80211_p2p_noa_desc noa_desc = {};
|
|
u32 interval = config->mcc_interval;
|
|
struct rtw89_vif_link *rtwvif_go;
|
|
u64 start_time;
|
|
u32 duration;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
|
|
return;
|
|
|
|
if (ref->is_go) {
|
|
start_time = config->start_tsf;
|
|
rtwvif_go = ref->rtwvif_link;
|
|
start_time += ieee80211_tu_to_usec(ref->duration);
|
|
duration = config->mcc_interval - ref->duration;
|
|
} else if (aux->is_go) {
|
|
start_time = config->start_tsf_in_aux_domain;
|
|
rtwvif_go = aux->rtwvif_link;
|
|
duration = config->mcc_interval - aux->duration;
|
|
} else {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC find no GO: skip updating beacon NoA\n");
|
|
return;
|
|
}
|
|
|
|
rtw89_p2p_noa_renew(rtwvif_go);
|
|
|
|
if (enable) {
|
|
duration += RTW89_MCC_SWITCH_CH_TIME;
|
|
noa_desc.start_time = cpu_to_le32(start_time);
|
|
noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(interval));
|
|
noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(duration));
|
|
noa_desc.count = 255;
|
|
rtw89_p2p_noa_append(rtwvif_go, &noa_desc);
|
|
}
|
|
|
|
/* without chanctx, we cannot get beacon from mac80211 stack */
|
|
if (!rtwvif_go->chanctx_assigned)
|
|
return;
|
|
|
|
rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_go);
|
|
}
|
|
|
|
static void rtw89_mcc_start_beacon_noa(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
|
|
return;
|
|
|
|
if (ref->is_go)
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, ref->rtwvif_link, true);
|
|
else if (aux->is_go)
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, aux->rtwvif_link, true);
|
|
|
|
rtw89_mcc_handle_beacon_noa(rtwdev, true);
|
|
}
|
|
|
|
static void rtw89_mcc_stop_beacon_noa(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
|
|
return;
|
|
|
|
if (ref->is_go)
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, ref->rtwvif_link, false);
|
|
else if (aux->is_go)
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, aux->rtwvif_link, false);
|
|
|
|
rtw89_mcc_handle_beacon_noa(rtwdev, false);
|
|
}
|
|
|
|
static bool rtw89_mcc_ignore_bcn(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role)
|
|
{
|
|
enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen;
|
|
|
|
if (role->is_go)
|
|
return true;
|
|
else if (chip_gen == RTW89_CHIP_BE && role->is_gc)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static int rtw89_mcc_start(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
int ret;
|
|
|
|
if (rtwdev->scanning)
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
rtw89_leave_lps(rtwdev);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC start\n");
|
|
|
|
ret = rtw89_mcc_fill_all_roles(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (ref->is_go || aux->is_go)
|
|
mcc->mode = RTW89_MCC_MODE_GO_STA;
|
|
else
|
|
mcc->mode = RTW89_MCC_MODE_GC_STA;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC sel mode: %d\n", mcc->mode);
|
|
|
|
mcc->group = RTW89_MCC_DFLT_GROUP;
|
|
|
|
ret = rtw89_mcc_fill_config(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn) {
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, false);
|
|
} else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn) {
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, false);
|
|
} else {
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, true);
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, true);
|
|
}
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_start(rtwdev, false);
|
|
else
|
|
ret = __mcc_fw_start(rtwdev, false);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_START);
|
|
|
|
rtw89_mcc_start_beacon_noa(rtwdev);
|
|
rtw89_phy_dig_suspend(rtwdev);
|
|
|
|
rtw89_mcc_prepare(rtwdev, true);
|
|
return 0;
|
|
}
|
|
|
|
struct rtw89_mcc_stop_sel {
|
|
struct {
|
|
const struct rtw89_vif_link *target;
|
|
} hint;
|
|
|
|
/* selection content */
|
|
bool filled;
|
|
u8 mac_id;
|
|
u8 slot_idx;
|
|
};
|
|
|
|
static void rtw89_mcc_stop_sel_fill(struct rtw89_mcc_stop_sel *sel,
|
|
const struct rtw89_mcc_role *mcc_role)
|
|
{
|
|
sel->mac_id = mcc_role->rtwvif_link->mac_id;
|
|
sel->slot_idx = mcc_role->slot_idx;
|
|
sel->filled = true;
|
|
}
|
|
|
|
static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_stop_sel *sel = data;
|
|
|
|
if (mcc_role->rtwvif_link == sel->hint.target) {
|
|
rtw89_mcc_stop_sel_fill(sel, mcc_role);
|
|
return 1; /* break iteration */
|
|
}
|
|
|
|
if (sel->filled)
|
|
return 0;
|
|
|
|
if (!mcc_role->rtwvif_link->chanctx_assigned)
|
|
return 0;
|
|
|
|
rtw89_mcc_stop_sel_fill(sel, mcc_role);
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_stop(struct rtw89_dev *rtwdev,
|
|
const struct rtw89_chanctx_pause_parm *pause)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_stop_sel sel = {
|
|
.hint.target = pause ? pause->trigger : NULL,
|
|
};
|
|
bool rsn_scan;
|
|
int ret;
|
|
|
|
if (!pause) {
|
|
wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work);
|
|
bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES);
|
|
}
|
|
|
|
rsn_scan = pause && pause->rsn == RTW89_CHANCTX_PAUSE_REASON_HW_SCAN;
|
|
if (rsn_scan && ref->is_go)
|
|
sel.hint.target = ref->rtwvif_link;
|
|
else if (rsn_scan && aux->is_go)
|
|
sel.hint.target = aux->rtwvif_link;
|
|
|
|
/* by default, stop at ref */
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel);
|
|
if (!sel.filled)
|
|
rtw89_mcc_stop_sel_fill(&sel, ref);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at <macid %d>\n", sel.mac_id);
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev)) {
|
|
ret = rtw89_fw_h2c_mrc_del(rtwdev, mcc->group, sel.slot_idx);
|
|
if (ret)
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to trigger del: %d\n", ret);
|
|
} else {
|
|
ret = rtw89_fw_h2c_stop_mcc(rtwdev, mcc->group,
|
|
sel.mac_id, true);
|
|
if (ret)
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to trigger stop: %d\n", ret);
|
|
|
|
ret = rtw89_fw_h2c_del_mcc_group(rtwdev, mcc->group, true);
|
|
if (ret)
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to delete group: %d\n", ret);
|
|
}
|
|
|
|
rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP);
|
|
|
|
rtw89_mcc_stop_beacon_noa(rtwdev);
|
|
rtw89_fw_h2c_mcc_dig(rtwdev, RTW89_CHANCTX_0, 0, 0, false);
|
|
rtw89_phy_dig_resume(rtwdev, true);
|
|
|
|
rtw89_mcc_prepare(rtwdev, false);
|
|
}
|
|
|
|
static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
bool old_ref_ignore_bcn = mcc->role_ref.ignore_bcn;
|
|
bool old_aux_ignore_bcn = mcc->role_aux.ignore_bcn;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
struct rtw89_mcc_config old_cfg = *config;
|
|
bool courtesy_changed;
|
|
bool sync_changed;
|
|
int ret;
|
|
|
|
if (rtwdev->scanning)
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC update\n");
|
|
|
|
ret = rtw89_mcc_fill_config(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (old_ref_ignore_bcn != ref->ignore_bcn)
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, !ref->ignore_bcn);
|
|
else if (old_aux_ignore_bcn != aux->ignore_bcn)
|
|
rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, !aux->ignore_bcn);
|
|
|
|
if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy,
|
|
sizeof(old_cfg.pattern.courtesy)) == 0)
|
|
courtesy_changed = false;
|
|
else
|
|
courtesy_changed = true;
|
|
|
|
if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT ||
|
|
config->pattern.plan != RTW89_MCC_PLAN_NO_BT ||
|
|
courtesy_changed) {
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_start(rtwdev, true);
|
|
else
|
|
ret = __mcc_fw_start(rtwdev, true);
|
|
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
if (memcmp(&old_cfg.sync, &config->sync, sizeof(old_cfg.sync)) == 0)
|
|
sync_changed = false;
|
|
else
|
|
sync_changed = true;
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_set_duration_no_bt(rtwdev, sync_changed);
|
|
else
|
|
ret = __mcc_fw_set_duration_no_bt(rtwdev, sync_changed);
|
|
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
rtw89_mcc_handle_beacon_noa(rtwdev, true);
|
|
return 0;
|
|
}
|
|
|
|
static int rtw89_mcc_search_gc_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_role **role = data;
|
|
|
|
if (mcc_role->is_gc)
|
|
*role = mcc_role;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct rtw89_mcc_role *rtw89_mcc_get_gc_role(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_role *role = NULL;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GC_STA)
|
|
return NULL;
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_search_gc_iterator, &role);
|
|
|
|
return role;
|
|
}
|
|
|
|
void rtw89_mcc_gc_detect_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link,
|
|
mcc_gc_detect_beacon_work.work);
|
|
struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
|
|
enum rtw89_entity_mode mode;
|
|
struct rtw89_dev *rtwdev;
|
|
|
|
lockdep_assert_wiphy(wiphy);
|
|
|
|
rtwdev = rtwvif_link->rtwvif->rtwdev;
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
if (mode != RTW89_ENTITY_MODE_MCC)
|
|
return;
|
|
|
|
if (READ_ONCE(rtwvif_link->sync_bcn_tsf) > rtwvif_link->last_sync_bcn_tsf)
|
|
rtwvif_link->detect_bcn_count = 0;
|
|
else
|
|
rtwvif_link->detect_bcn_count++;
|
|
|
|
if (rtwvif_link->detect_bcn_count < RTW89_MCC_DETECT_BCN_MAX_TRIES)
|
|
rtw89_chanctx_proceed(rtwdev, NULL);
|
|
else
|
|
ieee80211_connection_loss(vif);
|
|
}
|
|
|
|
bool rtw89_mcc_detect_go_bcn(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link)
|
|
{
|
|
enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);
|
|
struct rtw89_chanctx_pause_parm pause_parm = {
|
|
.rsn = RTW89_CHANCTX_PAUSE_REASON_GC_BCN_LOSS,
|
|
.trigger = rtwvif_link,
|
|
};
|
|
struct ieee80211_bss_conf *bss_conf;
|
|
struct rtw89_mcc_role *role;
|
|
u16 bcn_int;
|
|
|
|
if (mode != RTW89_ENTITY_MODE_MCC)
|
|
return false;
|
|
|
|
role = rtw89_mcc_get_gc_role(rtwdev);
|
|
if (!role)
|
|
return false;
|
|
|
|
if (role->rtwvif_link != rtwvif_link)
|
|
return false;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC GC beacon loss, pause MCC to detect GO beacon\n");
|
|
|
|
rcu_read_lock();
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
bcn_int = bss_conf->beacon_int;
|
|
|
|
rcu_read_unlock();
|
|
|
|
rtw89_chanctx_pause(rtwdev, &pause_parm);
|
|
rtwvif_link->last_sync_bcn_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);
|
|
wiphy_delayed_work_queue(rtwdev->hw->wiphy,
|
|
&rtwvif_link->mcc_gc_detect_beacon_work,
|
|
usecs_to_jiffies(ieee80211_tu_to_usec(bcn_int)));
|
|
|
|
return true;
|
|
}
|
|
|
|
static void rtw89_mcc_detect_connection(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *role)
|
|
{
|
|
struct ieee80211_vif *vif;
|
|
bool start_detect;
|
|
int ret;
|
|
|
|
ret = rtw89_core_send_nullfunc(rtwdev, role->rtwvif_link, true, false,
|
|
RTW89_MCC_PROBE_TIMEOUT);
|
|
if (ret)
|
|
role->probe_count++;
|
|
else
|
|
role->probe_count = 0;
|
|
|
|
if (role->probe_count < RTW89_MCC_PROBE_MAX_TRIES)
|
|
return;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC <macid %d> can not detect AP/GO\n", role->rtwvif_link->mac_id);
|
|
|
|
start_detect = rtw89_mcc_detect_go_bcn(rtwdev, role->rtwvif_link);
|
|
if (start_detect)
|
|
return;
|
|
|
|
vif = rtwvif_link_to_vif(role->rtwvif_link);
|
|
ieee80211_connection_loss(vif);
|
|
}
|
|
|
|
static void rtw89_mcc_track(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
struct rtw89_mcc_pattern *pattern = &config->pattern;
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
|
u16 tolerance;
|
|
u16 bcn_ofst;
|
|
u16 diff;
|
|
|
|
if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn)
|
|
rtw89_mcc_detect_connection(rtwdev, aux);
|
|
else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn)
|
|
rtw89_mcc_detect_connection(rtwdev, ref);
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GC_STA)
|
|
return;
|
|
|
|
bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev);
|
|
if (bcn_ofst == config->beacon_offset)
|
|
return;
|
|
|
|
if (bcn_ofst > config->beacon_offset) {
|
|
diff = bcn_ofst - config->beacon_offset;
|
|
if (pattern->tob_aux < 0)
|
|
tolerance = -pattern->tob_aux;
|
|
else if (pattern->toa_aux > 0)
|
|
tolerance = pattern->toa_aux;
|
|
else
|
|
return; /* no chance to improve */
|
|
} else {
|
|
diff = config->beacon_offset - bcn_ofst;
|
|
if (pattern->toa_aux < 0)
|
|
tolerance = -pattern->toa_aux;
|
|
else if (pattern->tob_aux > 0)
|
|
tolerance = pattern->tob_aux;
|
|
else
|
|
return; /* no chance to improve */
|
|
}
|
|
|
|
if (diff <= tolerance)
|
|
return;
|
|
|
|
rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_BCN_OFFSET_CHANGE);
|
|
}
|
|
|
|
static int __mcc_fw_upd_macid_bitmap(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *upd)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
int ret;
|
|
|
|
ret = rtw89_fw_h2c_mcc_macid_bitmap(rtwdev, mcc->group,
|
|
upd->rtwvif_link->mac_id,
|
|
upd->macid_bitmap);
|
|
if (ret) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MCC h2c failed to update macid bitmap: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __mrc_fw_upd_macid_bitmap(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *cur,
|
|
struct rtw89_mcc_role *upd)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
struct rtw89_fw_mrc_upd_bitmap_arg arg = {};
|
|
u32 old = rtw89_mcc_role_fw_macid_bitmap_to_u32(cur);
|
|
u32 new = rtw89_mcc_role_fw_macid_bitmap_to_u32(upd);
|
|
u32 add = new & ~old;
|
|
u32 del = old & ~new;
|
|
int ret;
|
|
int i;
|
|
|
|
arg.sch_idx = mcc->group;
|
|
arg.macid = upd->rtwvif_link->mac_id;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if (add & BIT(i)) {
|
|
arg.client_macid = i;
|
|
arg.action = RTW89_H2C_MRC_UPD_BITMAP_ACTION_ADD;
|
|
|
|
ret = rtw89_fw_h2c_mrc_upd_bitmap(rtwdev, &arg);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if (del & BIT(i)) {
|
|
arg.client_macid = i;
|
|
arg.action = RTW89_H2C_MRC_UPD_BITMAP_ACTION_DEL;
|
|
|
|
ret = rtw89_fw_h2c_mrc_upd_bitmap(rtwdev, &arg);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"MRC h2c failed to update bitmap: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int rtw89_mcc_upd_map_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_role upd = {
|
|
.rtwvif_link = mcc_role->rtwvif_link,
|
|
};
|
|
int ret;
|
|
|
|
if (!mcc_role->is_go)
|
|
return 0;
|
|
|
|
rtw89_mcc_fill_role_macid_bitmap(rtwdev, &upd);
|
|
if (memcmp(mcc_role->macid_bitmap, upd.macid_bitmap,
|
|
sizeof(mcc_role->macid_bitmap)) == 0)
|
|
return 0;
|
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
ret = __mrc_fw_upd_macid_bitmap(rtwdev, mcc_role, &upd);
|
|
else
|
|
ret = __mcc_fw_upd_macid_bitmap(rtwdev, &upd);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
memcpy(mcc_role->macid_bitmap, upd.macid_bitmap,
|
|
sizeof(mcc_role->macid_bitmap));
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_update_macid_bitmap(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
|
|
return;
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_map_iterator, NULL);
|
|
}
|
|
|
|
static int rtw89_mcc_upd_lmt_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
memset(&mcc_role->limit, 0, sizeof(mcc_role->limit));
|
|
rtw89_mcc_fill_role_limit(rtwdev, mcc_role);
|
|
return 0;
|
|
}
|
|
|
|
static void rtw89_mcc_update_limit(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GC_STA)
|
|
return;
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_lmt_iterator, NULL);
|
|
}
|
|
|
|
static int rtw89_mcc_get_links_iterator(struct rtw89_dev *rtwdev,
|
|
struct rtw89_mcc_role *mcc_role,
|
|
unsigned int ordered_idx,
|
|
void *data)
|
|
{
|
|
struct rtw89_mcc_links_info *info = data;
|
|
|
|
info->links[ordered_idx] = mcc_role->rtwvif_link;
|
|
return 0;
|
|
}
|
|
|
|
void rtw89_mcc_get_links(struct rtw89_dev *rtwdev, struct rtw89_mcc_links_info *info)
|
|
{
|
|
enum rtw89_entity_mode mode;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
if (unlikely(mode != RTW89_ENTITY_MODE_MCC))
|
|
return;
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_get_links_iterator, info);
|
|
}
|
|
|
|
void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work)
|
|
{
|
|
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
|
|
chanctx_work.work);
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
bool update_mcc_pattern = false;
|
|
enum rtw89_entity_mode mode;
|
|
u32 changed = 0;
|
|
int ret;
|
|
int i;
|
|
|
|
lockdep_assert_wiphy(wiphy);
|
|
|
|
if (hal->entity_pause)
|
|
return;
|
|
|
|
for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) {
|
|
if (test_and_clear_bit(i, hal->changes))
|
|
changed |= BIT(i);
|
|
}
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
case RTW89_ENTITY_MODE_MCC_PREPARE:
|
|
rtw89_set_entity_mode(rtwdev, RTW89_ENTITY_MODE_MCC);
|
|
rtw89_set_channel(rtwdev);
|
|
|
|
ret = rtw89_mcc_start(rtwdev);
|
|
if (ret)
|
|
rtw89_warn(rtwdev, "failed to start MCC: %d\n", ret);
|
|
break;
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
if (changed & BIT(RTW89_CHANCTX_BCN_OFFSET_CHANGE) ||
|
|
changed & BIT(RTW89_CHANCTX_P2P_PS_CHANGE) ||
|
|
changed & BIT(RTW89_CHANCTX_BT_SLOT_CHANGE) ||
|
|
changed & BIT(RTW89_CHANCTX_TSF32_TOGGLE_CHANGE))
|
|
update_mcc_pattern = true;
|
|
if (changed & BIT(RTW89_CHANCTX_REMOTE_STA_CHANGE))
|
|
rtw89_mcc_update_macid_bitmap(rtwdev);
|
|
if (changed & BIT(RTW89_CHANCTX_P2P_PS_CHANGE))
|
|
rtw89_mcc_update_limit(rtwdev);
|
|
if (changed & BIT(RTW89_CHANCTX_BT_SLOT_CHANGE))
|
|
rtw89_mcc_fill_bt_role(rtwdev);
|
|
if (update_mcc_pattern) {
|
|
ret = rtw89_mcc_update(rtwdev);
|
|
if (ret)
|
|
rtw89_warn(rtwdev, "failed to update MCC: %d\n",
|
|
ret);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_changes change)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_entity_mode mode;
|
|
u32 delay;
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
default:
|
|
return;
|
|
case RTW89_ENTITY_MODE_MCC_PREPARE:
|
|
delay = ieee80211_tu_to_usec(RTW89_CHANCTX_TIME_MCC_PREPARE);
|
|
rtw89_phy_dig_suspend(rtwdev);
|
|
break;
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
delay = ieee80211_tu_to_usec(RTW89_CHANCTX_TIME_MCC);
|
|
break;
|
|
}
|
|
|
|
if (change != RTW89_CHANCTX_CHANGE_DFLT) {
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "set chanctx change %d\n",
|
|
change);
|
|
set_bit(change, hal->changes);
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
"queue chanctx work for mode %d with delay %d us\n",
|
|
mode, delay);
|
|
wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work,
|
|
usecs_to_jiffies(delay));
|
|
}
|
|
|
|
void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev)
|
|
{
|
|
rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_CHANGE_DFLT);
|
|
}
|
|
|
|
static enum rtw89_mr_wtype __rtw89_query_mr_wtype(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
|
|
enum rtw89_chanctx_idx chanctx_idx;
|
|
struct ieee80211_vif *vif;
|
|
struct rtw89_vif *rtwvif;
|
|
unsigned int num_mld = 0;
|
|
unsigned int num_ml = 0;
|
|
unsigned int cnt = 0;
|
|
u8 role_idx;
|
|
u8 idx;
|
|
|
|
for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) {
|
|
rtwvif = mgnt->active_roles[role_idx];
|
|
if (!rtwvif)
|
|
continue;
|
|
|
|
cnt++;
|
|
|
|
vif = rtwvif_to_vif(rtwvif);
|
|
if (!ieee80211_vif_is_mld(vif))
|
|
continue;
|
|
|
|
num_mld++;
|
|
|
|
for (idx = 0; idx < __RTW89_MLD_MAX_LINK_NUM; idx++) {
|
|
chanctx_idx = mgnt->chanctx_tbl[role_idx][idx];
|
|
if (chanctx_idx != RTW89_CHANCTX_IDLE)
|
|
num_ml++;
|
|
}
|
|
}
|
|
|
|
if (num_mld > 1)
|
|
goto err;
|
|
|
|
switch (cnt) {
|
|
case 0:
|
|
return RTW89_MR_WTYPE_NONE;
|
|
case 1:
|
|
if (!num_mld)
|
|
return RTW89_MR_WTYPE_NONMLD;
|
|
switch (num_ml) {
|
|
case 1:
|
|
return RTW89_MR_WTYPE_MLD1L1R;
|
|
case 2:
|
|
return RTW89_MR_WTYPE_MLD2L1R;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!num_mld)
|
|
return RTW89_MR_WTYPE_NONMLD_NONMLD;
|
|
switch (num_ml) {
|
|
case 1:
|
|
return RTW89_MR_WTYPE_MLD1L1R_NONMLD;
|
|
case 2:
|
|
return RTW89_MR_WTYPE_MLD2L1R_NONMLD;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
err:
|
|
rtw89_warn(rtwdev, "%s: unhandled cnt %u mld %u ml %u\n", __func__,
|
|
cnt, num_mld, num_ml);
|
|
return RTW89_MR_WTYPE_UNKNOWN;
|
|
}
|
|
|
|
static enum rtw89_mr_wmode __rtw89_query_mr_wmode(struct rtw89_dev *rtwdev,
|
|
u8 inst_idx)
|
|
{
|
|
struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
|
|
unsigned int num[NUM_NL80211_IFTYPES] = {};
|
|
enum rtw89_chanctx_idx chanctx_idx;
|
|
struct ieee80211_vif *vif;
|
|
struct rtw89_vif *rtwvif;
|
|
unsigned int cnt = 0;
|
|
u8 role_idx;
|
|
|
|
if (unlikely(inst_idx >= __RTW89_MLD_MAX_LINK_NUM))
|
|
return RTW89_MR_WMODE_UNKNOWN;
|
|
|
|
for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) {
|
|
chanctx_idx = mgnt->chanctx_tbl[role_idx][inst_idx];
|
|
if (chanctx_idx == RTW89_CHANCTX_IDLE)
|
|
continue;
|
|
|
|
rtwvif = mgnt->active_roles[role_idx];
|
|
if (unlikely(!rtwvif))
|
|
continue;
|
|
|
|
vif = rtwvif_to_vif(rtwvif);
|
|
num[vif->type]++;
|
|
cnt++;
|
|
}
|
|
|
|
switch (cnt) {
|
|
case 0:
|
|
return RTW89_MR_WMODE_NONE;
|
|
case 1:
|
|
if (num[NL80211_IFTYPE_STATION])
|
|
return RTW89_MR_WMODE_1CLIENT;
|
|
if (num[NL80211_IFTYPE_AP])
|
|
return RTW89_MR_WMODE_1AP;
|
|
break;
|
|
case 2:
|
|
if (num[NL80211_IFTYPE_STATION] == 2)
|
|
return RTW89_MR_WMODE_2CLIENTS;
|
|
if (num[NL80211_IFTYPE_AP] == 2)
|
|
return RTW89_MR_WMODE_2APS;
|
|
if (num[NL80211_IFTYPE_STATION] && num[NL80211_IFTYPE_AP])
|
|
return RTW89_MR_WMODE_1AP_1CLIENT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rtw89_warn(rtwdev, "%s: unhandled cnt %u\n", __func__, cnt);
|
|
return RTW89_MR_WMODE_UNKNOWN;
|
|
}
|
|
|
|
static enum rtw89_mr_ctxtype __rtw89_query_mr_ctxtype(struct rtw89_dev *rtwdev,
|
|
u8 inst_idx)
|
|
{
|
|
struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
|
|
DECLARE_BITMAP(map, NUM_OF_RTW89_CHANCTX) = {};
|
|
unsigned int num[RTW89_BAND_NUM] = {};
|
|
enum rtw89_chanctx_idx chanctx_idx;
|
|
const struct rtw89_chan *chan;
|
|
unsigned int cnt = 0;
|
|
u8 role_idx;
|
|
|
|
if (unlikely(inst_idx >= __RTW89_MLD_MAX_LINK_NUM))
|
|
return RTW89_MR_CTX_UNKNOWN;
|
|
|
|
for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) {
|
|
chanctx_idx = mgnt->chanctx_tbl[role_idx][inst_idx];
|
|
if (chanctx_idx == RTW89_CHANCTX_IDLE)
|
|
continue;
|
|
|
|
if (__test_and_set_bit(chanctx_idx, map))
|
|
continue;
|
|
|
|
chan = rtw89_chan_get(rtwdev, chanctx_idx);
|
|
num[chan->band_type]++;
|
|
cnt++;
|
|
}
|
|
|
|
switch (cnt) {
|
|
case 0:
|
|
return RTW89_MR_CTX_NONE;
|
|
case 1:
|
|
if (num[RTW89_BAND_2G])
|
|
return RTW89_MR_CTX1_2GHZ;
|
|
if (num[RTW89_BAND_5G])
|
|
return RTW89_MR_CTX1_5GHZ;
|
|
if (num[RTW89_BAND_6G])
|
|
return RTW89_MR_CTX1_6GHZ;
|
|
break;
|
|
case 2:
|
|
if (num[RTW89_BAND_2G] == 2)
|
|
return RTW89_MR_CTX2_2GHZ;
|
|
if (num[RTW89_BAND_5G] == 2)
|
|
return RTW89_MR_CTX2_5GHZ;
|
|
if (num[RTW89_BAND_6G] == 2)
|
|
return RTW89_MR_CTX2_6GHZ;
|
|
if (num[RTW89_BAND_2G] && num[RTW89_BAND_5G])
|
|
return RTW89_MR_CTX2_2GHZ_5GHZ;
|
|
if (num[RTW89_BAND_2G] && num[RTW89_BAND_6G])
|
|
return RTW89_MR_CTX2_2GHZ_6GHZ;
|
|
if (num[RTW89_BAND_5G] && num[RTW89_BAND_6G])
|
|
return RTW89_MR_CTX2_5GHZ_6GHZ;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rtw89_warn(rtwdev, "%s: unhandled cnt %u\n", __func__, cnt);
|
|
return RTW89_MR_CTX_UNKNOWN;
|
|
}
|
|
|
|
void rtw89_query_mr_chanctx_info(struct rtw89_dev *rtwdev, u8 inst_idx,
|
|
struct rtw89_mr_chanctx_info *info)
|
|
{
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
info->wtype = __rtw89_query_mr_wtype(rtwdev);
|
|
info->wmode = __rtw89_query_mr_wmode(rtwdev, inst_idx);
|
|
info->ctxtype = __rtw89_query_mr_ctxtype(rtwdev, inst_idx);
|
|
}
|
|
|
|
void rtw89_chanctx_track(struct rtw89_dev *rtwdev)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_entity_mode mode;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
if (hal->entity_pause)
|
|
return;
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
rtw89_mcc_track(rtwdev);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void rtw89_chanctx_pause(struct rtw89_dev *rtwdev,
|
|
const struct rtw89_chanctx_pause_parm *pause_parm)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_entity_mode mode;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
if (hal->entity_pause)
|
|
return;
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn);
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
rtw89_mcc_stop(rtwdev, pause_parm);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
hal->entity_pause = true;
|
|
}
|
|
|
|
static void rtw89_chanctx_proceed_cb(struct rtw89_dev *rtwdev,
|
|
const struct rtw89_chanctx_cb_parm *parm)
|
|
{
|
|
int ret;
|
|
|
|
if (!parm || !parm->cb)
|
|
return;
|
|
|
|
ret = parm->cb(rtwdev, parm->data);
|
|
if (ret)
|
|
rtw89_warn(rtwdev, "%s (%s): cb failed: %d\n", __func__,
|
|
parm->caller ?: "unknown", ret);
|
|
}
|
|
|
|
/* pass @cb_parm if there is a @cb_parm->cb which needs to invoke right after
|
|
* call rtw89_set_channel() and right before proceed entity according to mode.
|
|
*/
|
|
void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,
|
|
const struct rtw89_chanctx_cb_parm *cb_parm)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_entity_mode mode;
|
|
int ret;
|
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
|
|
|
if (unlikely(!hal->entity_pause)) {
|
|
rtw89_chanctx_proceed_cb(rtwdev, cb_parm);
|
|
return;
|
|
}
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx proceed\n");
|
|
|
|
hal->entity_pause = false;
|
|
rtw89_set_channel(rtwdev);
|
|
|
|
rtw89_chanctx_proceed_cb(rtwdev, cb_parm);
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
switch (mode) {
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
ret = rtw89_mcc_start(rtwdev);
|
|
if (ret)
|
|
rtw89_warn(rtwdev, "failed to start MCC: %d\n", ret);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rtw89_queue_chanctx_work(rtwdev);
|
|
}
|
|
|
|
static void __rtw89_swap_chanctx(struct rtw89_vif *rtwvif,
|
|
enum rtw89_chanctx_idx idx1,
|
|
enum rtw89_chanctx_idx idx2)
|
|
{
|
|
struct rtw89_vif_link *rtwvif_link;
|
|
unsigned int link_id;
|
|
|
|
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) {
|
|
if (!rtwvif_link->chanctx_assigned)
|
|
continue;
|
|
|
|
if (rtwvif_link->chanctx_idx == idx1)
|
|
rtwvif_link->chanctx_idx = idx2;
|
|
else if (rtwvif_link->chanctx_idx == idx2)
|
|
rtwvif_link->chanctx_idx = idx1;
|
|
}
|
|
}
|
|
|
|
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
|
|
enum rtw89_chanctx_idx idx1,
|
|
enum rtw89_chanctx_idx idx2)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_vif *rtwvif;
|
|
u8 cur;
|
|
|
|
if (idx1 == idx2)
|
|
return;
|
|
|
|
hal->chanctx[idx1].cfg->idx = idx2;
|
|
hal->chanctx[idx2].cfg->idx = idx1;
|
|
|
|
swap(hal->chanctx[idx1], hal->chanctx[idx2]);
|
|
|
|
rtw89_for_each_rtwvif(rtwdev, rtwvif)
|
|
__rtw89_swap_chanctx(rtwvif, idx1, idx2);
|
|
|
|
cur = atomic_read(&hal->roc_chanctx_idx);
|
|
if (cur == idx1)
|
|
atomic_set(&hal->roc_chanctx_idx, idx2);
|
|
else if (cur == idx2)
|
|
atomic_set(&hal->roc_chanctx_idx, idx1);
|
|
}
|
|
|
|
int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
{
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
|
const struct rtw89_chip_info *chip = rtwdev->chip;
|
|
u8 idx;
|
|
|
|
idx = find_first_zero_bit(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
|
if (idx >= chip->support_chanctx_num)
|
|
return -ENOENT;
|
|
|
|
rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
|
|
cfg->idx = idx;
|
|
cfg->ref_count = 0;
|
|
hal->chanctx[idx].cfg = cfg;
|
|
return 0;
|
|
}
|
|
|
|
void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev,
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
{
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
|
|
|
rtw89_config_entity_chandef(rtwdev, cfg->idx, NULL);
|
|
}
|
|
|
|
void rtw89_chanctx_ops_change(struct rtw89_dev *rtwdev,
|
|
struct ieee80211_chanctx_conf *ctx,
|
|
u32 changed)
|
|
{
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
|
u8 idx = cfg->idx;
|
|
|
|
if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
|
|
rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
|
|
rtw89_set_channel(rtwdev);
|
|
}
|
|
|
|
if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
|
|
rtw89_chan_update_punctured(rtwdev, idx, &ctx->def);
|
|
}
|
|
|
|
int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
{
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
|
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
|
struct rtw89_entity_weight w = {};
|
|
int ret;
|
|
|
|
rtwvif_link->chanctx_idx = cfg->idx;
|
|
rtwvif_link->chanctx_assigned = true;
|
|
cfg->ref_count++;
|
|
|
|
if (rtwdev->scanning)
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
if (list_empty(&rtwvif->mgnt_entry))
|
|
list_add_tail(&rtwvif->mgnt_entry, &mgnt->active_list);
|
|
|
|
if (cfg->idx == RTW89_CHANCTX_0)
|
|
goto out;
|
|
|
|
rtw89_entity_calculate_weight(rtwdev, &w);
|
|
if (w.active_chanctxs != 1)
|
|
goto out;
|
|
|
|
/* put the first active chanctx at RTW89_CHANCTX_0 */
|
|
rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0);
|
|
|
|
out:
|
|
ret = rtw89_set_channel(rtwdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rtw89_tas_reset(rtwdev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
{
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
|
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
enum rtw89_chanctx_idx roll;
|
|
enum rtw89_entity_mode cur;
|
|
enum rtw89_entity_mode new;
|
|
int ret;
|
|
|
|
rtwvif_link->chanctx_idx = RTW89_CHANCTX_0;
|
|
rtwvif_link->chanctx_assigned = false;
|
|
cfg->ref_count--;
|
|
|
|
if (rtwdev->scanning)
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
if (!rtw89_vif_is_active_role(rtwvif))
|
|
list_del_init(&rtwvif->mgnt_entry);
|
|
|
|
if (cfg->ref_count != 0)
|
|
goto out;
|
|
|
|
if (cfg->idx != RTW89_CHANCTX_0)
|
|
goto out;
|
|
|
|
roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_CHANCTX,
|
|
cfg->idx + 1);
|
|
/* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */
|
|
if (roll == NUM_OF_RTW89_CHANCTX)
|
|
goto out;
|
|
|
|
/* RTW89_CHANCTX_0 is going to release, and another exists.
|
|
* Make another roll down to RTW89_CHANCTX_0 to replace.
|
|
*/
|
|
rtw89_swap_chanctx(rtwdev, cfg->idx, roll);
|
|
|
|
out:
|
|
if (!hal->entity_pause) {
|
|
cur = rtw89_get_entity_mode(rtwdev);
|
|
switch (cur) {
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
rtw89_mcc_stop(rtwdev, NULL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = rtw89_set_channel(rtwdev);
|
|
if (ret)
|
|
return;
|
|
|
|
if (hal->entity_pause)
|
|
return;
|
|
|
|
new = rtw89_get_entity_mode(rtwdev);
|
|
switch (new) {
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
/* re-plan MCC for chanctx changes. */
|
|
ret = rtw89_mcc_start(rtwdev);
|
|
if (ret)
|
|
rtw89_warn(rtwdev, "failed to start MCC: %d\n", ret);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int rtw89_chanctx_ops_reassign_vif(struct rtw89_dev *rtwdev,
|
|
struct rtw89_vif_link *rtwvif_link,
|
|
struct ieee80211_chanctx_conf *old_ctx,
|
|
struct ieee80211_chanctx_conf *new_ctx,
|
|
bool replace)
|
|
{
|
|
int ret;
|
|
|
|
rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, old_ctx);
|
|
|
|
if (!replace)
|
|
goto assign;
|
|
|
|
rtw89_chanctx_ops_remove(rtwdev, old_ctx);
|
|
ret = rtw89_chanctx_ops_add(rtwdev, new_ctx);
|
|
if (ret) {
|
|
rtw89_err(rtwdev, "%s: failed to add chanctx: %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
assign:
|
|
ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, new_ctx);
|
|
if (ret) {
|
|
rtw89_err(rtwdev, "%s: failed to assign chanctx: %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
_rtw89_chan_update_punctured(rtwdev, rtwvif_link, &new_ctx->def);
|
|
|
|
return 0;
|
|
}
|