blob: b35da7b607ea2fa315e0b7534ad9c778108d3c95 [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From e61df790cc5ef66271a12e14af49f48ad097f7a6 Mon Sep 17 00:00:00 2001
2From: Shayne Chen <shayne.chen@mediatek.com>
3Date: Fri, 31 May 2024 18:14:59 +0800
4Subject: [PATCH 182/199] mtk: mt76: mt7996: add AP affiliated link removal
5 support
6
7Add support for ap link removal of MLD reconfiguration.
8
9Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
10---
11 mt76_connac_mcu.h | 3 +
12 mt7996/main.c | 68 ++++++++++++---
13 mt7996/mcu.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++
14 mt7996/mcu.h | 89 ++++++++++++++++++-
15 mt7996/mt7996.h | 2 +
16 5 files changed, 367 insertions(+), 12 deletions(-)
17
18diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
19index 545de4ae..fd1bf1d1 100644
20--- a/mt76_connac_mcu.h
21+++ b/mt76_connac_mcu.h
22@@ -1074,6 +1074,7 @@ enum {
23 MCU_UNI_EVENT_PP = 0x5a,
24 MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
25 MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
26+ MCU_UNI_EVENT_MLD = 0x81,
27 };
28
29 #define MCU_UNI_CMD_EVENT BIT(1)
30@@ -1319,6 +1320,7 @@ enum {
31 MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
32 MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
33 MCU_UNI_CMD_PTA_3WIRE_CTRL = 0x78,
34+ MCU_UNI_CMD_MLD = 0x82,
35 };
36
37 enum {
38@@ -1393,6 +1395,7 @@ enum {
39 UNI_BSS_INFO_PM_DISABLE = 27,
40 UNI_BSS_INFO_BCN_CRIT_UPDATE = 32,
41 UNI_BSS_INFO_BCN_STA_PROF_CSA = 37,
42+ UNI_BSS_INFO_BCN_ML_RECONF = 38,
43 };
44
45 enum {
46diff --git a/mt7996/main.c b/mt7996/main.c
47index ff0b9c0e..fc6f4ef9 100644
48--- a/mt7996/main.c
49+++ b/mt7996/main.c
50@@ -319,6 +319,21 @@ static void mt7996_remove_bss_conf(struct ieee80211_vif *vif,
51 list_del_init(&mlink->wcid.poll_list);
52 spin_unlock_bh(&dev->mt76.sta_poll_lock);
53
54+ /* reassign a new bss wcid if the previous one was removed */
55+ if (vif->txq && ieee80211_vif_is_mld(vif) &&
56+ hweight16(vif->valid_links) > 1) {
57+ struct mt76_txq *mtxq = (struct mt76_txq *)vif->txq->drv_priv;
58+
59+ if (mtxq->wcid == mlink->wcid.idx) {
60+ u8 new_link = __ffs(vif->valid_links & ~BIT(link_id));
61+ struct mt7996_link_sta *new_mlink =
62+ mlink_dereference_protected(&mvif->sta, new_link);
63+
64+ if (new_mlink)
65+ mtxq->wcid = new_mlink->wcid.idx;
66+ }
67+ }
68+
69 mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
70
71 if (mlink != &mvif->sta.deflink)
72@@ -425,7 +440,7 @@ static int mt7996_add_bss_conf(struct mt7996_phy *phy,
73 mt7996_mac_wtbl_update(dev, idx,
74 MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
75
76- if (vif->txq) {
77+ if (vif->txq && hweight16(vif->valid_links) <= 1) {
78 mtxq = (struct mt76_txq *)vif->txq->drv_priv;
79 mtxq->wcid = idx;
80 }
81@@ -1128,6 +1143,8 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
82 {
83 struct ieee80211_sta *sta = link_sta->sta;
84 struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
85+ u16 valid_links = sta->valid_links;
86+ bool pri_changed;
87 int i;
88
89 if (!mlink)
90@@ -1148,6 +1165,7 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
91 mt7996_mac_twt_teardown_flow(dev, mlink, i);
92
93 rcu_assign_pointer(mlink->sta->link[mlink->wcid.link_id], NULL);
94+ rcu_assign_pointer(dev->mt76.wcid[mlink->wcid.idx], NULL);
95
96 spin_lock_bh(&dev->mt76.sta_poll_lock);
97 if (!list_empty(&mlink->wcid.poll_list))
98@@ -1156,17 +1174,41 @@ static void mt7996_remove_link_sta(struct mt7996_dev *dev,
99 list_del_init(&mlink->rc_list);
100 spin_unlock_bh(&dev->mt76.sta_poll_lock);
101
102- /* TODO: update primary link */
103- if (sta->valid_links) {
104- if (mlink->wcid.link_id == msta->pri_link)
105- msta->pri_link = msta->sec_link;
106+ valid_links &= ~BIT(mlink->wcid.link_id);
107+ if (!valid_links)
108+ goto done;
109
110- if (sta->valid_links & ~(BIT(msta->pri_link)))
111- msta->sec_link = __ffs(sta->valid_links & ~(BIT(msta->pri_link)));
112- else
113- msta->sec_link = msta->pri_link;
114+ /* update primary and secondary link */
115+ pri_changed = mlink->wcid.link_id == msta->pri_link;
116+ if (pri_changed)
117+ msta->pri_link = msta->sec_link;
118+
119+ if (valid_links & ~(BIT(msta->pri_link)))
120+ msta->sec_link = __ffs(valid_links & ~(BIT(msta->pri_link)));
121+ else
122+ msta->sec_link = msta->pri_link;
123+
124+ if (pri_changed) {
125+ struct mt7996_link_sta *mlink_new =
126+ mlink_dereference_protected(msta, msta->pri_link);
127+
128+ if (!mlink_new)
129+ goto done;
130+
131+ mlink_new->wcid.ampdu_state = mlink->wcid.ampdu_state;
132+ for (i = 0; i < ARRAY_SIZE(mlink->wcid.aggr); i++)
133+ rcu_assign_pointer(mlink_new->wcid.aggr[i], mlink->wcid.aggr[i]);
134+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
135+ struct mt76_txq *mtxq;
136+
137+ if (!sta->txq[i])
138+ continue;
139+ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
140+ mtxq->wcid = mlink_new->wcid.idx;
141+ }
142 }
143
144+done:
145 mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
146 mt76_wcid_mask_clear(dev->mt76.wcid_mask, mlink->wcid.idx);
147 mt76_wcid_mask_clear(dev->mt76.wcid_phy_mask, mlink->wcid.idx);
148@@ -3104,9 +3146,15 @@ mt7996_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
149 mt76_vif_dbg(vif, "STA %pM old=0x%x, new=0x%x\n", sta->addr, old_links, new_links);
150 mutex_lock(&dev->mt76.mutex);
151
152- if (rem)
153+ if (rem) {
154 mt7996_mac_sta_remove_links(dev, vif, sta, rem);
155
156+ /* Todo: update hw info of MLD STA */
157+ /* ret = mt7996_mcu_add_mld_sta(dev, vif, sta, new_links); */
158+ /* if (ret) */
159+ /* goto remove; */
160+ }
161+
162 ret = mt7996_mac_sta_add_links(dev, vif, sta, add, false);
163 if (ret)
164 goto remove;
165diff --git a/mt7996/mcu.c b/mt7996/mcu.c
166index 4310d35b..662e4f02 100644
167--- a/mt7996/mcu.c
168+++ b/mt7996/mcu.c
169@@ -1350,6 +1350,48 @@ mt7996_mcu_pp_event(struct mt7996_dev *dev, struct sk_buff *skb)
170 }
171 }
172
173+static void
174+mt7996_mcu_mld_reconf_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
175+{
176+ struct mt7996_mld_event_data *data = priv;
177+ struct mt7996_mcu_mld_ap_reconf_event *reconf = (void *)data->data;
178+
179+ if (!ether_addr_equal(vif->addr, data->mld_addr))
180+ return;
181+
182+ ieee80211_links_removed(vif, le16_to_cpu(reconf->link_bitmap));
183+}
184+
185+static void
186+mt7996_mcu_mld_event(struct mt7996_dev *dev, struct sk_buff *skb)
187+{
188+ struct mt7996_mcu_mld_event *event = (void *)skb->data;
189+ struct mt7996_mld_event_data data = {};
190+ struct tlv *tlv;
191+ int len;
192+
193+ memcpy(data.mld_addr, event->mld_addr, ETH_ALEN);
194+ skb_pull(skb, sizeof(*event));
195+ tlv = (struct tlv *)skb->data;
196+ len = skb->len;
197+
198+ while (len > 0 && le16_to_cpu(tlv->len) <= len) {
199+ switch (le16_to_cpu(tlv->tag)) {
200+ case UNI_EVENT_MLD_RECONF_AP_REM_TIMER:
201+ data.data = (u8 *)tlv;
202+ ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
203+ IEEE80211_IFACE_ITER_RESUME_ALL,
204+ mt7996_mcu_mld_reconf_finish, &data);
205+ break;
206+ default:
207+ break;
208+ }
209+
210+ len -= le16_to_cpu(tlv->len);
211+ tlv = (struct tlv *)((u8 *)(tlv) + le16_to_cpu(tlv->len));
212+ }
213+}
214+
215 static void
216 mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
217 {
218@@ -1378,6 +1420,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
219 case MCU_UNI_EVENT_PP:
220 mt7996_mcu_pp_event(dev, skb);
221 break;
222+ case MCU_UNI_EVENT_MLD:
223+ mt7996_mcu_mld_event(dev, skb);
224+ break;
225 #ifdef CONFIG_MTK_DEBUG
226 case MCU_UNI_EVENT_SR:
227 mt7996_mcu_rx_sr_event(dev, skb);
228@@ -3169,6 +3214,44 @@ out:
229 MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
230 }
231
232+int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
233+ struct ieee80211_sta *sta, unsigned long add)
234+{
235+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
236+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
237+ u8 link_id;
238+
239+ if (!sta->mlo)
240+ return 0;
241+
242+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
243+ struct mt7996_bss_conf *mconf =
244+ mconf_dereference_protected(mvif, link_id);
245+ struct mt7996_link_sta *mlink =
246+ mlink_dereference_protected(msta, link_id);
247+ struct sk_buff *skb;
248+ int ret;
249+
250+ if (!mconf || !mlink)
251+ continue;
252+
253+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
254+ &mlink->wcid,
255+ MT7996_STA_UPDATE_MAX_SIZE);
256+ if (IS_ERR(skb))
257+ return PTR_ERR(skb);
258+ /* starec mld setup */
259+ mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta, add);
260+ /* starec eht mld */
261+ mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta);
262+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
263+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
264+ if (ret)
265+ return ret;
266+ }
267+ return 0;
268+}
269+
270 int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
271 struct mt7996_bss_conf *mconf,
272 struct mt7996_link_sta *mlink)
273@@ -3381,6 +3464,51 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
274 &data, sizeof(data), true);
275 }
276
277+static int
278+mt7996_mcu_mld_reconf(struct mt7996_dev *dev, struct ieee80211_vif *vif,
279+ u16 removed_links, u16 *removal_count)
280+{
281+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
282+ struct mld_req_hdr hdr = { .mld_idx = 0xff };
283+ struct mld_reconf_timer *rt;
284+ struct sk_buff *skb;
285+ struct tlv *tlv;
286+ int len = sizeof(hdr) + sizeof(*rt);
287+ unsigned long rem = removed_links;
288+ u8 link_id;
289+
290+ memcpy(hdr.mld_addr, vif->addr, ETH_ALEN);
291+
292+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
293+ if (!skb)
294+ return -ENOMEM;
295+
296+ skb_put_data(skb, &hdr, sizeof(hdr));
297+
298+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_AP_REM_TIMER, sizeof(*rt));
299+ rt = (struct mld_reconf_timer *)tlv;
300+ rt->link_bitmap = cpu_to_le16(removed_links);
301+
302+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
303+ struct ieee80211_bss_conf *conf =
304+ link_conf_dereference_protected(vif, link_id);
305+ struct mt7996_bss_conf *mconf =
306+ mconf_dereference_protected(mvif, link_id);
307+ u8 band_idx;
308+ u16 to_sec;
309+
310+ if (!conf || !mconf)
311+ continue;
312+
313+ band_idx = mconf->phy->mt76->band_idx;
314+ to_sec = conf->beacon_int * removal_count[link_id] / 1000;
315+ rt->to_sec[band_idx] = cpu_to_le16(to_sec);
316+ rt->bss_idx[band_idx] = mconf->mt76.idx;
317+ }
318+
319+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), true);
320+}
321+
322 static void
323 mt7996_mcu_beacon_cntdwn(struct ieee80211_bss_conf *conf, struct sk_buff *rskb,
324 struct sk_buff *skb,
325@@ -3575,6 +3703,94 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *conf,
326 mt7996_packet_log_to_host(dev, skb->data, skb->len, PKT_BIN_DEBUG_TX, 0);
327 }
328
329+static void
330+mt7996_mcu_beacon_ml_reconf(struct mt7996_dev *dev,
331+ struct ieee80211_bss_conf *conf,
332+ struct sk_buff *rskb, struct sk_buff *skb,
333+ struct ieee80211_mutable_offsets *offs)
334+{
335+ struct mt7996_vif *mvif = (struct mt7996_vif *)conf->vif->drv_priv;
336+ struct bss_bcn_ml_reconf_tlv *reconf;
337+ struct bss_bcn_ml_reconf_offset *reconf_offs;
338+ const struct element *elem, *sub;
339+ struct tlv *tlv;
340+ u16 removal_offs[IEEE80211_MLD_MAX_NUM_LINKS] = {};
341+ u16 removal_count[IEEE80211_MLD_MAX_NUM_LINKS] = {};
342+ u16 tail_offset = offs->tim_offset + offs->tim_length;
343+ unsigned long removed_links = 0;
344+ bool has_reconf = false;
345+ u8 link_id, *beacon_tail = skb->data + tail_offset;
346+
347+ if (!ieee80211_vif_is_mld(conf->vif))
348+ return;
349+
350+ /* TODO: currently manually parse reconf info directly from the IE, it
351+ * is expected to be passed from upper layer in the future.
352+ */
353+ for_each_element_extid(elem, WLAN_EID_EXT_EHT_MULTI_LINK,
354+ beacon_tail, skb->len - tail_offset) {
355+ if (ieee80211_mle_type_ok(elem->data + 1,
356+ IEEE80211_ML_CONTROL_TYPE_RECONF,
357+ elem->datalen - 1)) {
358+ has_reconf = true;
359+ break;
360+ }
361+ }
362+
363+ if (!has_reconf)
364+ return;
365+
366+ for_each_mle_subelement(sub, elem->data + 1, elem->datalen - 1) {
367+ struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
368+ u8 *pos = prof->variable;
369+ u16 control;
370+
371+ if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
372+ continue;
373+
374+ if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
375+ sub->datalen))
376+ return;
377+
378+ control = le16_to_cpu(prof->control);
379+ link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
380+
381+ removed_links |= BIT(link_id);
382+
383+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
384+ pos += 6;
385+
386+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT) {
387+ removal_offs[link_id] = pos - skb->data;
388+ removal_count[link_id] = le16_to_cpu(*(__le16 *)pos);
389+ }
390+ }
391+
392+ if (!removed_links)
393+ return;
394+
395+ /* the first link to be removed */
396+ if (conf->link_id == ffs(removed_links) - 1)
397+ mt7996_mcu_mld_reconf(dev, conf->vif, removed_links, removal_count);
398+
399+ tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_ML_RECONF,
400+ sizeof(*reconf) +
401+ sizeof(*reconf_offs) * hweight16(removed_links));
402+ reconf = (struct bss_bcn_ml_reconf_tlv *)tlv;
403+ reconf->reconf_count = hweight16(removed_links);
404+
405+ reconf_offs = (struct bss_bcn_ml_reconf_offset *)reconf->offset;
406+ for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) {
407+ struct mt7996_bss_conf *mconf =
408+ mconf_dereference_protected(mvif, link_id);
409+
410+ reconf_offs->ap_removal_timer_offs =
411+ cpu_to_le16(removal_offs[link_id]);
412+ reconf_offs->bss_idx = mconf->mt76.idx;
413+ reconf_offs++;
414+ }
415+}
416+
417 int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
418 struct ieee80211_bss_conf *conf,
419 struct mt7996_bss_conf *mconf, int en)
420@@ -3628,6 +3844,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
421 mt7996_mcu_beacon_cntdwn(conf, rskb, skb, &offs);
422 mt7996_mcu_beacon_sta_prof_csa(rskb, conf, &offs);
423 mt7996_mcu_beacon_crit_update(rskb, skb, conf, mconf, &offs);
424+ mt7996_mcu_beacon_ml_reconf(dev, conf, rskb, skb, &offs);
425 out:
426 dev_kfree_skb(skb);
427 return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
428diff --git a/mt7996/mcu.h b/mt7996/mcu.h
429index 739e357c..7c559d2b 100644
430--- a/mt7996/mcu.h
431+++ b/mt7996/mcu.h
432@@ -546,6 +546,20 @@ struct bss_bcn_sta_prof_cntdwn_tlv {
433 u8 pkt_content[9];
434 } __packed;
435
436+struct bss_bcn_ml_reconf_tlv {
437+ __le16 tag;
438+ __le16 len;
439+ u8 reconf_count;
440+ u8 rsv[3];
441+ u8 offset[];
442+} __packed;
443+
444+struct bss_bcn_ml_reconf_offset {
445+ __le16 ap_removal_timer_offs;
446+ u8 bss_idx;
447+ u8 rsv;
448+} __packed;
449+
450 struct bss_txcmd_tlv {
451 __le16 tag;
452 __le16 len;
453@@ -594,6 +608,17 @@ struct bss_mld_tlv {
454 u8 __rsv[3];
455 } __packed;
456
457+struct bss_mld_link_op_tlv {
458+ __le16 tag;
459+ __le16 len;
460+ u8 group_mld_id;
461+ u8 own_mld_id;
462+ u8 mac_addr[ETH_ALEN];
463+ u8 remap_idx;
464+ u8 link_operation;
465+ u8 rsv[2];
466+} __packed;
467+
468 struct sta_rec_ht_uni {
469 __le16 tag;
470 __le16 len;
471@@ -1003,8 +1028,9 @@ enum {
472 sizeof(struct bss_bcn_cntdwn_tlv) + \
473 sizeof(struct bss_bcn_mbss_tlv) + \
474 sizeof(struct bss_bcn_crit_update_tlv) + \
475- sizeof(struct bss_bcn_sta_prof_cntdwn_tlv)) \
476-
477+ sizeof(struct bss_bcn_sta_prof_cntdwn_tlv) + \
478+ sizeof(struct bss_bcn_ml_reconf_tlv) + \
479+ 3 * sizeof(struct bss_bcn_ml_reconf_offset))
480 #define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \
481 MT7996_BEACON_UPDATE_SIZE)
482
483@@ -1105,6 +1131,65 @@ enum {
484 UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
485 };
486
487+struct mld_req_hdr {
488+ u8 ver;
489+ u8 mld_addr[ETH_ALEN];
490+ u8 mld_idx;
491+ u8 flag;
492+ u8 rsv[3];
493+ u8 buf[];
494+} __packed;
495+
496+struct mld_reconf_timer {
497+ __le16 tag;
498+ __le16 len;
499+ __le16 link_bitmap;
500+ __le16 to_sec[__MT_MAX_BAND]; /* timeout of reconf (second) */
501+ u8 bss_idx[__MT_MAX_BAND];
502+ u8 rsv;
503+} __packed;
504+
505+struct mld_reconf_stop_link {
506+ __le16 tag;
507+ __le16 len;
508+ __le16 link_bitmap;
509+ u8 rsv[2];
510+ u8 bss_idx[16];
511+} __packed;
512+
513+enum {
514+ UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03,
515+};
516+
517+struct mt7996_mcu_mld_event {
518+ struct mt7996_mcu_rxd rxd;
519+
520+ /* fixed field */
521+ u8 ver;
522+ u8 mld_addr[ETH_ALEN];
523+ u8 mld_idx;
524+ u8 rsv[4];
525+ /* tlv */
526+ u8 buf[];
527+} __packed;
528+
529+struct mt7996_mld_event_data {
530+ u8 mld_addr[ETH_ALEN];
531+ u8 *data;
532+};
533+
534+struct mt7996_mcu_mld_ap_reconf_event {
535+ __le16 tag;
536+ __le16 len;
537+ __le16 link_bitmap;
538+ u8 bss_idx[3];
539+ u8 rsv[3];
540+} __packed;
541+
542+enum {
543+ UNI_EVENT_MLD_RECONF_AP_REM_TIMER = 0x04,
544+};
545+
546 struct tx_power_ctrl {
547 u8 _rsv[4];
548
549diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
550index 8b00b05b..6f9f82b7 100644
551--- a/mt7996/mt7996.h
552+++ b/mt7996/mt7996.h
553@@ -1083,6 +1083,8 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev,
554 struct mt7996_bss_conf *mconf,
555 struct ieee80211_link_sta *link_sta,
556 struct mt7996_link_sta *mlink, bool changed);
557+int mt7996_mcu_add_mld_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
558+ struct ieee80211_sta *sta, unsigned long add);
559 int mt7996_set_channel(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef);
560 int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag, bool sta);
561 int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct mt7996_bss_conf *mconf);
562--
5632.18.0
564