Handle the UNI STAREC event (0x0f) carrying the MLO link-state TLV (tag 0x27) and fold the reported ACTIVE/INACTIVE state into vif->active_links via ieee80211_set_active_links_async(). Signed-off-by: Emery Hsin --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + .../net/wireless/mediatek/mt76/mt7925/main.c | 2 + .../net/wireless/mediatek/mt76/mt7925/mcu.c | 93 +++++++++++++++++++ .../wireless/mediatek/mt76/mt7925/mt7925.h | 23 +++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 2 + 5 files changed, 121 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 2acaae9830fb..5c27285542ab 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1098,6 +1098,7 @@ enum { MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c, MCU_UNI_EVENT_PS_SYNC = 0x0d, MCU_UNI_EVENT_SCAN_DONE = 0x0e, + MCU_UNI_EVENT_STAREC = 0x0f, MCU_UNI_EVENT_RDD_REPORT = 0x11, MCU_UNI_EVENT_SAP = 0x1c, MCU_UNI_EVENT_ROC = 0x27, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 00244a650464..2b1346044c5a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -484,6 +484,7 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mvif->sta.vif = mvif; mvif->deflink_id = IEEE80211_LINK_UNSPECIFIED; mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; + mvif->delay_inactive_links = U16_MAX; mvif->valid_links = 0; ret = mt7925_mac_link_bss_add(dev, &vif->bss_conf, &mvif->sta.deflink, @@ -1889,6 +1890,7 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, } mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; + mvif->delay_inactive_links = U16_MAX; } EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index b502f93166e5..2c2ab85180c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -805,6 +805,96 @@ mt7925_mcu_handle_mbmc_event(struct mt792x_dev *dev, struct sk_buff *skb) } } +/* Apply the link state reported by firmware to the vif active links. */ +static void +mt7925_mcu_starec_link_state(struct mt792x_dev *dev, u16 wlan_idx, + struct mt7925_starec_mlo_link_state_tlv *state) +{ + struct mt792x_link_sta *mlink; + struct ieee80211_vif *vif; + struct mt792x_vif *mvif; + struct mt792x_sta *msta; + u16 old_links, new_links; + struct mt76_wcid *wcid; + u8 link_id; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wlan_idx); + if (!wcid || !wcid->sta) + goto out; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + msta = mlink->sta; + if (!msta || !msta->vif) + goto out; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + if (!ieee80211_vif_is_mld(vif)) + goto out; + + mvif = (struct mt792x_vif *)vif->drv_priv; + link_id = wcid->link_id; + old_links = vif->active_links; + + if (state->link_state == MT7925_MLO_LINK_STATE_ACTIVE) { + new_links = old_links | BIT(link_id); + mvif->delay_inactive_links |= BIT(link_id); + } else { + new_links = old_links & ~BIT(link_id); + } + + /* Links held back from being deactivated, so a back-to-back + * INACTIVE+ACTIVE pair does not briefly leave the vif linkless. + * Never ask for an empty set: mac80211 drops such a request and the + * vif would stay out of sync with the firmware. + */ + if (!new_links || !(new_links & mvif->delay_inactive_links)) { + mvif->delay_inactive_links &= ~BIT(link_id); + } else if (new_links != old_links) { + new_links &= mvif->delay_inactive_links; + mvif->delay_inactive_links = U16_MAX; + ieee80211_set_active_links_async(vif, new_links); + } + +out: + rcu_read_unlock(); +} + +static void +mt7925_mcu_starec_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7925_uni_starec_event *event; + struct tlv *tlv; + u16 wlan_idx; + u32 tlv_len; + + if (skb->len < sizeof(struct mt7925_mcu_rxd) + sizeof(event->hdr)) + return; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd)); + event = (struct mt7925_uni_starec_event *)skb->data; + wlan_idx = le16_to_cpu(event->hdr.wlan_idx); + + tlv = (struct tlv *)event->tlv; + tlv_len = skb->len - sizeof(event->hdr); + + mt7925_for_each_tlv(tlv, tlv_len) { + switch (le16_to_cpu(tlv->tag)) { + case MT7925_STAREC_TAG_MLO_LINK_STATE: + if (le16_to_cpu(tlv->len) < + sizeof(struct mt7925_starec_mlo_link_state_tlv)) + break; + + mt7925_mcu_starec_link_state(dev, wlan_idx, + (void *)tlv); + break; + default: + break; + } + } +} + static void mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) @@ -841,6 +931,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, case MCU_UNI_EVENT_BSS_IS_ABSENCE: mt7925_p2p_mcu_bss_is_absence_event(dev, skb); break; + case MCU_UNI_EVENT_STAREC: + mt7925_mcu_starec_event(dev, skb); + break; case MCU_UNI_EVENT_RSSI_MONITOR: mt7925_mcu_rssi_monitor_event(dev, skb); break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index d829eb9686c7..5a46a6a45e0c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -133,6 +133,29 @@ struct mt7925_uni_beacon_loss_event { struct mt7925_beacon_loss_tlv beacon_loss; } __packed; +/* STAREC event: MLO link state */ +#define MT7925_STAREC_TAG_MLO_LINK_STATE 0x27 + +/* link_state values */ +#define MT7925_MLO_LINK_STATE_ACTIVE 0 +#define MT7925_MLO_LINK_STATE_INACTIVE 1 + +struct mt7925_starec_mlo_link_state_tlv { + __le16 tag; + __le16 len; + u8 link_state; + u8 reason; + u8 pad[2]; +} __packed; + +struct mt7925_uni_starec_event { + struct { + __le16 wlan_idx; + u8 pad[2]; + } __packed hdr; + u8 tlv[]; +} __packed; + struct mt7925_uni_rssi_monitor_event { __le16 tag; __le16 len; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 96d4dfe8ee45..f1a0a296ae0f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -220,6 +220,8 @@ struct mt792x_vif { struct mt792x_phy *phy; bool roc_join_held; /* join ROC kept until the STA is authorized */ u16 valid_links; + /* Links held back from being deactivated, U16_MAX when none is held. */ + u16 delay_inactive_links; u8 deflink_id; enum mt792x_mlo_pm_state mlo_pm_state; -- 2.45.2