struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
u16 usable_links = ieee80211_vif_usable_links(vif);
u16 active_links = vif->active_links;
- struct rtw89_vif_link *target, *cur;
+ struct rtw89_vif_link *target;
int ret;
lockdep_assert_wiphy(rtwdev->hw->wiphy);
ieee80211_stop_queues(rtwdev->hw);
flush_work(&rtwdev->txq_work);
- cur = rtw89_get_designated_link(rtwvif);
-
- ret = ieee80211_set_active_links(vif, active_links | BIT(link_id));
+ ret = ieee80211_set_active_links(vif, BIT(link_id));
if (ret) {
- rtw89_err(rtwdev, "%s: failed to activate link id %u\n",
+ rtw89_err(rtwdev, "%s: failed to work on link id %u\n",
__func__, link_id);
goto wake_queue;
}
goto wake_queue;
}
- if (likely(cur))
- rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false);
-
- rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true);
-
- ret = ieee80211_set_active_links(vif, BIT(link_id));
- if (ret)
- rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n",
- __func__, active_links);
-
rtw89_chip_rfk_channel(rtwdev, target);
rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
if (changed & BSS_CHANGED_ARP_FILTER)
rtwvif->ip_addr = vif->cfg.arp_addr_list[0];
+
+ if (changed & BSS_CHANGED_MLD_VALID_LINKS) {
+ struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif);
+
+ rtw89_chip_rfk_channel(rtwdev, cur);
+
+ if (hweight16(vif->active_links) == 1)
+ rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+ else
+ rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR;
+ }
}
static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
u16 active_links)
{
struct rtw89_dev *rtwdev = hw->priv;
+ struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
+ u16 current_links = vif->active_links;
+ struct rtw89_vif_ml_trans trans = {
+ .mediate_links = current_links | active_links,
+ .links_to_del = current_links & ~active_links,
+ .links_to_add = active_links & ~current_links,
+ };
lockdep_assert_wiphy(hw->wiphy);
- return rtw89_can_work_on_links(rtwdev, vif, active_links);
+ if (!rtw89_can_work_on_links(rtwdev, vif, active_links))
+ return false;
+
+ /*
+ * Leave LPS at the beginning of ieee80211_set_active_links().
+ * Because the entire process takes the same lock as our track
+ * work, LPS will not enter during ieee80211_set_active_links().
+ */
+ rtw89_leave_lps(rtwdev);
+
+ rtwvif->ml_trans = trans;
+
+ return true;
}
static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev,
return 0;
}
+static void rtw89_vif_cfg_fw_links(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ unsigned long links, bool en)
+{
+ struct rtw89_vif_link *rtwvif_link;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ rtwvif_link = rtwvif->links[link_id];
+ if (unlikely(!rtwvif_link))
+ continue;
+
+ rtw89_fw_h2c_mlo_link_cfg(rtwdev, rtwvif_link, en);
+ }
+}
+
+static void rtw89_vif_update_fw_links(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ u16 current_links)
+{
+ struct rtw89_vif_ml_trans *trans = &rtwvif->ml_trans;
+
+ /* Do follow-up when all updating links exist. */
+ if (current_links != trans->mediate_links)
+ return;
+
+ rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_del, false);
+ rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_add, true);
+}
+
static
int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
if (rtwdev->scanning)
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
+ rtw89_vif_update_fw_links(rtwdev, rtwvif, old_links);
+
if (!old_links)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif,
BIT(RTW89_VIF_IDLE_LINK_ID));