2022-08-09 18:49:42 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
|
|
|
/* Copyright(c) 2020-2022 Realtek Corporation
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "chan.h"
|
2023-08-31 13:31:28 +08:00
|
|
|
#include "coex.h"
|
2022-08-09 18:49:49 +08:00
|
|
|
#include "debug.h"
|
2023-08-16 16:21:33 +08:00
|
|
|
#include "fw.h"
|
2023-08-31 13:31:28 +08:00
|
|
|
#include "mac.h"
|
2025-07-10 12:24:11 +08:00
|
|
|
#include "phy.h"
|
2023-08-16 16:21:33 +08:00
|
|
|
#include "ps.h"
|
2025-03-06 10:11:41 +08:00
|
|
|
#include "sar.h"
|
2022-11-29 16:31:30 +08:00
|
|
|
#include "util.h"
|
2022-08-09 18:49:42 +08:00
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
|
|
|
|
enum rtw89_chanctx_idx idx1,
|
|
|
|
enum rtw89_chanctx_idx idx2);
|
|
|
|
|
2022-08-09 18:49:43 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-20 15:43:16 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-08-09 18:49:43 +08:00
|
|
|
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);
|
2023-09-20 15:43:16 +08:00
|
|
|
chan->pri_sb_idx = rtw89_get_primary_sb_idx(center_chan, primary_chan,
|
|
|
|
bandwidth);
|
2022-08-09 18:49:43 +08:00
|
|
|
}
|
|
|
|
|
2025-07-15 11:52:57 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-09 18:49:42 +08:00
|
|
|
bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev,
|
2024-07-27 16:06:46 +08:00
|
|
|
enum rtw89_chanctx_idx idx,
|
2022-08-09 18:49:42 +08:00
|
|
|
const struct rtw89_chan *new)
|
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2024-07-27 16:06:46 +08:00
|
|
|
struct rtw89_chan *chan = &hal->chanctx[idx].chan;
|
|
|
|
struct rtw89_chan_rcd *rcd = &hal->chanctx[idx].rcd;
|
2022-08-09 18:49:42 +08:00
|
|
|
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;
|
2023-08-16 16:21:33 +08:00
|
|
|
rcd->band_changed = band_changed;
|
2022-08-09 18:49:42 +08:00
|
|
|
|
|
|
|
*chan = *new;
|
|
|
|
return band_changed;
|
|
|
|
}
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
|
2024-07-02 20:44:52 +08:00
|
|
|
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;
|
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2024-07-02 20:44:52 +08:00
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {
|
2024-07-02 20:44:52 +08:00
|
|
|
chan = rtw89_chan_get(rtwdev, idx);
|
|
|
|
ret = iterator(chan, data);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
|
2024-07-27 16:06:46 +08:00
|
|
|
enum rtw89_chanctx_idx idx,
|
2025-06-05 19:42:03 +08:00
|
|
|
const struct cfg80211_chan_def *chandef)
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
hal->chanctx[idx].chandef = *chandef;
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
|
2024-07-27 16:06:46 +08:00
|
|
|
enum rtw89_chanctx_idx idx,
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
2025-06-05 19:42:03 +08:00
|
|
|
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);
|
wifi: rtw89: concentrate chandef setting to stack callback
Originally, we didn't support mac80211 chanctx, so it's expected that
ieee80211_hw::conf::chandef would be filled by mac80211. And then, we
could just query it whenever we need the current chandef.
However, we are planing to support mac80211 chanctx. After that, the
above assumption would be broken. So, we adjust a bit ahead to reduce
future works about mac80211 chanctx.
After this, we don't query ieee80211_hw::conf::chandef directly, and
we add a map, entity_map, to HAL to indicate which chandef came from
stack. And it will later be used to recalcate entity mode.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-9-pkshih@realtek.com
2022-08-09 18:49:47 +08:00
|
|
|
}
|
wifi: rtw89: initialize entity and configure default chandef
While idle, we need a default chandef to set channel for things,
such as scan. Before support of mac80211 chanctx, mac80211 would
configure a default one on ieee80211_hw::conf::chandef. And we
just queried it whenever we did set channel. However, after support
of mac80211 chanctx, the flow won't work like before.
Besides, we don't now query chandef from ieee80211_hw::conf::chandef
either. So, similar to mac80211 without using chanctx, we configure
the default chandef with ieee80211_channel of index 0 in 2GHz.
Although we have not added the support of mac80211 chanctx here,
this configuration should be compatible before that. So, we commit
this ahead.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-10-pkshih@realtek.com
2022-08-09 18:49:48 +08:00
|
|
|
|
2023-04-11 20:48:30 +08:00
|
|
|
void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
|
2025-04-28 19:24:55 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link,
|
2023-04-11 20:48:30 +08:00
|
|
|
const struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
2025-04-28 19:24:55 +08:00
|
|
|
enum rtw89_chanctx_idx idx = rtwvif_link->chanctx_idx;
|
2023-04-11 20:48:30 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2024-07-27 16:06:46 +08:00
|
|
|
enum rtw89_chanctx_idx cur;
|
2023-04-11 20:48:30 +08:00
|
|
|
|
|
|
|
if (chandef) {
|
2024-08-19 17:17:23 +08:00
|
|
|
cur = atomic_cmpxchg(&hal->roc_chanctx_idx,
|
2024-07-27 16:06:46 +08:00
|
|
|
RTW89_CHANCTX_IDLE, idx);
|
|
|
|
if (cur != RTW89_CHANCTX_IDLE) {
|
2023-04-11 20:48:30 +08:00
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_TXRX,
|
|
|
|
"ROC still processing on entity %d\n", idx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
hal->roc_chandef = *chandef;
|
2025-04-28 19:24:55 +08:00
|
|
|
hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link);
|
2023-04-11 20:48:30 +08:00
|
|
|
} else {
|
2024-08-19 17:17:23 +08:00
|
|
|
cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx,
|
2024-07-27 16:06:46 +08:00
|
|
|
RTW89_CHANCTX_IDLE);
|
2023-04-11 20:48:30 +08:00
|
|
|
if (cur == idx)
|
|
|
|
return;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
if (cur == RTW89_CHANCTX_IDLE)
|
2023-04-11 20:48:30 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: initialize entity and configure default chandef
While idle, we need a default chandef to set channel for things,
such as scan. Before support of mac80211 chanctx, mac80211 would
configure a default one on ieee80211_hw::conf::chandef. And we
just queried it whenever we did set channel. However, after support
of mac80211 chanctx, the flow won't work like before.
Besides, we don't now query chandef from ieee80211_hw::conf::chandef
either. So, similar to mac80211 without using chanctx, we configure
the default chandef with ieee80211_channel of index 0 in 2GHz.
Although we have not added the support of mac80211 chanctx here,
this configuration should be compatible before that. So, we commit
this ahead.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-10-pkshih@realtek.com
2022-08-09 18:49:48 +08:00
|
|
|
static void rtw89_config_default_chandef(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
|
|
|
struct cfg80211_chan_def chandef = {0};
|
|
|
|
|
|
|
|
rtw89_get_default_chandef(&chandef);
|
2025-06-05 19:42:03 +08:00
|
|
|
__rtw89_config_entity_chandef(rtwdev, RTW89_CHANCTX_0, &chandef);
|
wifi: rtw89: initialize entity and configure default chandef
While idle, we need a default chandef to set channel for things,
such as scan. Before support of mac80211 chanctx, mac80211 would
configure a default one on ieee80211_hw::conf::chandef. And we
just queried it whenever we did set channel. However, after support
of mac80211 chanctx, the flow won't work like before.
Besides, we don't now query chandef from ieee80211_hw::conf::chandef
either. So, similar to mac80211 without using chanctx, we configure
the default chandef with ieee80211_channel of index 0 in 2GHz.
Although we have not added the support of mac80211 chanctx here,
this configuration should be compatible before that. So, we commit
this ahead.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-10-pkshih@realtek.com
2022-08-09 18:49:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void rtw89_entity_init(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2024-10-22 16:31:04 +08:00
|
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
wifi: rtw89: initialize entity and configure default chandef
While idle, we need a default chandef to set channel for things,
such as scan. Before support of mac80211 chanctx, mac80211 would
configure a default one on ieee80211_hw::conf::chandef. And we
just queried it whenever we did set channel. However, after support
of mac80211 chanctx, the flow won't work like before.
Besides, we don't now query chandef from ieee80211_hw::conf::chandef
either. So, similar to mac80211 without using chanctx, we configure
the default chandef with ieee80211_channel of index 0 in 2GHz.
Although we have not added the support of mac80211 chanctx here,
this configuration should be compatible before that. So, we commit
this ahead.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-10-pkshih@realtek.com
2022-08-09 18:49:48 +08:00
|
|
|
|
2023-09-21 08:35:57 +08:00
|
|
|
hal->entity_pause = false;
|
2024-07-27 16:06:46 +08:00
|
|
|
bitmap_zero(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
2023-09-08 11:11:41 +08:00
|
|
|
bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES);
|
2024-08-19 17:17:23 +08:00
|
|
|
atomic_set(&hal->roc_chanctx_idx, RTW89_CHANCTX_IDLE);
|
2024-10-22 16:31:04 +08:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mgnt->active_list);
|
|
|
|
|
wifi: rtw89: initialize entity and configure default chandef
While idle, we need a default chandef to set channel for things,
such as scan. Before support of mac80211 chanctx, mac80211 would
configure a default one on ieee80211_hw::conf::chandef. And we
just queried it whenever we did set channel. However, after support
of mac80211 chanctx, the flow won't work like before.
Besides, we don't now query chandef from ieee80211_hw::conf::chandef
either. So, similar to mac80211 without using chanctx, we configure
the default chandef with ieee80211_channel of index 0 in 2GHz.
Although we have not added the support of mac80211 chanctx here,
this configuration should be compatible before that. So, we commit
this ahead.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220809104952.61355-10-pkshih@realtek.com
2022-08-09 18:49:48 +08:00
|
|
|
rtw89_config_default_chandef(rtwdev);
|
|
|
|
}
|
2022-08-09 18:49:49 +08:00
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-06 11:06:22 +08:00
|
|
|
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;
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
struct rtw89_vif *rtwvif;
|
2024-02-06 11:06:22 +08:00
|
|
|
int idx;
|
|
|
|
|
wifi: rtw89: chan: re-config default chandef only when none is registered
Previously, default chandef is configured if no chanctx is active, i.e. no
chanctx is assigned to some vif. For normal cases, it's fine. However, for
impending CSA support, need to consider that one chanctx may be added, or
called registered, ahead without being assigned immediately. Then, it will
keep inactive, and might be covered by the default one when re-calculating
chanctxs happens in certain sequences. So now, don't re-config the default
chandef unless no chanctx is registered.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250605114207.12381-3-pkshih@realtek.com
2025-06-05 19:42:04 +08:00
|
|
|
w->registered_chanctxs = bitmap_weight(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {
|
|
|
|
cfg = hal->chanctx[idx].cfg;
|
2024-02-06 11:06:22 +08:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
rtw89_for_each_rtwvif(rtwdev, rtwvif) {
|
|
|
|
if (rtw89_vif_is_active_role(rtwvif))
|
2024-02-06 11:06:22 +08:00
|
|
|
w->active_roles++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:05 +08:00
|
|
|
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;
|
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2024-10-22 16:31:05 +08:00
|
|
|
|
|
|
|
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) {
|
2025-04-28 19:24:55 +08:00
|
|
|
/* ROC is ongoing (given ROC runs on @hal->roc_link_index).
|
|
|
|
* If @link_index is the same, get the ongoing ROC chanctx.
|
2024-10-22 16:31:05 +08:00
|
|
|
*/
|
2025-04-28 19:24:55 +08:00
|
|
|
if (link_index == hal->roc_link_index)
|
2024-10-22 16:31:05 +08:00
|
|
|
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);
|
|
|
|
|
wifi: rtw89: chan: re-calculate MLO DBCC mode during setting channel
Wi-Fi 7 chips have dual HW bands. After impending MLO support, they
can work with HW-[0] / HW-[1] / HW-[0,1] according to active links.
So, during setting channel, also re-calculate the MLO DBCC mode flag.
Then, leaf chip functions of setting channel can configure with HWs
based on current case.
Besides, tweak the initial and idle MLO DBCC mode of Wi-Fi 7 chips to
MLO_1_PLUS_1_1RF to work with dual HW bands. And, after disconnecting,
due to no active links, MLO DBCC mode will re-calculate to idle case,
i.e. MLO_1_PLUS_1_1RF.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250505072440.45113-5-pkshih@realtek.com
2025-05-05 15:24:33 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
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;
|
wifi: rtw89: chan: re-calculate MLO DBCC mode during setting channel
Wi-Fi 7 chips have dual HW bands. After impending MLO support, they
can work with HW-[0] / HW-[1] / HW-[0,1] according to active links.
So, during setting channel, also re-calculate the MLO DBCC mode flag.
Then, leaf chip functions of setting channel can configure with HWs
based on current case.
Besides, tweak the initial and idle MLO DBCC mode of Wi-Fi 7 chips to
MLO_1_PLUS_1_1RF to work with dual HW bands. And, after disconnecting,
due to no active links, MLO DBCC mode will re-calculate to idle case,
i.e. MLO_1_PLUS_1_1RF.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250505072440.45113-5-pkshih@realtek.com
2025-05-05 15:24:33 +08:00
|
|
|
u8 active_hws = 0;
|
2024-10-22 16:31:04 +08:00
|
|
|
u8 pos = 0;
|
2024-10-22 16:31:05 +08:00
|
|
|
int i, j;
|
2024-10-22 16:31:04 +08:00
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2024-10-22 16:31:04 +08:00
|
|
|
|
|
|
|
for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++)
|
|
|
|
mgnt->active_roles[i] = NULL;
|
|
|
|
|
2024-10-22 16:31:05 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
/* 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);
|
2024-12-31 08:48:09 +08:00
|
|
|
goto fill;
|
2024-10-22 16:31:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-31 08:48:09 +08:00
|
|
|
fill:
|
2024-10-22 16:31:04 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:05 +08:00
|
|
|
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;
|
wifi: rtw89: chan: re-calculate MLO DBCC mode during setting channel
Wi-Fi 7 chips have dual HW bands. After impending MLO support, they
can work with HW-[0] / HW-[1] / HW-[0,1] according to active links.
So, during setting channel, also re-calculate the MLO DBCC mode flag.
Then, leaf chip functions of setting channel can configure with HWs
based on current case.
Besides, tweak the initial and idle MLO DBCC mode of Wi-Fi 7 chips to
MLO_1_PLUS_1_1RF to work with dual HW bands. And, after disconnecting,
due to no active links, MLO DBCC mode will re-calculate to idle case,
i.e. MLO_1_PLUS_1_1RF.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250505072440.45113-5-pkshih@realtek.com
2025-05-05 15:24:33 +08:00
|
|
|
active_hws |= BIT(i);
|
2024-10-22 16:31:05 +08:00
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
mgnt->active_roles[pos++] = role;
|
|
|
|
}
|
wifi: rtw89: chan: re-calculate MLO DBCC mode during setting channel
Wi-Fi 7 chips have dual HW bands. After impending MLO support, they
can work with HW-[0] / HW-[1] / HW-[0,1] according to active links.
So, during setting channel, also re-calculate the MLO DBCC mode flag.
Then, leaf chip functions of setting channel can configure with HWs
based on current case.
Besides, tweak the initial and idle MLO DBCC mode of Wi-Fi 7 chips to
MLO_1_PLUS_1_1RF to work with dual HW bands. And, after disconnecting,
due to no active links, MLO DBCC mode will re-calculate to idle case,
i.e. MLO_1_PLUS_1_1RF.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250505072440.45113-5-pkshih@realtek.com
2025-05-05 15:24:33 +08:00
|
|
|
|
|
|
|
rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws);
|
2024-10-22 16:31:04 +08:00
|
|
|
}
|
|
|
|
|
2022-08-09 18:49:49 +08:00
|
|
|
enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
2024-07-27 16:06:46 +08:00
|
|
|
DECLARE_BITMAP(recalc_map, NUM_OF_RTW89_CHANCTX) = {};
|
2022-08-09 18:49:49 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2023-08-16 16:21:33 +08:00
|
|
|
const struct cfg80211_chan_def *chandef;
|
2024-02-06 11:06:22 +08:00
|
|
|
struct rtw89_entity_weight w = {};
|
2022-08-09 18:49:49 +08:00
|
|
|
enum rtw89_entity_mode mode;
|
2023-08-16 16:21:33 +08:00
|
|
|
struct rtw89_chan chan;
|
|
|
|
u8 idx;
|
2022-08-09 18:49:49 +08:00
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2023-09-21 08:35:57 +08:00
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
2024-02-06 11:06:21 +08:00
|
|
|
|
2024-02-06 11:06:22 +08:00
|
|
|
rtw89_entity_calculate_weight(rtwdev, &w);
|
|
|
|
switch (w.active_chanctxs) {
|
2022-08-09 18:49:49 +08:00
|
|
|
default:
|
2024-02-06 11:06:22 +08:00
|
|
|
rtw89_warn(rtwdev, "unknown ent chanctxs weight: %d\n",
|
|
|
|
w.active_chanctxs);
|
2024-07-27 16:06:46 +08:00
|
|
|
bitmap_zero(recalc_map, NUM_OF_RTW89_CHANCTX);
|
2022-08-09 18:49:49 +08:00
|
|
|
fallthrough;
|
|
|
|
case 0:
|
wifi: rtw89: chan: re-config default chandef only when none is registered
Previously, default chandef is configured if no chanctx is active, i.e. no
chanctx is assigned to some vif. For normal cases, it's fine. However, for
impending CSA support, need to consider that one chanctx may be added, or
called registered, ahead without being assigned immediately. Then, it will
keep inactive, and might be covered by the default one when re-calculating
chanctxs happens in certain sequences. So now, don't re-config the default
chandef unless no chanctx is registered.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250605114207.12381-3-pkshih@realtek.com
2025-06-05 19:42:04 +08:00
|
|
|
if (!w.registered_chanctxs)
|
|
|
|
rtw89_config_default_chandef(rtwdev);
|
2024-07-27 16:06:46 +08:00
|
|
|
set_bit(RTW89_CHANCTX_0, recalc_map);
|
2022-08-09 18:49:49 +08:00
|
|
|
fallthrough;
|
|
|
|
case 1:
|
2024-10-22 16:31:05 +08:00
|
|
|
mode = RTW89_ENTITY_MODE_SCC_OR_SMLD;
|
2022-08-09 18:49:49 +08:00
|
|
|
break;
|
2024-07-27 16:06:46 +08:00
|
|
|
case 2 ... NUM_OF_RTW89_CHANCTX:
|
2024-10-22 16:31:05 +08:00
|
|
|
if (w.active_roles == 1) {
|
|
|
|
mode = RTW89_ENTITY_MODE_SCC_OR_SMLD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-02-06 11:06:22 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
|
|
if (mode == RTW89_ENTITY_MODE_MCC)
|
|
|
|
break;
|
|
|
|
|
|
|
|
mode = RTW89_ENTITY_MODE_MCC_PREPARE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
for_each_set_bit(idx, recalc_map, NUM_OF_RTW89_CHANCTX) {
|
2023-08-16 16:21:33 +08:00
|
|
|
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);
|
2022-08-09 18:49:49 +08:00
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
rtw89_entity_recalc_mgnt_roles(rtwdev);
|
|
|
|
|
2023-09-21 08:35:57 +08:00
|
|
|
if (hal->entity_pause)
|
|
|
|
return rtw89_get_entity_mode(rtwdev);
|
|
|
|
|
2022-08-09 18:49:49 +08:00
|
|
|
rtw89_set_entity_mode(rtwdev, mode);
|
|
|
|
return mode;
|
|
|
|
}
|
2022-08-09 18:49:50 +08:00
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
/* 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)
|
|
|
|
{
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link = role->rtwvif_link;
|
2023-08-31 13:31:28 +08:00
|
|
|
u32 bcn_intvl_us = ieee80211_tu_to_usec(role->beacon_interval);
|
2024-09-16 13:31:52 +08:00
|
|
|
u64 sync_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);
|
2023-08-31 13:31:28 +08:00
|
|
|
u32 remainder;
|
|
|
|
|
2025-04-22 09:46:15 +08:00
|
|
|
if (role->is_go) {
|
|
|
|
sync_tsf = 0;
|
|
|
|
} else if (tsf < sync_tsf) {
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
static int __mcc_fw_req_tsf(struct rtw89_dev *rtwdev, u64 *tsf_ref, u64 *tsf_aux)
|
2023-08-31 13:31:29 +08:00
|
|
|
{
|
|
|
|
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;
|
2024-09-16 13:31:52 +08:00
|
|
|
req.macid_x = ref->rtwvif_link->mac_id;
|
|
|
|
req.macid_y = aux->rtwvif_link->mac_id;
|
2023-08-31 13:31:29 +08:00
|
|
|
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);
|
2024-02-13 15:35:13 +08:00
|
|
|
return ret;
|
2023-08-31 13:31:29 +08:00
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
*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;
|
2024-09-16 13:31:52 +08:00
|
|
|
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;
|
2024-02-13 15:35:13 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2023-08-31 13:31:29 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta)
|
|
|
|
{
|
|
|
|
struct rtw89_mcc_role *mcc_role = data;
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
struct rtw89_vif *target = mcc_role->rtwvif_link->rtwvif;
|
|
|
|
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
|
|
|
|
struct rtw89_vif *rtwvif = rtwsta->rtwvif;
|
2025-04-28 19:24:56 +08:00
|
|
|
u8 macid;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
if (rtwvif != target)
|
2023-08-31 13:31:28 +08:00
|
|
|
return;
|
|
|
|
|
2025-04-28 19:24:56 +08:00
|
|
|
macid = rtw89_sta_get_main_macid(rtwsta);
|
|
|
|
rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, macid);
|
2023-08-31 13:31:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rtw89_mcc_fill_role_macid_bitmap(struct rtw89_dev *rtwdev,
|
|
|
|
struct rtw89_mcc_role *mcc_role)
|
|
|
|
{
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link = mcc_role->rtwvif_link;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, rtwvif_link->mac_id);
|
2023-08-31 13:31:28 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-09-16 13:31:54 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link = mcc_role->rtwvif_link;
|
2023-08-31 13:31:28 +08:00
|
|
|
struct ieee80211_p2p_noa_desc *noa_desc;
|
2024-09-16 13:31:54 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf;
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
|
|
|
|
2025-05-11 11:52:16 +08:00
|
|
|
if (!mcc_role->is_gc)
|
2023-08-31 13:31:28 +08:00
|
|
|
return;
|
|
|
|
|
2025-05-11 11:52:16 +08:00
|
|
|
rtw89_p2p_noa_once_recalc(rtwvif_link);
|
|
|
|
|
2024-09-16 13:31:54 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
/* find the first periodic NoA */
|
|
|
|
for (i = 0; i < RTW89_P2P_MAX_NOA_NUM; i++) {
|
2024-09-16 13:31:54 +08:00
|
|
|
noa_desc = &bss_conf->p2p_noa_attr.desc[i];
|
2023-08-31 13:31:28 +08:00
|
|
|
if (noa_desc->count == 255)
|
|
|
|
goto fill;
|
|
|
|
}
|
|
|
|
|
2024-09-16 13:31:54 +08:00
|
|
|
rcu_read_unlock();
|
2023-08-31 13:31:28 +08:00
|
|
|
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);
|
|
|
|
|
2024-09-16 13:31:54 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf);
|
2023-08-31 13:31:28 +08:00
|
|
|
if (ret) {
|
|
|
|
rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsf_lmt = (tsf & GENMASK_ULL(63, 32)) | start_time;
|
2025-04-22 09:46:16 +08:00
|
|
|
if (tsf_lmt < tsf)
|
|
|
|
tsf_lmt += roundup_u64(tsf - tsf_lmt, interval);
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
wifi: rtw89: mcc: consider time limits not divisible by 1024
For each MCC role, time limits, including max_tob_us, max_toa_us, and
mac_dur_us, are calculated if there are NoA attributes. The relation
between these time limits is "max_dur_us = max_tob_us + max_toa_us".
Then, the unit is converted from us to TU. However, originally, each
time limit was divided by 1024 independently. It missed to consider
the cases that max_tob_us or max_toa_us is not divisible by 1024. It
causes the result breaks "max_dur (TU) = max_tob (TU) + max_toa (TU)".
Finally, when MCC calculates pattern parameters based on these kinds
of time limits, it might not perform well.
Fixes: b09df09b55fb ("wifi: rtw89: mcc: initialize start flow")
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250103074412.124066-1-pkshih@realtek.com
2025-01-03 15:44:12 +08:00
|
|
|
mcc_role->limit.max_dur = mcc_role->limit.max_toa + mcc_role->limit.max_tob;
|
2023-08-31 13:31:28 +08:00
|
|
|
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,
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link,
|
2023-08-31 13:31:28 +08:00
|
|
|
struct rtw89_mcc_role *role)
|
|
|
|
{
|
2024-09-16 13:31:54 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf;
|
2023-08-31 13:31:28 +08:00
|
|
|
const struct rtw89_chan *chan;
|
|
|
|
|
|
|
|
memset(role, 0, sizeof(*role));
|
2024-09-16 13:31:52 +08:00
|
|
|
role->rtwvif_link = rtwvif_link;
|
2024-09-16 13:31:54 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true);
|
|
|
|
role->beacon_interval = bss_conf->beacon_int;
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
if (!role->beacon_interval) {
|
|
|
|
rtw89_warn(rtwdev,
|
|
|
|
"cannot handle MCC role without beacon interval\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
role->duration = role->beacon_interval / 2;
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx);
|
2023-08-31 13:31:28 +08:00
|
|
|
role->is_2ghz = chan->band_type == RTW89_BAND_2G;
|
2024-09-16 13:31:52 +08:00
|
|
|
role->is_go = rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_GO;
|
|
|
|
role->is_gc = rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
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 {
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *bind_vif[NUM_OF_RTW89_CHANCTX];
|
2023-08-31 13:31:28 +08:00
|
|
|
};
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
static_assert((u8)NUM_OF_RTW89_CHANCTX >= NUM_OF_RTW89_MCC_ROLES);
|
2024-10-22 16:31:04 +08:00
|
|
|
static_assert(RTW89_MAX_INTERFACE_NUM >= NUM_OF_RTW89_MCC_ROLES);
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
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;
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *role_vif = sel->bind_vif[ordered_idx];
|
2023-08-31 13:31:28 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-10-22 16:31:04 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
2023-08-31 13:31:28 +08:00
|
|
|
struct rtw89_mcc_fill_role_selector sel = {};
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link;
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
struct rtw89_vif *rtwvif;
|
2023-08-31 13:31:28 +08:00
|
|
|
int ret;
|
2024-10-22 16:31:04 +08:00
|
|
|
int i;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
for (i = 0; i < NUM_OF_RTW89_MCC_ROLES; i++) {
|
|
|
|
rtwvif = mgnt->active_roles[i];
|
|
|
|
if (!rtwvif)
|
|
|
|
break;
|
2024-02-06 11:06:22 +08:00
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
sel.bind_vif[i] = rtwvif_link;
|
2025-06-10 21:00:31 +08:00
|
|
|
rtw89_p2p_disable_all_noa(rtwdev, rtwvif_link, NULL);
|
2023-08-31 13:31:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_fill_role_iterator, &sel);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
rtw89_mcc_fill_bt_role(rtwdev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-05-11 11:52:14 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
2025-04-22 09:46:18 +08:00
|
|
|
struct rtw89_mcc_courtesy_cfg *crtz;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC pattern plan: %d\n", new->plan);
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
*pattern = *new;
|
|
|
|
memset(&pattern->courtesy, 0, sizeof(pattern->courtesy));
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, aux) && aux->is_gc)
|
|
|
|
aux->ignore_bcn = true;
|
|
|
|
else
|
|
|
|
aux->ignore_bcn = false;
|
|
|
|
|
2025-05-11 11:52:14 +08:00
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) {
|
2025-04-22 09:46:18 +08:00
|
|
|
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;
|
2023-08-31 13:31:28 +08:00
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, ref) && ref->is_gc)
|
|
|
|
ref->ignore_bcn = true;
|
|
|
|
else
|
|
|
|
ref->ignore_bcn = false;
|
|
|
|
|
2025-05-11 11:52:14 +08:00
|
|
|
if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) {
|
2025-04-22 09:46:18 +08:00
|
|
|
crtz = &pattern->courtesy.aux;
|
|
|
|
aux->crtz = crtz;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
crtz->macid_tgt = ref->rtwvif_link->mac_id;
|
|
|
|
crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
|
|
"MCC courtesy aux: tgt %d, slot %d\n",
|
|
|
|
crtz->macid_tgt, crtz->slot_num);
|
|
|
|
} else {
|
|
|
|
aux->crtz = NULL;
|
|
|
|
}
|
2023-08-31 13:31:28 +08:00
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
/* 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;
|
2025-04-22 09:46:20 +08:00
|
|
|
u16 mcc_intvl = config->mcc_interval;
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
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);
|
2025-04-22 09:46:20 +08:00
|
|
|
lower = max_t(s16, 0, ref->duration - (mcc_intvl - bcn_ofst));
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
|
|
|
|
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;
|
2025-07-10 12:24:12 +08:00
|
|
|
u16 min_tob = RTW89_MCC_EARLY_RX_BCN_TIME + RTW89_MCC_SWITCH_CH_TIME;
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-05-11 11:52:15 +08:00
|
|
|
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;
|
|
|
|
|
2025-07-10 12:24:12 +08:00
|
|
|
if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME)
|
2025-05-11 11:52:15 +08:00
|
|
|
small_bcn_ofst = true;
|
2025-06-10 21:00:32 +08:00
|
|
|
else if (bcn_ofst < aux->duration - aux->limit.max_toa)
|
|
|
|
small_bcn_ofst = true;
|
2025-07-10 12:24:12 +08:00
|
|
|
else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME)
|
2025-05-11 11:52:15 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-05-11 11:52:15 +08:00
|
|
|
ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt);
|
|
|
|
if (!ret)
|
|
|
|
goto done;
|
|
|
|
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
__rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt);
|
|
|
|
|
|
|
|
done:
|
|
|
|
rtw89_mcc_assign_pattern(rtwdev, &ptrn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:29 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:30 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-06-10 21:00:27 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
2025-04-22 09:46:19 +08:00
|
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
2023-08-31 13:31:28 +08:00
|
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
|
|
|
u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval);
|
2025-04-22 09:46:19 +08:00
|
|
|
s32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref);
|
2023-08-31 13:31:28 +08:00
|
|
|
u64 tsf, start_tsf;
|
|
|
|
u32 cur_tbtt_ofst;
|
|
|
|
u64 min_time;
|
2025-04-22 09:46:15 +08:00
|
|
|
u64 tsf_aux;
|
2023-08-31 13:31:28 +08:00
|
|
|
int ret;
|
|
|
|
|
2025-04-22 09:46:15 +08:00
|
|
|
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)
|
2023-08-31 13:31:28 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
min_time = tsf;
|
2025-04-22 09:46:19 +08:00
|
|
|
if (ref->is_go || aux->is_go)
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
2025-04-22 09:46:19 +08:00
|
|
|
if (start_tsf < min_time)
|
|
|
|
start_tsf += roundup_u64(min_time - start_tsf, bcn_intvl_ref_us);
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
config->start_tsf = start_tsf;
|
2025-04-22 09:46:15 +08:00
|
|
|
config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf;
|
2025-06-10 21:00:27 +08:00
|
|
|
config->prepare_delay = start_tsf - tsf;
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
|
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
2023-08-31 13:31:29 +08:00
|
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
2023-08-31 13:31:28 +08:00
|
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
2023-08-31 13:31:30 +08:00
|
|
|
bool hdl_bt;
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
int ret;
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
memset(config, 0, sizeof(*config));
|
2023-08-31 13:31:29 +08:00
|
|
|
|
|
|
|
switch (mcc->mode) {
|
|
|
|
case RTW89_MCC_MODE_GO_STA:
|
2025-04-22 09:46:15 +08:00
|
|
|
config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev);
|
2023-08-31 13:31:29 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:30 +08:00
|
|
|
hdl_bt = rtw89_mcc_duration_decision_on_bt(rtwdev);
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC handle bt: %d\n", hdl_bt);
|
|
|
|
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
ret = rtw89_mcc_calc_pattern(rtwdev, hdl_bt);
|
|
|
|
if (!ret)
|
|
|
|
goto bottom;
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
rtw89_mcc_set_default_pattern(rtwdev);
|
wifi: rtw89: mcc: decide pattern and calculate parameters
After the previous works, we can now expand and display the MCC pattern
in more detail, as shown below.
|< MCC interval >|
|< duration ref >| (if mid bt) |< duration aux >| (if tail bt) |
|<tob ref >|< toa ref>| ... |<tob aux >|< toa aux>| ... |
V V
tbtt ref tbtt aux
|< beacon offset >|
(where tob means `time offset behind` and toa means `time offset ahead`)
There are two key points.
1. decide position of BT slot if MCC pattern needs to handle BT duration.
2. calculate all parameters related to tob and toa in MCC pattern.
For point (1), when BT duration needs to be handled, BT position will
rely on beacon offset, either middle or tail. For point (2), to ensure
durations of the Wi-Fi roles cover their beacons, we have to calculate
tob and toa for them according to their TBTT.
And, there are two strategies to calculate parameters, strict and loose.
In strict pattern, all parameters take HW time into account as limitation.
But, the strict calculation are not always successful. In loose pattern,
it only tries to give positive parameters to reference role and doesn't
care much about auxiliary role. If unfortunately auxiliary role gets
negative parameters in loose pattern, FW will be notified and then deal
with it. So, the loose calculation won't fail. In general, we always try
strict pattern cases before using a loose pattern.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20230831053133.24015-5-pkshih@realtek.com
2023-08-31 13:31:31 +08:00
|
|
|
|
|
|
|
bottom:
|
2023-08-31 13:31:28 +08:00
|
|
|
return rtw89_mcc_fill_start_tsf(rtwdev);
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:33 +08:00
|
|
|
static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role)
|
|
|
|
{
|
2025-04-22 09:46:18 +08:00
|
|
|
const struct rtw89_mcc_courtesy_cfg *crtz = role->crtz;
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
chan = rtw89_chan_get(rtwdev, role->rtwvif_link->chanctx_idx);
|
2023-08-31 13:31:33 +08:00
|
|
|
req.central_ch_seg0 = chan->channel;
|
|
|
|
req.primary_ch = chan->primary_channel;
|
|
|
|
req.bandwidth = chan->band_width;
|
|
|
|
req.ch_band_type = chan->band_type;
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
req.macid = role->rtwvif_link->mac_id;
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
if (crtz) {
|
|
|
|
req.courtesy_target = crtz->macid_tgt;
|
|
|
|
req.courtesy_num = crtz->slot_num;
|
2023-08-31 13:31:33 +08:00
|
|
|
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,
|
2024-09-16 13:31:52 +08:00
|
|
|
role->rtwvif_link->mac_id,
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
chan = rtw89_chan_get(rtwdev, role->rtwvif_link->chanctx_idx);
|
2024-02-13 15:35:13 +08:00
|
|
|
|
|
|
|
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;
|
2024-09-16 13:31:52 +08:00
|
|
|
slot_arg->roles[0].macid = role->rtwvif_link->mac_id;
|
2024-02-13 15:35:13 +08:00
|
|
|
slot_arg->roles[0].macid_main_bitmap =
|
|
|
|
rtw89_mcc_role_fw_macid_bitmap_to_u32(role);
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
static int __mcc_fw_start(struct rtw89_dev *rtwdev, bool replace)
|
2023-08-31 13:31:33 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
req.macid = ref->rtwvif_link->mac_id;
|
2023-08-31 13:31:33 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
if (ref->crtz) {
|
2024-02-13 15:35:13 +08:00
|
|
|
slot_arg_src = &arg->slots[ref->slot_idx];
|
2025-04-22 09:46:18 +08:00
|
|
|
|
|
|
|
slot_arg_src->courtesy_target = aux->slot_idx;
|
|
|
|
slot_arg_src->courtesy_period = ref->crtz->slot_num;
|
|
|
|
slot_arg_src->courtesy_en = true;
|
2024-02-13 15:35:13 +08:00
|
|
|
}
|
|
|
|
|
2025-04-22 09:46:18 +08:00
|
|
|
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;
|
|
|
|
}
|
2024-02-13 15:35:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
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,
|
2024-09-16 13:31:52 +08:00
|
|
|
.start_macid = ref->rtwvif_link->mac_id,
|
|
|
|
.macid_x = ref->rtwvif_link->mac_id,
|
|
|
|
.macid_y = aux->rtwvif_link->mac_id,
|
2023-09-08 11:11:42 +08:00
|
|
|
.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;
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:45 +08:00
|
|
|
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;
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_go;
|
2025-04-22 09:46:15 +08:00
|
|
|
u64 start_time;
|
2023-09-08 11:11:45 +08:00
|
|
|
u32 duration;
|
|
|
|
|
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GO_STA)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ref->is_go) {
|
2025-04-22 09:46:15 +08:00
|
|
|
start_time = config->start_tsf;
|
2024-09-16 13:31:52 +08:00
|
|
|
rtwvif_go = ref->rtwvif_link;
|
2023-09-08 11:11:45 +08:00
|
|
|
start_time += ieee80211_tu_to_usec(ref->duration);
|
|
|
|
duration = config->mcc_interval - ref->duration;
|
|
|
|
} else if (aux->is_go) {
|
2025-04-22 09:46:15 +08:00
|
|
|
start_time = config->start_tsf_in_aux_domain;
|
2024-09-16 13:31:52 +08:00
|
|
|
rtwvif_go = aux->rtwvif_link;
|
2023-09-08 11:11:45 +08:00
|
|
|
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) {
|
2025-07-10 12:24:12 +08:00
|
|
|
duration += RTW89_MCC_SWITCH_CH_TIME;
|
2023-09-08 11:11:45 +08:00
|
|
|
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;
|
|
|
|
|
2024-01-08 17:13:59 +08:00
|
|
|
rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_go);
|
2023-09-08 11:11:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2024-09-16 13:31:52 +08:00
|
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, ref->rtwvif_link, true);
|
2023-09-08 11:11:45 +08:00
|
|
|
else if (aux->is_go)
|
2024-09-16 13:31:52 +08:00
|
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, aux->rtwvif_link, true);
|
2023-09-08 11:11:45 +08:00
|
|
|
|
|
|
|
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)
|
2024-09-16 13:31:52 +08:00
|
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, ref->rtwvif_link, false);
|
2023-09-08 11:11:45 +08:00
|
|
|
else if (aux->is_go)
|
2024-09-16 13:31:52 +08:00
|
|
|
rtw89_fw_h2c_tsf32_toggle(rtwdev, aux->rtwvif_link, false);
|
2023-09-08 11:11:45 +08:00
|
|
|
|
|
|
|
rtw89_mcc_handle_beacon_noa(rtwdev, false);
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
static int rtw89_mcc_start(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
2023-08-31 13:31:28 +08:00
|
|
|
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;
|
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
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");
|
2023-08-31 13:31:28 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2023-08-31 13:31:29 +08:00
|
|
|
mcc->group = RTW89_MCC_DFLT_GROUP;
|
|
|
|
|
2023-08-31 13:31:28 +08:00
|
|
|
ret = rtw89_mcc_fill_config(rtwdev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
|
|
ret = __mrc_fw_start(rtwdev, false);
|
|
|
|
else
|
|
|
|
ret = __mcc_fw_start(rtwdev, false);
|
|
|
|
|
2023-08-31 13:31:33 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_START);
|
2023-09-08 11:11:45 +08:00
|
|
|
|
|
|
|
rtw89_mcc_start_beacon_noa(rtwdev);
|
2025-07-10 12:24:11 +08:00
|
|
|
rtw89_phy_dig_suspend(rtwdev);
|
2025-06-10 21:00:27 +08:00
|
|
|
|
|
|
|
rtw89_mcc_prepare(rtwdev, true);
|
2023-08-16 16:21:33 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-07-27 16:06:45 +08:00
|
|
|
struct rtw89_mcc_stop_sel {
|
2025-05-11 11:52:12 +08:00
|
|
|
struct {
|
|
|
|
const struct rtw89_vif_link *target;
|
|
|
|
} hint;
|
|
|
|
|
|
|
|
/* selection content */
|
|
|
|
bool filled;
|
2024-07-27 16:06:45 +08:00
|
|
|
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)
|
|
|
|
{
|
2024-09-16 13:31:52 +08:00
|
|
|
sel->mac_id = mcc_role->rtwvif_link->mac_id;
|
2024-07-27 16:06:45 +08:00
|
|
|
sel->slot_idx = mcc_role->slot_idx;
|
2025-05-11 11:52:12 +08:00
|
|
|
sel->filled = true;
|
2024-07-27 16:06:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2025-05-11 11:52:12 +08:00
|
|
|
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;
|
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
if (!mcc_role->rtwvif_link->chanctx_assigned)
|
2024-07-27 16:06:45 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
rtw89_mcc_stop_sel_fill(sel, mcc_role);
|
2025-05-11 11:52:12 +08:00
|
|
|
return 0;
|
2024-07-27 16:06:45 +08:00
|
|
|
}
|
|
|
|
|
2025-05-11 11:52:12 +08:00
|
|
|
static void rtw89_mcc_stop(struct rtw89_dev *rtwdev,
|
|
|
|
const struct rtw89_chanctx_pause_parm *pause)
|
2023-08-16 16:21:33 +08:00
|
|
|
{
|
2025-05-11 11:52:13 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2023-08-31 13:31:33 +08:00
|
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
|
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
2025-07-10 12:24:13 +08:00
|
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
2025-05-11 11:52:12 +08:00
|
|
|
struct rtw89_mcc_stop_sel sel = {
|
|
|
|
.hint.target = pause ? pause->trigger : NULL,
|
|
|
|
};
|
2025-07-10 12:24:13 +08:00
|
|
|
bool rsn_scan;
|
2023-08-31 13:31:33 +08:00
|
|
|
int ret;
|
|
|
|
|
2025-05-11 11:52:13 +08:00
|
|
|
if (!pause) {
|
|
|
|
wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work);
|
|
|
|
bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES);
|
|
|
|
}
|
|
|
|
|
2025-07-10 12:24:13 +08:00
|
|
|
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;
|
|
|
|
|
2024-07-27 16:06:45 +08:00
|
|
|
/* by default, stop at ref */
|
|
|
|
rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel);
|
2025-05-11 11:52:12 +08:00
|
|
|
if (!sel.filled)
|
|
|
|
rtw89_mcc_stop_sel_fill(&sel, ref);
|
2024-07-27 16:06:45 +08:00
|
|
|
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at <macid %d>\n", sel.mac_id);
|
2023-08-31 13:31:33 +08:00
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev)) {
|
2024-07-27 16:06:45 +08:00
|
|
|
ret = rtw89_fw_h2c_mrc_del(rtwdev, mcc->group, sel.slot_idx);
|
2024-02-13 15:35:13 +08:00
|
|
|
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,
|
2024-07-27 16:06:45 +08:00
|
|
|
sel.mac_id, true);
|
2024-02-13 15:35:13 +08:00
|
|
|
if (ret)
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
|
|
"MCC h2c failed to trigger stop: %d\n", ret);
|
2023-08-31 13:31:33 +08:00
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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);
|
|
|
|
}
|
2023-08-31 13:31:33 +08:00
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP);
|
2023-09-08 11:11:45 +08:00
|
|
|
|
|
|
|
rtw89_mcc_stop_beacon_noa(rtwdev);
|
2025-07-10 12:24:10 +08:00
|
|
|
rtw89_fw_h2c_mcc_dig(rtwdev, RTW89_CHANCTX_0, 0, 0, false);
|
2025-07-10 12:24:11 +08:00
|
|
|
rtw89_phy_dig_resume(rtwdev, true);
|
2025-06-10 21:00:27 +08:00
|
|
|
|
|
|
|
rtw89_mcc_prepare(rtwdev, false);
|
2023-08-16 16:21:33 +08:00
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
static int rtw89_mcc_update(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
|
|
|
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
bool old_ref_ignore_bcn = mcc->role_ref.ignore_bcn;
|
|
|
|
bool old_aux_ignore_bcn = mcc->role_aux.ignore_bcn;
|
2023-09-08 11:11:42 +08:00
|
|
|
struct rtw89_mcc_config *config = &mcc->config;
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
2023-09-08 11:11:42 +08:00
|
|
|
struct rtw89_mcc_config old_cfg = *config;
|
2025-04-22 09:46:17 +08:00
|
|
|
bool courtesy_changed;
|
2023-09-08 11:11:42 +08:00
|
|
|
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;
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
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);
|
|
|
|
|
2025-04-22 09:46:17 +08:00
|
|
|
if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy,
|
|
|
|
sizeof(old_cfg.pattern.courtesy)) == 0)
|
|
|
|
courtesy_changed = false;
|
|
|
|
else
|
|
|
|
courtesy_changed = true;
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT ||
|
2025-04-22 09:46:17 +08:00
|
|
|
config->pattern.plan != RTW89_MCC_PLAN_NO_BT ||
|
|
|
|
courtesy_changed) {
|
2024-02-13 15:35:13 +08:00
|
|
|
if (rtw89_concurrent_via_mrc(rtwdev))
|
|
|
|
ret = __mrc_fw_start(rtwdev, true);
|
|
|
|
else
|
|
|
|
ret = __mcc_fw_start(rtwdev, true);
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
if (memcmp(&old_cfg.sync, &config->sync, sizeof(old_cfg.sync)) == 0)
|
|
|
|
sync_changed = false;
|
|
|
|
else
|
|
|
|
sync_changed = true;
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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);
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:45 +08:00
|
|
|
rtw89_mcc_handle_beacon_noa(rtwdev, true);
|
2023-09-08 11:11:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
static void rtw89_mcc_detect_connection(struct rtw89_dev *rtwdev,
|
|
|
|
struct rtw89_mcc_role *role)
|
|
|
|
{
|
|
|
|
struct ieee80211_vif *vif;
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
bool start_detect;
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
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,
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
"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;
|
|
|
|
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
vif = rtwvif_link_to_vif(role->rtwvif_link);
|
|
|
|
ieee80211_connection_loss(vif);
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
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;
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
struct rtw89_mcc_role *ref = &mcc->role_ref;
|
|
|
|
struct rtw89_mcc_role *aux = &mcc->role_aux;
|
2025-05-11 11:52:17 +08:00
|
|
|
u16 tolerance;
|
2023-09-08 11:11:42 +08:00
|
|
|
u16 bcn_ofst;
|
|
|
|
u16 diff;
|
|
|
|
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn)
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
rtw89_mcc_detect_connection(rtwdev, aux);
|
wifi: rtw89: mcc: solve GO's TBTT change and TBTT too close to NoA issue
For some implementation acting as GO under MCC(GO+STA), the GO's TBTT
might change after STA roams to another AP. This could result the new GO
beacon TX at the STA timeslot of GC+STA, causing GC beacon loss.
Therefore, if the GC detects beacon loss, it will pause MCC and remain
on the GO side for 100 TU to detect the new TBTT beacon.
Additionally, some implementation acting as GO under MCC might TX beacon
too close to the NoA period. The GC calculates timeslot pattern the TOB
(time offset behind) or TOA(time offset ahead) less than the minimum
RX beacon time, which leads to beacon loss. Therefore, disable the
beacon filter in this case. Then, if the GO's TBTT changed, the pattern
TOB/TOA greater than the minimum RX beacon time, the beacon filter should
be retriggered during MCC update.
Moreover, if the beacon filter is disabled initially but the GO timeslot
change, causing QoS null data detection fail, also pause MCC to detect new
TBTT beacon.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250710042423.73617-7-pkshih@realtek.com
2025-07-10 12:24:15 +08:00
|
|
|
else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn)
|
wifi: rtw89: mcc: adjust beacon filter when MCC and detect connection
MCC needs to wait at most 300ms to start. Additionally, if
scanning happens before MCC starts, it will miss some beacons,
which might cause beacon loss. Therefore, we reset beacon
filter when MCC start to let hardware reset beacon loss counter.
Additionally, GO is forbid to enter courtesy mode might cause
STA beacon loss. Therefore, disable beacon filter when GO+STA.
However, In WiFi 7 chip, even when GC+STA enable courtesy mode, the
beacon might loss because switching to courtesy timeslot will disable
TX/RX. If the TOB(time offset behind) or TOA(time offset ahead) is
too close to the edge of timeslot, the beacon might not be received.
Therefore, disable beacon filter when GC+STA in WiFi 7 chip.
Because disabling the beacon filter might prevent disconnection
when the AP power-off without sending a deauth. Therefore, driver
TX QOS nulldata periodically to detect the AP status, and the
connection is terminated if no ACK is received for 6 seconds.
Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250610130034.14692-5-pkshih@realtek.com
2025-06-10 21:00:26 +08:00
|
|
|
rtw89_mcc_detect_connection(rtwdev, ref);
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
if (mcc->mode != RTW89_MCC_MODE_GC_STA)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev);
|
2025-05-11 11:52:17 +08:00
|
|
|
if (bcn_ofst == config->beacon_offset)
|
|
|
|
return;
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
if (bcn_ofst > config->beacon_offset) {
|
|
|
|
diff = bcn_ofst - config->beacon_offset;
|
|
|
|
if (pattern->tob_aux < 0)
|
|
|
|
tolerance = -pattern->tob_aux;
|
2025-05-11 11:52:17 +08:00
|
|
|
else if (pattern->toa_aux > 0)
|
2023-09-08 11:11:42 +08:00
|
|
|
tolerance = pattern->toa_aux;
|
2025-05-11 11:52:17 +08:00
|
|
|
else
|
|
|
|
return; /* no chance to improve */
|
2023-09-08 11:11:42 +08:00
|
|
|
} else {
|
|
|
|
diff = config->beacon_offset - bcn_ofst;
|
|
|
|
if (pattern->toa_aux < 0)
|
|
|
|
tolerance = -pattern->toa_aux;
|
2025-05-11 11:52:17 +08:00
|
|
|
else if (pattern->tob_aux > 0)
|
2023-09-08 11:11:42 +08:00
|
|
|
tolerance = pattern->tob_aux;
|
2025-05-11 11:52:17 +08:00
|
|
|
else
|
|
|
|
return; /* no chance to improve */
|
2023-09-08 11:11:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (diff <= tolerance)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_BCN_OFFSET_CHANGE);
|
|
|
|
}
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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,
|
2024-09-16 13:31:52 +08:00
|
|
|
upd->rtwvif_link->mac_id,
|
2024-02-13 15:35:13 +08:00
|
|
|
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;
|
2024-09-16 13:31:52 +08:00
|
|
|
arg.macid = upd->rtwvif_link->mac_id;
|
2024-02-13 15:35:13 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:41 +08:00
|
|
|
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 = {
|
2024-09-16 13:31:52 +08:00
|
|
|
.rtwvif_link = mcc_role->rtwvif_link,
|
2023-09-08 11:11:41 +08:00
|
|
|
};
|
|
|
|
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;
|
|
|
|
|
2024-02-13 15:35:13 +08:00
|
|
|
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)
|
2023-09-08 11:11:41 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:43 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-07-10 12:24:10 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-01-22 14:03:02 +08:00
|
|
|
void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work)
|
2023-08-16 16:21:33 +08:00
|
|
|
{
|
|
|
|
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
|
|
|
|
chanctx_work.work);
|
2023-09-08 11:11:41 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2023-09-08 11:11:42 +08:00
|
|
|
bool update_mcc_pattern = false;
|
2023-08-16 16:21:33 +08:00
|
|
|
enum rtw89_entity_mode mode;
|
2023-09-08 11:11:41 +08:00
|
|
|
u32 changed = 0;
|
2023-08-16 16:21:33 +08:00
|
|
|
int ret;
|
2023-09-08 11:11:41 +08:00
|
|
|
int i;
|
2023-08-16 16:21:33 +08:00
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(wiphy);
|
2023-08-16 16:21:33 +08:00
|
|
|
|
2025-01-22 14:03:08 +08:00
|
|
|
if (hal->entity_pause)
|
2023-09-21 08:35:57 +08:00
|
|
|
return;
|
|
|
|
|
2023-09-08 11:11:41 +08:00
|
|
|
for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) {
|
|
|
|
if (test_and_clear_bit(i, hal->changes))
|
|
|
|
changed |= BIT(i);
|
|
|
|
}
|
|
|
|
|
2023-08-16 16:21:33 +08:00
|
|
|
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;
|
2023-09-08 11:11:41 +08:00
|
|
|
case RTW89_ENTITY_MODE_MCC:
|
2023-09-08 11:11:43 +08:00
|
|
|
if (changed & BIT(RTW89_CHANCTX_BCN_OFFSET_CHANGE) ||
|
2023-09-08 11:11:44 +08:00
|
|
|
changed & BIT(RTW89_CHANCTX_P2P_PS_CHANGE) ||
|
2023-09-08 11:11:45 +08:00
|
|
|
changed & BIT(RTW89_CHANCTX_BT_SLOT_CHANGE) ||
|
|
|
|
changed & BIT(RTW89_CHANCTX_TSF32_TOGGLE_CHANGE))
|
2023-09-08 11:11:42 +08:00
|
|
|
update_mcc_pattern = true;
|
2023-09-08 11:11:41 +08:00
|
|
|
if (changed & BIT(RTW89_CHANCTX_REMOTE_STA_CHANGE))
|
|
|
|
rtw89_mcc_update_macid_bitmap(rtwdev);
|
2023-09-08 11:11:43 +08:00
|
|
|
if (changed & BIT(RTW89_CHANCTX_P2P_PS_CHANGE))
|
|
|
|
rtw89_mcc_update_limit(rtwdev);
|
2023-09-08 11:11:44 +08:00
|
|
|
if (changed & BIT(RTW89_CHANCTX_BT_SLOT_CHANGE))
|
|
|
|
rtw89_mcc_fill_bt_role(rtwdev);
|
2023-09-08 11:11:42 +08:00
|
|
|
if (update_mcc_pattern) {
|
|
|
|
ret = rtw89_mcc_update(rtwdev);
|
|
|
|
if (ret)
|
|
|
|
rtw89_warn(rtwdev, "failed to update MCC: %d\n",
|
|
|
|
ret);
|
|
|
|
}
|
2023-09-08 11:11:41 +08:00
|
|
|
break;
|
2023-08-16 16:21:33 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:41 +08:00
|
|
|
void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
|
|
|
|
enum rtw89_chanctx_changes change)
|
2023-08-16 16:21:33 +08:00
|
|
|
{
|
2023-09-08 11:11:41 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2023-08-16 16:21:33 +08:00
|
|
|
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);
|
2025-07-10 12:24:11 +08:00
|
|
|
rtw89_phy_dig_suspend(rtwdev);
|
2023-08-16 16:21:33 +08:00
|
|
|
break;
|
2023-09-08 11:11:41 +08:00
|
|
|
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);
|
2023-08-16 16:21:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN,
|
|
|
|
"queue chanctx work for mode %d with delay %d us\n",
|
|
|
|
mode, delay);
|
2025-01-22 14:03:02 +08:00
|
|
|
wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work,
|
|
|
|
usecs_to_jiffies(delay));
|
2023-08-16 16:21:33 +08:00
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:41 +08:00
|
|
|
void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
|
|
|
rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_CHANGE_DFLT);
|
|
|
|
}
|
|
|
|
|
2025-06-11 11:55:14 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
void rtw89_chanctx_track(struct rtw89_dev *rtwdev)
|
|
|
|
{
|
2023-09-21 08:35:57 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2023-09-08 11:11:42 +08:00
|
|
|
enum rtw89_entity_mode mode;
|
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2023-09-08 11:11:42 +08:00
|
|
|
|
2023-09-21 08:35:57 +08:00
|
|
|
if (hal->entity_pause)
|
|
|
|
return;
|
|
|
|
|
2023-09-08 11:11:42 +08:00
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
|
|
switch (mode) {
|
|
|
|
case RTW89_ENTITY_MODE_MCC:
|
|
|
|
rtw89_mcc_track(rtwdev);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 08:35:57 +08:00
|
|
|
void rtw89_chanctx_pause(struct rtw89_dev *rtwdev,
|
2025-05-11 11:52:12 +08:00
|
|
|
const struct rtw89_chanctx_pause_parm *pause_parm)
|
2023-09-21 08:35:57 +08:00
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
enum rtw89_entity_mode mode;
|
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2023-09-21 08:35:57 +08:00
|
|
|
|
|
|
|
if (hal->entity_pause)
|
|
|
|
return;
|
|
|
|
|
2025-05-11 11:52:12 +08:00
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn);
|
2023-09-21 08:35:57 +08:00
|
|
|
|
|
|
|
mode = rtw89_get_entity_mode(rtwdev);
|
|
|
|
switch (mode) {
|
|
|
|
case RTW89_ENTITY_MODE_MCC:
|
2025-05-11 11:52:12 +08:00
|
|
|
rtw89_mcc_stop(rtwdev, pause_parm);
|
2023-09-21 08:35:57 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hal->entity_pause = true;
|
|
|
|
}
|
|
|
|
|
2024-12-31 08:48:08 +08:00
|
|
|
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)
|
2023-09-21 08:35:57 +08:00
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
enum rtw89_entity_mode mode;
|
|
|
|
int ret;
|
|
|
|
|
2025-01-22 14:03:07 +08:00
|
|
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
2023-09-21 08:35:57 +08:00
|
|
|
|
2024-12-31 08:48:08 +08:00
|
|
|
if (unlikely(!hal->entity_pause)) {
|
|
|
|
rtw89_chanctx_proceed_cb(rtwdev, cb_parm);
|
2023-09-21 08:35:57 +08:00
|
|
|
return;
|
2024-12-31 08:48:08 +08:00
|
|
|
}
|
2023-09-21 08:35:57 +08:00
|
|
|
|
|
|
|
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx proceed\n");
|
|
|
|
|
|
|
|
hal->entity_pause = false;
|
|
|
|
rtw89_set_channel(rtwdev);
|
|
|
|
|
2024-12-31 08:48:08 +08:00
|
|
|
rtw89_chanctx_proceed_cb(rtwdev, cb_parm);
|
|
|
|
|
2023-09-21 08:35:57 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
|
|
|
|
enum rtw89_chanctx_idx idx1,
|
|
|
|
enum rtw89_chanctx_idx idx2)
|
2024-02-06 11:06:20 +08:00
|
|
|
{
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
struct rtw89_vif *rtwvif;
|
2024-02-06 11:06:20 +08:00
|
|
|
u8 cur;
|
|
|
|
|
|
|
|
if (idx1 == idx2)
|
|
|
|
return;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
hal->chanctx[idx1].cfg->idx = idx2;
|
|
|
|
hal->chanctx[idx2].cfg->idx = idx1;
|
2024-02-06 11:06:20 +08:00
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
swap(hal->chanctx[idx1], hal->chanctx[idx2]);
|
2024-02-06 11:06:20 +08:00
|
|
|
|
wifi: rtw89: tweak driver architecture for impending MLO support
The drv_priv hooked to mac80211 become as below.
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_vif | <---> | rtw89_vif | -------> | rtw89_vif_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_vif_link |
+----------------+
(drv_priv) (instance-0)
+---------------+ +-----------+ +----------------+
| ieee80211_sta | <---> | rtw89_sta | -------> | rtw89_sta_link |
+---------------+ +-----------+ | +----------------+
|
| (instance-1)
| +----------------+
+---> | rtw89_sta_link |
+----------------+
The relation bewteen mac80211 link_id and our link instance is like below.
|\
(link_id) | \
0 -------- | |
1 -------- | | ------ instance-0 (link_id: X) -> work on HW band 0
2 -------- | |
... | | ------ instance-1 (link_id: Y) -> work on HW band 1
14 -------- | |
| /
|/
N.B. For cases of non-MLD connection, we set our link instance-0
active with link_id 0. So, our code flow can be compatible between
non-MLD connection and MLD connection.
Based on above, we tweak entire driver architecture first. But, we don't
dynamically enable multiple links here. That will be handled separately.
Most of the things changed here are changing flows to iterate all active
links and read bss_conf/link_sta data according to target link. And, for
cases of scan, ROC, WOW, we use instance-0 to deal with the request.
There are some things listed below, which work for now but need to extend
before multiple active links.
1. tx path
select suitable link instance among multiple active links
2. rx path
determine rx link by PPDU instead of always link instance-0
3. CAM
apply MLD pairwise key to any active links dynamically
Besides, PS code cannot easily work along with tweaking architecture. With
supporting MLO flag (currently false), we disable PS first and will fix it
by another commit in the following.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20240916053158.47350-8-pkshih@realtek.com
2024-09-16 13:31:58 +08:00
|
|
|
rtw89_for_each_rtwvif(rtwdev, rtwvif)
|
|
|
|
__rtw89_swap_chanctx(rtwvif, idx1, idx2);
|
2024-02-06 11:06:20 +08:00
|
|
|
|
2024-08-19 17:17:23 +08:00
|
|
|
cur = atomic_read(&hal->roc_chanctx_idx);
|
2024-02-06 11:06:20 +08:00
|
|
|
if (cur == idx1)
|
2024-08-19 17:17:23 +08:00
|
|
|
atomic_set(&hal->roc_chanctx_idx, idx2);
|
2024-02-06 11:06:20 +08:00
|
|
|
else if (cur == idx2)
|
2024-08-19 17:17:23 +08:00
|
|
|
atomic_set(&hal->roc_chanctx_idx, idx1);
|
2024-02-06 11:06:20 +08:00
|
|
|
}
|
|
|
|
|
2022-08-09 18:49:50 +08:00
|
|
|
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;
|
2022-08-09 18:49:51 +08:00
|
|
|
const struct rtw89_chip_info *chip = rtwdev->chip;
|
2022-08-09 18:49:50 +08:00
|
|
|
u8 idx;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
idx = find_first_zero_bit(hal->entity_map, NUM_OF_RTW89_CHANCTX);
|
2022-08-09 18:49:51 +08:00
|
|
|
if (idx >= chip->support_chanctx_num)
|
2022-08-09 18:49:50 +08:00
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
|
|
|
|
cfg->idx = idx;
|
2024-02-06 11:06:24 +08:00
|
|
|
cfg->ref_count = 0;
|
2024-07-27 16:06:46 +08:00
|
|
|
hal->chanctx[idx].cfg = cfg;
|
2022-08-09 18:49:50 +08:00
|
|
|
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;
|
2022-11-29 16:31:30 +08:00
|
|
|
|
2025-06-05 19:42:03 +08:00
|
|
|
rtw89_config_entity_chandef(rtwdev, cfg->idx, NULL);
|
2022-08-09 18:49:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2025-07-15 11:52:57 +08:00
|
|
|
|
|
|
|
if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
|
|
|
|
rtw89_chan_update_punctured(rtwdev, idx, &ctx->def);
|
2022-08-09 18:49:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link,
|
2022-08-09 18:49:50 +08:00
|
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
|
|
{
|
2022-11-29 16:31:30 +08:00
|
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
2024-10-22 16:31:04 +08:00
|
|
|
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
|
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
|
|
|
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
|
2024-02-06 11:06:24 +08:00
|
|
|
struct rtw89_entity_weight w = {};
|
2025-03-06 10:11:41 +08:00
|
|
|
int ret;
|
2022-11-29 16:31:30 +08:00
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
rtwvif_link->chanctx_idx = cfg->idx;
|
|
|
|
rtwvif_link->chanctx_assigned = true;
|
2024-02-06 11:06:22 +08:00
|
|
|
cfg->ref_count++;
|
2024-02-06 11:06:23 +08:00
|
|
|
|
2025-06-10 21:00:34 +08:00
|
|
|
if (rtwdev->scanning)
|
|
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
if (list_empty(&rtwvif->mgnt_entry))
|
|
|
|
list_add_tail(&rtwvif->mgnt_entry, &mgnt->active_list);
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
if (cfg->idx == RTW89_CHANCTX_0)
|
2024-02-06 11:06:24 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
rtw89_entity_calculate_weight(rtwdev, &w);
|
|
|
|
if (w.active_chanctxs != 1)
|
|
|
|
goto out;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
/* put the first active chanctx at RTW89_CHANCTX_0 */
|
|
|
|
rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0);
|
2024-02-06 11:06:24 +08:00
|
|
|
|
|
|
|
out:
|
2025-03-06 10:11:41 +08:00
|
|
|
ret = rtw89_set_channel(rtwdev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
rtw89_tas_reset(rtwdev, true);
|
|
|
|
|
|
|
|
return 0;
|
2022-08-09 18:49:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
|
2024-09-16 13:31:52 +08:00
|
|
|
struct rtw89_vif_link *rtwvif_link,
|
2022-08-09 18:49:50 +08:00
|
|
|
struct ieee80211_chanctx_conf *ctx)
|
|
|
|
{
|
2024-02-06 11:06:22 +08:00
|
|
|
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
|
2024-10-22 16:31:04 +08:00
|
|
|
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
|
2024-02-06 11:06:23 +08:00
|
|
|
struct rtw89_hal *hal = &rtwdev->hal;
|
2024-07-27 16:06:46 +08:00
|
|
|
enum rtw89_chanctx_idx roll;
|
2024-02-06 11:06:23 +08:00
|
|
|
enum rtw89_entity_mode cur;
|
2024-07-27 16:06:44 +08:00
|
|
|
enum rtw89_entity_mode new;
|
|
|
|
int ret;
|
2024-02-06 11:06:22 +08:00
|
|
|
|
2024-09-16 13:31:52 +08:00
|
|
|
rtwvif_link->chanctx_idx = RTW89_CHANCTX_0;
|
|
|
|
rtwvif_link->chanctx_assigned = false;
|
2024-02-06 11:06:22 +08:00
|
|
|
cfg->ref_count--;
|
2024-02-06 11:06:23 +08:00
|
|
|
|
2025-06-10 21:00:34 +08:00
|
|
|
if (rtwdev->scanning)
|
|
|
|
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
|
|
|
|
|
2024-10-22 16:31:04 +08:00
|
|
|
if (!rtw89_vif_is_active_role(rtwvif))
|
|
|
|
list_del_init(&rtwvif->mgnt_entry);
|
|
|
|
|
2024-02-06 11:06:23 +08:00
|
|
|
if (cfg->ref_count != 0)
|
|
|
|
goto out;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
if (cfg->idx != RTW89_CHANCTX_0)
|
2024-02-06 11:06:23 +08:00
|
|
|
goto out;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_CHANCTX,
|
2024-02-06 11:06:23 +08:00
|
|
|
cfg->idx + 1);
|
|
|
|
/* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */
|
2024-07-27 16:06:46 +08:00
|
|
|
if (roll == NUM_OF_RTW89_CHANCTX)
|
2024-02-06 11:06:23 +08:00
|
|
|
goto out;
|
|
|
|
|
2024-07-27 16:06:46 +08:00
|
|
|
/* RTW89_CHANCTX_0 is going to release, and another exists.
|
|
|
|
* Make another roll down to RTW89_CHANCTX_0 to replace.
|
2024-02-06 11:06:23 +08:00
|
|
|
*/
|
2024-07-27 16:06:46 +08:00
|
|
|
rtw89_swap_chanctx(rtwdev, cfg->idx, roll);
|
2024-02-06 11:06:23 +08:00
|
|
|
|
|
|
|
out:
|
2024-07-27 16:06:44 +08:00
|
|
|
if (!hal->entity_pause) {
|
|
|
|
cur = rtw89_get_entity_mode(rtwdev);
|
|
|
|
switch (cur) {
|
|
|
|
case RTW89_ENTITY_MODE_MCC:
|
2025-05-11 11:52:12 +08:00
|
|
|
rtw89_mcc_stop(rtwdev, NULL);
|
2024-07-27 16:06:44 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = rtw89_set_channel(rtwdev);
|
|
|
|
if (ret)
|
|
|
|
return;
|
2024-02-06 11:06:23 +08:00
|
|
|
|
2024-07-27 16:06:44 +08:00
|
|
|
if (hal->entity_pause)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new = rtw89_get_entity_mode(rtwdev);
|
|
|
|
switch (new) {
|
2024-02-06 11:06:23 +08:00
|
|
|
case RTW89_ENTITY_MODE_MCC:
|
2024-07-27 16:06:44 +08:00
|
|
|
/* re-plan MCC for chanctx changes. */
|
|
|
|
ret = rtw89_mcc_start(rtwdev);
|
|
|
|
if (ret)
|
|
|
|
rtw89_warn(rtwdev, "failed to start MCC: %d\n", ret);
|
2024-02-06 11:06:23 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2022-08-09 18:49:50 +08:00
|
|
|
}
|
2025-06-05 19:42:05 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-07-15 11:52:57 +08:00
|
|
|
_rtw89_chan_update_punctured(rtwdev, rtwvif_link, &new_ctx->def);
|
|
|
|
|
2025-06-05 19:42:05 +08:00
|
|
|
return 0;
|
|
|
|
}
|