wifi: mt76: mt7996: extend CSA and CCA support for MLO

Use correct link_id to report CSA and CCA countdown events, and also
modify mt7996_channel_switch_beacon() to set beacon with the correct
link_id.

Co-developed-by: Peter Chiu <[email protected]>
Signed-off-by: Peter Chiu <[email protected]>
Co-developed-by: StanleyYP Wang <[email protected]>
Signed-off-by: StanleyYP Wang <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
This commit is contained in:
Shayne Chen
2026-01-27 10:02:43 +00:00
committed by Felix Fietkau
parent 3c232526a0
commit 12c099c526
3 changed files with 131 additions and 59 deletions
+22 -1
View File
@@ -926,9 +926,30 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band);
struct ieee80211_bss_conf *link_conf;
unsigned int link_id;
mutex_lock(&dev->mt76.mutex); mutex_lock(&dev->mt76.mutex);
mt7996_mcu_add_beacon(hw, vif, &vif->bss_conf, vif->bss_conf.enable_beacon);
for_each_vif_active_link(vif, link_conf, link_id) {
struct mt7996_vif_link *link =
mt7996_vif_link(dev, vif, link_id);
if (!link || link->phy != phy)
continue;
/* Reset beacon when channel switch triggered during CAC to let
* FW correctly perform CSA countdown
*/
if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->mt76->chandef,
vif->type))
mt7996_mcu_add_beacon(hw, vif, link_conf, false);
mt7996_mcu_add_beacon(hw, vif, link_conf, true);
break;
}
mutex_unlock(&dev->mt76.mutex); mutex_unlock(&dev->mt76.mutex);
} }
+106 -53
View File
@@ -390,13 +390,117 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
sizeof(req), false); sizeof(req), false);
} }
struct mt7996_mcu_countdown_data {
struct mt76_phy *mphy;
u8 omac_idx;
};
static void static void
mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{ {
if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) struct mt7996_mcu_countdown_data *cdata = (void *)priv;
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct ieee80211_bss_conf *link_conf = NULL;
unsigned long valid_links = vif->valid_links ?: BIT(0);
unsigned int link_id;
if (vif->type == NL80211_IFTYPE_STATION)
return; return;
ieee80211_csa_finish(vif, 0); for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt76_vif_link *mlink =
rcu_dereference(mvif->mt76.link[link_id]);
if (mlink && mlink->band_idx == cdata->mphy->band_idx &&
mlink->omac_idx == cdata->omac_idx) {
link_conf = rcu_dereference(vif->link_conf[link_id]);
break;
}
}
if (!link_conf || !link_conf->csa_active)
return;
ieee80211_csa_finish(vif, link_conf->link_id);
}
static void
mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
struct mt7996_mcu_countdown_data *cdata = (void *)priv;
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct ieee80211_bss_conf *link_conf = NULL;
unsigned long valid_links = vif->valid_links ?: BIT(0);
unsigned int link_id;
if (vif->type == NL80211_IFTYPE_STATION)
return;
for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct mt76_vif_link *mlink =
rcu_dereference(mvif->mt76.link[link_id]);
if (mlink && mlink->band_idx == cdata->mphy->band_idx &&
mlink->omac_idx == cdata->omac_idx) {
link_conf = rcu_dereference(vif->link_conf[link_id]);
break;
}
}
if (!link_conf || !link_conf->color_change_active)
return;
ieee80211_color_change_finish(vif, link_conf->link_id);
}
static void
mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
{
#define UNI_EVENT_IE_COUNTDOWN_CSA 0
#define UNI_EVENT_IE_COUNTDOWN_BCC 1
struct header {
u8 band;
u8 rsv[3];
};
struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
const char *data = (char *)&rxd[1], *tail;
struct header *hdr = (struct header *)data;
struct tlv *tlv = (struct tlv *)(data + 4);
struct mt7996_mcu_countdown_notify *event;
struct mt7996_mcu_countdown_data cdata;
if (hdr->band >= ARRAY_SIZE(dev->mt76.phys))
return;
cdata.mphy = dev->mt76.phys[hdr->band];
if (!cdata.mphy)
return;
tail = skb->data + skb->len;
data += sizeof(*hdr);
while (data + sizeof(*tlv) < tail && le16_to_cpu(tlv->len)) {
event = (struct mt7996_mcu_countdown_notify *)tlv->data;
cdata.omac_idx = event->omac_idx;
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_IE_COUNTDOWN_CSA:
ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7996_mcu_csa_finish, &cdata);
break;
case UNI_EVENT_IE_COUNTDOWN_BCC:
ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7996_mcu_cca_finish, &cdata);
break;
default:
break;
}
data += le16_to_cpu(tlv->len);
tlv = (struct tlv *)data;
}
} }
static void static void
@@ -476,57 +580,6 @@ out:
wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data);
} }
static void
mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
return;
ieee80211_color_change_finish(vif, 0);
}
static void
mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
{
#define UNI_EVENT_IE_COUNTDOWN_CSA 0
#define UNI_EVENT_IE_COUNTDOWN_BCC 1
struct header {
u8 band;
u8 rsv[3];
};
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
const char *data = (char *)&rxd[1], *tail;
struct header *hdr = (struct header *)data;
struct tlv *tlv = (struct tlv *)(data + 4);
if (hdr->band >= ARRAY_SIZE(dev->mt76.phys))
return;
if (hdr->band && dev->mt76.phys[hdr->band])
mphy = dev->mt76.phys[hdr->band];
tail = skb->data + skb->len;
data += sizeof(struct header);
while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_IE_COUNTDOWN_CSA:
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7996_mcu_csa_finish, mphy->hw);
break;
case UNI_EVENT_IE_COUNTDOWN_BCC:
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7996_mcu_cca_finish, mphy->hw);
break;
}
data += le16_to_cpu(tlv->len);
tlv = (struct tlv *)data;
}
}
static int static int
mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate) mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate)
{ {
+3 -5
View File
@@ -52,12 +52,10 @@ struct mt7996_mcu_thermal_enable {
u8 rsv[2]; u8 rsv[2];
} __packed; } __packed;
struct mt7996_mcu_csa_notify { struct mt7996_mcu_countdown_notify {
struct mt7996_mcu_rxd rxd;
u8 omac_idx; u8 omac_idx;
u8 csa_count; u8 count;
u8 band_idx; u8 csa_failure_reason; /* 0: success, 1: beacon disabled */
u8 rsv; u8 rsv;
} __packed; } __packed;