[rdkb][common][bsp][Refactor and sync wifi from openwrt]
[Description]
3a2eef0b [MAC80211][Release][Update release note for Filogic 880/860 MLO Beta release]
cfbd2411 [MAC80211][Release][Filogic 880/860 MLO Beta release]
6c180e3f [MAC80211][WiFi7][misc][Add Eagle BE14000 efem default bin]
a55f34db [MAC80211][Release][Prepare for Filogic 880/860 release]
5b45ebca [MAC80211][WiFi7][hostapd][Add puncture bitmap to ucode]
95bbea73 [MAC80211][WiFi6][mt76][Add PID to only report data-frame TX rate]
b15ced26 [MAC80211][WiFi6][hostapd][Fix DFS channel selection issue]
d59133cb [MAC80211][WiFi6][mt76][Fix pse info not correct information]
3921b4b2 [MAC80211][WiFi6][mt76][Fix incomplete QoS-map setting to FW]
4e7690c7 [MAC80211][WiFi6/7][app][Change ATECHANNEL mapping cmd]
eb37af90 [MAC80211][WiFi7][app][Add support for per-packet bw & primary selection]
0ea82adf [MAC80211][WiFi6][core][Fix DFS CAC issue after CSA]
[Release-log]
Change-Id: I9bec97ec1b2e1c49ed43a812a07a5b21fcbb70a6
diff --git a/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch b/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch
new file mode 100644
index 0000000..b35da7b
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches-3.x/0182-mtk-mt76-mt7996-add-AP-affiliated-link-removal-suppo.patch
@@ -0,0 +1,564 @@
+From e61df790cc5ef66271a12e14af49f48ad097f7a6 Mon Sep 17 00:00:00 2001
+From: Shayne Chen <shayne.chen@mediatek.com>
+Date: Fri, 31 May 2024 18:14:59 +0800
+Subject: [PATCH 182/199] mtk: mt76: mt7996: add AP affiliated link removal
+ support
+
+Add support for ap link removal of MLD reconfiguration.
+
+Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
+---
+ mt76_connac_mcu.h | 3 +
+ mt7996/main.c | 68 ++++++++++++---
+ mt7996/mcu.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++
+ mt7996/mcu.h | 89 ++++++++++++++++++-
+ mt7996/mt7996.h | 2 +
+ 5 files changed, 367 insertions(+), 12 deletions(-)
+
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index 545de4ae..fd1bf1d1 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -1074,6 +1074,7 @@ enum {
+ MCU_UNI_EVENT_PP = 0x5a,
+ MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
+ MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
++ MCU_UNI_EVENT_MLD = 0x81,
+ };
+
+ #define MCU_UNI_CMD_EVENT BIT(1)
+@@ -1319,6 +1320,7 @@ enum {
+ MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
+ MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
++ MCU_UNI_CMD_MLD = 0x82,
+ };
+
+ enum {
+@@ -1393,6 +1395,7 @@ enum {
+ UNI_BSS_INFO_PM_DISABLE = 27,
+ UNI_BSS_INFO_BCN_CRIT_UPDATE = 32,
+ UNI_BSS_INFO_BCN_STA_PROF_CSA = 37,
++ UNI_BSS_INFO_BCN_ML_RECONF = 38,
+ };
+
+ enum {
+diff --git a/mt7996/main.c b/mt7996/main.c
+index ff0b9c0e..fc6f4ef9 100644
+--- a/mt7996/main.c
++++ b/mt7996/main.c
+@@ -319,6 +319,21 @@ static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
+ list_del_init(&mlink->wcid.poll_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
++ /* reassign a new bss wcid if the previous one was removed */
++ if (vif->txq && ieee80211_vif_is_mld(vif) &&
++ hweight16(vif->valid_links) > 1) {
++ struct mt76_txq *mtxq = (struct mt76_txq *)vif->txq->drv_priv;
++
++ if (mtxq->wcid == mlink->wcid.idx) {
++ u8 new_link = __ffs(vif->valid_links & ~BIT(link_id));
++ struct mt7996_link_sta *new_mlink =
++ mlink_dereference_protected(&mvif->sta, new_link);
++
++ if (new_mlink)
++ mtxq->wcid = new_mlink->wcid.idx;
++ }
++ }
++
+ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+
+ if (mlink != &mvif->sta.deflink)
+@@ -425,7 +440,7 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+- if (vif->txq) {
++ if (vif->txq && hweight16(vif->valid_links) <= 1) {
+ mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+ mtxq->wcid = idx;
+ }
+@@ -1128,6 +1143,8 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ {
+ struct ieee80211_sta *sta = link_sta->sta;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ u16 valid_links = sta->valid_links;
++ bool pri_changed;
+ int i;
+
+ if (!mlink)
+@@ -1148,6 +1165,7 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ mt7996_mac_twt_teardown_flow(dev, mlink, i);
+
+ rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
++ rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
+
+ spin_lock_bh(&dev->mt76.sta_poll_lock);
+ if (!list_empty(&mlink->wcid.poll_list))
+@@ -1156,17 +1174,41 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
+ list_del_init(&mlink->rc_list);
+ spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+- /* TODO: update primary link */
+- if (sta->valid_links) {
+- if (mlink->wcid.link_id == msta->pri_link)
+- msta->pri_link = msta->sec_link;
++ valid_links &= ~BIT(mlink->wcid.link_id);
++ if (!valid_links)
++ goto done;
+
+- if (sta->valid_links & ~(BIT(msta->pri_link)))
+- msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
+- else
+- msta->sec_link = msta->pri_link;
++ /* update primary and secondary link */
++ pri_changed = mlink->wcid.link_id == msta->pri_link;
++ if (pri_changed)
++ msta->pri_link = msta->sec_link;
++
++ if (valid_links & ~(BIT(msta->pri_link)))
++ msta->sec_link = __ffs(valid_links & ~(BIT(msta->pri_link)));
++ else
++ msta->sec_link = msta->pri_link;
++
++ if (pri_changed) {
++ struct mt7996_link_sta *mlink_new =
++ mlink_dereference_protected(msta, msta->pri_link);
++
++ if (!mlink_new)
++ goto done;
++
++ mlink_new->wcid.ampdu_state = mlink->wcid.ampdu_state;
++ for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
++ rcu_assign_pointer(mlink_new->wcid.aggr[i], mlink->wcid.aggr[i]);
++ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
++ struct mt76_txq *mtxq;
++
++ if (!sta->txq[i])
++ continue;
++ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
++ mtxq->wcid = mlink_new->wcid.idx;
++ }
+ }
+
++done:
+ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
+ mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
+@@ -3104,9 +3146,15 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mt76_vif_dbg(vif, "STA %pM old=0x%x, new=0x%x\n", sta->addr, old_links, new_links);
+ mutex_lock(&dev->mt76.mutex);
+
+- if (rem)
++ if (rem) {
+ mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+
++ /* Todo: update hw info of MLD STA */
++ /* ret = mt7996_mcu_add_mld_sta(dev, vif, sta, new_links); */
++ /* if (ret) */
++ /* goto remove; */
++ }
++
+ ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
+ if (ret)
+ goto remove;
+diff --git a/mt7996/mcu.c b/mt7996/mcu.c
+index 4310d35b..662e4f02 100644
+--- a/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -1350,6 +1350,48 @@ mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ }
+ }
+
++static void
++mt7996_mcu_mld_reconf_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
++{
++ struct mt7996_mld_event_data *data = priv;
++ struct mt7996_mcu_mld_ap_reconf_event *reconf = (void *)data->data;
++
++ if (!ether_addr_equal(vif->addr, data->mld_addr))
++ return;
++
++ ieee80211_links_removed(vif, le16_to_cpu(reconf->link_bitmap));
++}
++
++static void
++mt7996_mcu_mld_event(struct mt7996_dev *dev, struct sk_buff *skb)
++{
++ struct mt7996_mcu_mld_event *event = (void *)skb->data;
++ struct mt7996_mld_event_data data = {};
++ struct tlv *tlv;
++ int len;
++
++ memcpy(data.mld_addr, event->mld_addr, ETH_ALEN);
++ skb_pull(skb, sizeof(*event));
++ tlv = (struct tlv *)skb->data;
++ len = skb->len;
++
++ while (len > 0 && le16_to_cpu(tlv->len) <= len) {
++ switch (le16_to_cpu(tlv->tag)) {
++ case UNI_EVENT_MLD_RECONF_AP_REM_TIMER:
++ data.data = (u8 *)tlv;
++ ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
++ IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7996_mcu_mld_reconf_finish, &data);
++ break;
++ default:
++ break;
++ }
++
++ len -= le16_to_cpu(tlv->len);
++ tlv = (struct tlv *)((u8 *)(tlv) + le16_to_cpu(tlv->len));
++ }
++}
++
+ static void
+ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ {
+@@ -1378,6 +1420,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+ case MCU_UNI_EVENT_PP:
+ mt7996_mcu_pp_event(dev, skb);
+ break;
++ case MCU_UNI_EVENT_MLD:
++ mt7996_mcu_mld_event(dev, skb);
++ break;
+ #ifdef CONFIG_MTK_DEBUG
+ case MCU_UNI_EVENT_SR:
+ mt7996_mcu_rx_sr_event(dev, skb);
+@@ -3169,6 +3214,44 @@ out:
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+ }
+
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long add)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
++ u8 link_id;
++
++ if (!sta->mlo)
++ return 0;
++
++ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ struct mt7996_link_sta *mlink =
++ mlink_dereference_protected(msta, link_id);
++ struct sk_buff *skb;
++ int ret;
++
++ if (!mconf || !mlink)
++ continue;
++
++ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
++ &mlink->wcid,
++ MT7996_STA_UPDATE_MAX_SIZE);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++ /* starec mld setup */
++ mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta, add);
++ /* starec eht mld */
++ mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
++ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
++ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++
+ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
+ struct mt7996_bss_conf *mconf,
+ struct mt7996_link_sta *mlink)
+@@ -3381,6 +3464,51 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ &data, sizeof(data), true);
+ }
+
++static int
++mt7996_mcu_mld_reconf(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ u16 removed_links, u16 *removal_count)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
++ struct mld_req_hdr hdr = { .mld_idx = 0xff };
++ struct mld_reconf_timer *rt;
++ struct sk_buff *skb;
++ struct tlv *tlv;
++ int len = sizeof(hdr) + sizeof(*rt);
++ unsigned long rem = removed_links;
++ u8 link_id;
++
++ memcpy(hdr.mld_addr, vif->addr, ETH_ALEN);
++
++ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
++ if (!skb)
++ return -ENOMEM;
++
++ skb_put_data(skb, &hdr, sizeof(hdr));
++
++ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_AP_REM_TIMER, sizeof(*rt));
++ rt = (struct mld_reconf_timer *)tlv;
++ rt->link_bitmap = cpu_to_le16(removed_links);
++
++ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct ieee80211_bss_conf *conf =
++ link_conf_dereference_protected(vif, link_id);
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++ u8 band_idx;
++ u16 to_sec;
++
++ if (!conf || !mconf)
++ continue;
++
++ band_idx = mconf->phy->mt76->band_idx;
++ to_sec = conf->beacon_int * removal_count[link_id] / 1000;
++ rt->to_sec[band_idx] = cpu_to_le16(to_sec);
++ rt->bss_idx[band_idx] = mconf->mt76.idx;
++ }
++
++ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), true);
++}
++
+ static void
+ mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
+ struct sk_buff *skb,
+@@ -3575,6 +3703,94 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
+ mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_TX, 0);
+ }
+
++static void
++mt7996_mcu_beacon_ml_reconf(struct mt7996_dev *dev,
++ struct ieee80211_bss_conf *conf,
++ struct sk_buff *rskb, struct sk_buff *skb,
++ struct ieee80211_mutable_offsets *offs)
++{
++ struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
++ struct bss_bcn_ml_reconf_tlv *reconf;
++ struct bss_bcn_ml_reconf_offset *reconf_offs;
++ const struct element *elem, *sub;
++ struct tlv *tlv;
++ u16 removal_offs[IEEE80211_MLD_MAX_NUM_LINKS] = {};
++ u16 removal_count[IEEE80211_MLD_MAX_NUM_LINKS] = {};
++ u16 tail_offset = offs->tim_offset + offs->tim_length;
++ unsigned long removed_links = 0;
++ bool has_reconf = false;
++ u8 link_id, *beacon_tail = skb->data + tail_offset;
++
++ if (!ieee80211_vif_is_mld(conf->vif))
++ return;
++
++ /* TODO: currently manually parse reconf info directly from the IE, it
++ * is expected to be passed from upper layer in the future.
++ */
++ for_each_element_extid(elem, WLAN_EID_EXT_EHT_MULTI_LINK,
++ beacon_tail, skb->len - tail_offset) {
++ if (ieee80211_mle_type_ok(elem->data + 1,
++ IEEE80211_ML_CONTROL_TYPE_RECONF,
++ elem->datalen - 1)) {
++ has_reconf = true;
++ break;
++ }
++ }
++
++ if (!has_reconf)
++ return;
++
++ for_each_mle_subelement(sub, elem->data + 1, elem->datalen - 1) {
++ struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
++ u8 *pos = prof->variable;
++ u16 control;
++
++ if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
++ continue;
++
++ if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
++ sub->datalen))
++ return;
++
++ control = le16_to_cpu(prof->control);
++ link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
++
++ removed_links |= BIT(link_id);
++
++ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
++ pos += 6;
++
++ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT) {
++ removal_offs[link_id] = pos - skb->data;
++ removal_count[link_id] = le16_to_cpu(*(__le16 *)pos);
++ }
++ }
++
++ if (!removed_links)
++ return;
++
++ /* the first link to be removed */
++ if (conf->link_id == ffs(removed_links) - 1)
++ mt7996_mcu_mld_reconf(dev, conf->vif, removed_links, removal_count);
++
++ tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_ML_RECONF,
++ sizeof(*reconf) +
++ sizeof(*reconf_offs) * hweight16(removed_links));
++ reconf = (struct bss_bcn_ml_reconf_tlv *)tlv;
++ reconf->reconf_count = hweight16(removed_links);
++
++ reconf_offs = (struct bss_bcn_ml_reconf_offset *)reconf->offset;
++ for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) {
++ struct mt7996_bss_conf *mconf =
++ mconf_dereference_protected(mvif, link_id);
++
++ reconf_offs->ap_removal_timer_offs =
++ cpu_to_le16(removal_offs[link_id]);
++ reconf_offs->bss_idx = mconf->mt76.idx;
++ reconf_offs++;
++ }
++}
++
+ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_bss_conf *conf,
+ struct mt7996_bss_conf *mconf, int en)
+@@ -3628,6 +3844,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
+ mt7996_mcu_beacon_sta_prof_csa(rskb, conf, &offs);
+ mt7996_mcu_beacon_crit_update(rskb, skb, conf, mconf, &offs);
++ mt7996_mcu_beacon_ml_reconf(dev, conf, rskb, skb, &offs);
+ out:
+ dev_kfree_skb(skb);
+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+diff --git a/mt7996/mcu.h b/mt7996/mcu.h
+index 739e357c..7c559d2b 100644
+--- a/mt7996/mcu.h
++++ b/mt7996/mcu.h
+@@ -546,6 +546,20 @@ struct bss_bcn_sta_prof_cntdwn_tlv {
+ u8 pkt_content[9];
+ } __packed;
+
++struct bss_bcn_ml_reconf_tlv {
++ __le16 tag;
++ __le16 len;
++ u8 reconf_count;
++ u8 rsv[3];
++ u8 offset[];
++} __packed;
++
++struct bss_bcn_ml_reconf_offset {
++ __le16 ap_removal_timer_offs;
++ u8 bss_idx;
++ u8 rsv;
++} __packed;
++
+ struct bss_txcmd_tlv {
+ __le16 tag;
+ __le16 len;
+@@ -594,6 +608,17 @@ struct bss_mld_tlv {
+ u8 __rsv[3];
+ } __packed;
+
++struct bss_mld_link_op_tlv {
++ __le16 tag;
++ __le16 len;
++ u8 group_mld_id;
++ u8 own_mld_id;
++ u8 mac_addr[ETH_ALEN];
++ u8 remap_idx;
++ u8 link_operation;
++ u8 rsv[2];
++} __packed;
++
+ struct sta_rec_ht_uni {
+ __le16 tag;
+ __le16 len;
+@@ -1003,8 +1028,9 @@ enum {
+ sizeof(struct bss_bcn_cntdwn_tlv) + \
+ sizeof(struct bss_bcn_mbss_tlv) + \
+ sizeof(struct bss_bcn_crit_update_tlv) + \
+- sizeof(struct bss_bcn_sta_prof_cntdwn_tlv)) \
+-
++ sizeof(struct bss_bcn_sta_prof_cntdwn_tlv) + \
++ sizeof(struct bss_bcn_ml_reconf_tlv) + \
++ 3 * sizeof(struct bss_bcn_ml_reconf_offset))
+ #define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \
+ MT7996_BEACON_UPDATE_SIZE)
+
+@@ -1105,6 +1131,65 @@ enum {
+ UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
+ };
+
++struct mld_req_hdr {
++ u8 ver;
++ u8 mld_addr[ETH_ALEN];
++ u8 mld_idx;
++ u8 flag;
++ u8 rsv[3];
++ u8 buf[];
++} __packed;
++
++struct mld_reconf_timer {
++ __le16 tag;
++ __le16 len;
++ __le16 link_bitmap;
++ __le16 to_sec[__MT_MAX_BAND]; /* timeout of reconf (second) */
++ u8 bss_idx[__MT_MAX_BAND];
++ u8 rsv;
++} __packed;
++
++struct mld_reconf_stop_link {
++ __le16 tag;
++ __le16 len;
++ __le16 link_bitmap;
++ u8 rsv[2];
++ u8 bss_idx[16];
++} __packed;
++
++enum {
++ UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03,
++};
++
++struct mt7996_mcu_mld_event {
++ struct mt7996_mcu_rxd rxd;
++
++ /* fixed field */
++ u8 ver;
++ u8 mld_addr[ETH_ALEN];
++ u8 mld_idx;
++ u8 rsv[4];
++ /* tlv */
++ u8 buf[];
++} __packed;
++
++struct mt7996_mld_event_data {
++ u8 mld_addr[ETH_ALEN];
++ u8 *data;
++};
++
++struct mt7996_mcu_mld_ap_reconf_event {
++ __le16 tag;
++ __le16 len;
++ __le16 link_bitmap;
++ u8 bss_idx[3];
++ u8 rsv[3];
++} __packed;
++
++enum {
++ UNI_EVENT_MLD_RECONF_AP_REM_TIMER = 0x04,
++};
++
+ struct tx_power_ctrl {
+ u8 _rsv[4];
+
+diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
+index 8b00b05b..6f9f82b7 100644
+--- a/mt7996/mt7996.h
++++ b/mt7996/mt7996.h
+@@ -1083,6 +1083,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
+ struct mt7996_bss_conf *mconf,
+ struct ieee80211_link_sta *link_sta,
+ struct mt7996_link_sta *mlink, bool changed);
++int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, unsigned long add);
+ int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
+ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta);
+ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
+--
+2.18.0
+