wifi: mt76: mt7996: Enable MLO support for client interfaces

Report MT7996 MLO STA capabilities to mac80211 stack.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20250901-mt7996-enable-mlo-client-v1-1-50c46317325d@kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Lorenzo Bianconi 2025-09-01 15:02:33 +02:00 committed by Felix Fietkau
parent 24e2846f15
commit f5160304d5
5 changed files with 88 additions and 57 deletions

View File

@ -2060,3 +2060,55 @@ void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif)
mt76_abort_roc(mvif->roc_phy);
}
EXPORT_SYMBOL_GPL(mt76_vif_cleanup);
u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links)
{
unsigned long usable_links = ieee80211_vif_usable_links(vif);
struct {
u8 link_id;
enum nl80211_band band;
} data[IEEE80211_MLD_MAX_NUM_LINKS];
unsigned int link_id;
int i, n_data = 0;
u16 sel_links = 0;
if (!ieee80211_vif_is_mld(vif))
return 0;
if (vif->active_links == usable_links)
return vif->active_links;
rcu_read_lock();
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_bss_conf *link_conf;
link_conf = rcu_dereference(vif->link_conf[link_id]);
if (WARN_ON_ONCE(!link_conf))
continue;
data[n_data].link_id = link_id;
data[n_data].band = link_conf->chanreq.oper.chan->band;
n_data++;
}
rcu_read_unlock();
for (i = 0; i < n_data; i++) {
int j;
if (!(BIT(data[i].link_id) & vif->active_links))
continue;
sel_links = BIT(data[i].link_id);
for (j = 0; j < n_data; j++) {
if (data[i].band != data[j].band) {
sel_links |= BIT(data[j].link_id);
if (hweight16(sel_links) == max_active_links)
break;
}
}
break;
}
return sel_links;
}
EXPORT_SYMBOL_GPL(mt76_select_links);

View File

@ -1872,6 +1872,7 @@ mt76_vif_init(struct ieee80211_vif *vif, struct mt76_vif_data *mvif)
}
void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif);
u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links);
static inline struct mt76_vif_link *
mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id)

View File

@ -988,56 +988,6 @@ int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
}
EXPORT_SYMBOL_GPL(mt7925_mac_sta_add);
static u16
mt7925_mac_select_links(struct mt76_dev *mdev, struct ieee80211_vif *vif)
{
unsigned long usable_links = ieee80211_vif_usable_links(vif);
struct {
u8 link_id;
enum nl80211_band band;
} data[IEEE80211_MLD_MAX_NUM_LINKS];
u8 link_id, i, j, n_data = 0;
u16 sel_links = 0;
if (!ieee80211_vif_is_mld(vif))
return 0;
if (vif->active_links == usable_links)
return vif->active_links;
rcu_read_lock();
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_bss_conf *link_conf =
rcu_dereference(vif->link_conf[link_id]);
if (WARN_ON_ONCE(!link_conf))
continue;
data[n_data].link_id = link_id;
data[n_data].band = link_conf->chanreq.oper.chan->band;
n_data++;
}
rcu_read_unlock();
for (i = 0; i < n_data; i++) {
if (!(BIT(data[i].link_id) & vif->active_links))
continue;
sel_links = BIT(data[i].link_id);
for (j = 0; j < n_data; j++) {
if (data[i].band != data[j].band) {
sel_links |= BIT(data[j].link_id);
break;
}
}
break;
}
return sel_links;
}
static void
mt7925_mac_set_links(struct mt76_dev *mdev, struct ieee80211_vif *vif)
{
@ -1048,7 +998,7 @@ mt7925_mac_set_links(struct mt76_dev *mdev, struct ieee80211_vif *vif)
struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper;
enum nl80211_band band = chandef->chan->band, secondary_band;
u16 sel_links = mt7925_mac_select_links(mdev, vif);
u16 sel_links = mt76_select_links(vif, 2);
u8 secondary_link_id = __ffs(~BIT(mvif->deflink_id) & sel_links);
if (!ieee80211_vif_is_mld(vif) || hweight16(sel_links) < 2)

View File

@ -79,6 +79,14 @@ static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = {
.mld_capa_and_ops =
FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS,
MT7996_MAX_RADIOS - 1),
}, {
.iftype = NL80211_IFTYPE_STATION,
.extended_capabilities = if_types_ext_capa_ap,
.extended_capabilities_mask = if_types_ext_capa_ap,
.extended_capabilities_len = sizeof(if_types_ext_capa_ap),
.mld_capa_and_ops =
FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS,
MT7996_MAX_RADIOS - 1),
},
};

View File

@ -1220,6 +1220,24 @@ mt7996_mac_sta_remove(struct mt7996_dev *dev, struct ieee80211_vif *vif,
mutex_unlock(&dev->mt76.mutex);
}
static void
mt7996_set_active_links(struct ieee80211_vif *vif)
{
u16 active_links;
if (vif->type != NL80211_IFTYPE_STATION)
return;
if (!ieee80211_vif_is_mld(vif))
return;
active_links = mt76_select_links(vif, MT7996_MAX_RADIOS);
if (hweight16(active_links) < 2)
return;
ieee80211_set_active_links_async(vif, active_links);
}
static int
mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, enum ieee80211_sta_state old_state,
@ -1237,16 +1255,18 @@ mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mt7996_mac_sta_remove(dev, vif, sta);
if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC)
new_state == IEEE80211_STA_ASSOC) {
mt7996_set_active_links(vif);
ev = MT76_STA_EVENT_ASSOC;
else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED)
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
ev = MT76_STA_EVENT_AUTHORIZE;
else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH)
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
ev = MT76_STA_EVENT_DISASSOC;
else
} else {
return 0;
}
return mt7996_mac_sta_event(dev, vif, sta, ev);
}