aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211/link.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/link.c')
-rw-r--r--net/mac80211/link.c237
1 files changed, 224 insertions, 13 deletions
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index 096f313c2a6e..e309708abae8 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -9,6 +9,7 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
+#include "key.h"
void ieee80211_link_setup(struct ieee80211_link_data *link)
{
@@ -73,28 +74,37 @@ struct link_container {
struct ieee80211_bss_conf conf;
};
-static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
- struct link_container **links)
+static void ieee80211_tear_down_links(struct ieee80211_sub_if_data *sdata,
+ struct link_container **links, u16 mask)
{
+ struct ieee80211_link_data *link;
LIST_HEAD(keys);
unsigned int link_id;
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
- if (!links[link_id])
+ if (!(mask & BIT(link_id)))
+ continue;
+ link = &links[link_id]->data;
+ if (link_id == 0 && !link)
+ link = &sdata->deflink;
+ if (WARN_ON(!link))
continue;
- ieee80211_remove_link_keys(&links[link_id]->data, &keys);
+ ieee80211_remove_link_keys(link, &keys);
+ ieee80211_link_stop(link);
}
synchronize_rcu();
ieee80211_free_key_list(sdata->local, &keys);
+}
- for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
- if (!links[link_id])
- continue;
- ieee80211_link_stop(&links[link_id]->data);
+static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
+ struct link_container **links)
+{
+ unsigned int link_id;
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
kfree(links[link_id]);
- }
}
static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
@@ -123,11 +133,38 @@ static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
return 0;
}
+static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
+ u16 links)
+{
+ sdata->vif.valid_links = links;
+
+ if (!links) {
+ sdata->vif.active_links = 0;
+ return;
+ }
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ /* in an AP all links are always active */
+ sdata->vif.active_links = links;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (sdata->vif.active_links)
+ break;
+ WARN_ON(hweight16(links) > 1);
+ sdata->vif.active_links = links;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
struct link_container **to_free,
u16 new_links)
{
u16 old_links = sdata->vif.valid_links;
+ u16 old_active = sdata->vif.active_links;
unsigned long add = new_links & ~old_links;
unsigned long rem = old_links & ~new_links;
unsigned int link_id;
@@ -195,13 +232,17 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
ieee80211_link_init(sdata, -1, &sdata->deflink,
&sdata->vif.bss_conf);
- sdata->vif.valid_links = new_links;
-
ret = ieee80211_check_dup_link_addrs(sdata);
if (!ret) {
+ /* for keys we will not be able to undo this */
+ ieee80211_tear_down_links(sdata, to_free, rem);
+
+ ieee80211_set_vif_links_bitmaps(sdata, new_links);
+
/* tell the driver */
ret = drv_change_vif_links(sdata->local, sdata,
- old_links, new_links,
+ old_links & old_active,
+ new_links & sdata->vif.active_links,
old);
}
@@ -209,7 +250,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
/* restore config */
memcpy(sdata->link, old_data, sizeof(old_data));
memcpy(sdata->vif.link_conf, old, sizeof(old));
- sdata->vif.valid_links = old_links;
+ ieee80211_set_vif_links_bitmaps(sdata, old_links);
/* and free (only) the newly allocated links */
memset(to_free, 0, sizeof(links));
goto free;
@@ -260,3 +301,173 @@ void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
ieee80211_free_links(sdata, links);
}
+
+static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
+ u16 active_links)
+{
+ struct ieee80211_bss_conf *link_confs[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct ieee80211_local *local = sdata->local;
+ u16 old_active = sdata->vif.active_links;
+ unsigned long rem = old_active & ~active_links;
+ unsigned long add = active_links & ~old_active;
+ struct sta_info *sta;
+ unsigned int link_id;
+ int ret, i;
+
+ if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ /* cannot activate links that don't exist */
+ if (active_links & ~sdata->vif.valid_links)
+ return -EINVAL;
+
+ /* nothing to do */
+ if (old_active == active_links)
+ return 0;
+
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
+ link_confs[i] = sdata_dereference(sdata->vif.link_conf[i],
+ sdata);
+
+ if (add) {
+ sdata->vif.active_links |= active_links;
+ ret = drv_change_vif_links(local, sdata,
+ old_active,
+ sdata->vif.active_links,
+ link_confs);
+ if (ret) {
+ sdata->vif.active_links = old_active;
+ return ret;
+ }
+ }
+
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_data *link;
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+
+ /* FIXME: kill TDLS connections on the link */
+
+ ieee80211_link_release_channel(link);
+ }
+
+ list_for_each_entry(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+ ret = drv_change_sta_links(local, sdata, &sta->sta,
+ old_active,
+ old_active | active_links);
+ WARN_ON_ONCE(ret);
+ }
+
+ ret = ieee80211_key_switch_links(sdata, rem, add);
+ WARN_ON_ONCE(ret);
+
+ list_for_each_entry(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+ ret = drv_change_sta_links(local, sdata, &sta->sta,
+ old_active | active_links,
+ active_links);
+ WARN_ON_ONCE(ret);
+ }
+
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_data *link;
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+
+ ret = ieee80211_link_use_channel(link, &link->conf->chandef,
+ IEEE80211_CHANCTX_SHARED);
+ WARN_ON_ONCE(ret);
+
+ ieee80211_link_info_change_notify(sdata, link,
+ BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_ERP_SLOT |
+ BSS_CHANGED_HT |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_CQM |
+ BSS_CHANGED_QOS |
+ BSS_CHANGED_TXPOWER |
+ BSS_CHANGED_BANDWIDTH |
+ BSS_CHANGED_TWT |
+ BSS_CHANGED_HE_OBSS_PD |
+ BSS_CHANGED_HE_BSS_COLOR);
+ ieee80211_mgd_set_link_qos_params(link);
+ }
+
+ old_active = sdata->vif.active_links;
+ sdata->vif.active_links = active_links;
+
+ if (rem) {
+ ret = drv_change_vif_links(local, sdata, old_active,
+ active_links, link_confs);
+ WARN_ON_ONCE(ret);
+ }
+
+ return 0;
+}
+
+int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
+ u16 old_active;
+ int ret;
+
+ sdata_lock(sdata);
+ mutex_lock(&local->sta_mtx);
+ mutex_lock(&local->mtx);
+ mutex_lock(&local->key_mtx);
+ old_active = sdata->vif.active_links;
+ if (old_active & active_links) {
+ /*
+ * if there's at least one link that stays active across
+ * the change then switch to it (to those) first, and
+ * then enable the additional links
+ */
+ ret = _ieee80211_set_active_links(sdata,
+ old_active & active_links);
+ if (!ret)
+ ret = _ieee80211_set_active_links(sdata, active_links);
+ } else {
+ /* otherwise switch directly */
+ ret = _ieee80211_set_active_links(sdata, active_links);
+ }
+ mutex_unlock(&local->key_mtx);
+ mutex_unlock(&local->mtx);
+ mutex_unlock(&local->sta_mtx);
+ sdata_unlock(sdata);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_active_links);
+
+void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
+ u16 active_links)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (!ieee80211_sdata_running(sdata))
+ return;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
+
+ /* cannot activate links that don't exist */
+ if (active_links & ~sdata->vif.valid_links)
+ return;
+
+ /* nothing to do */
+ if (sdata->vif.active_links == active_links)
+ return;
+
+ sdata->desired_active_links = active_links;
+ schedule_work(&sdata->activate_links_work);
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_active_links_async);