1e73b5e51SJohannes Berg // SPDX-License-Identifier: GPL-2.0-only 2e73b5e51SJohannes Berg /* 3e73b5e51SJohannes Berg * MLO link handling 4e73b5e51SJohannes Berg * 501605ad6SJohannes Berg * Copyright (C) 2022-2023 Intel Corporation 6e73b5e51SJohannes Berg */ 7e73b5e51SJohannes Berg #include <linux/slab.h> 8e73b5e51SJohannes Berg #include <linux/kernel.h> 9e73b5e51SJohannes Berg #include <net/mac80211.h> 10e73b5e51SJohannes Berg #include "ieee80211_i.h" 11e73b5e51SJohannes Berg #include "driver-ops.h" 123d901102SJohannes Berg #include "key.h" 13170cd6a6SBenjamin Berg #include "debugfs_netdev.h" 14e73b5e51SJohannes Berg 15e73b5e51SJohannes Berg void ieee80211_link_setup(struct ieee80211_link_data *link) 16e73b5e51SJohannes Berg { 17e73b5e51SJohannes Berg if (link->sdata->vif.type == NL80211_IFTYPE_STATION) 18e73b5e51SJohannes Berg ieee80211_mgd_setup_link(link); 19e73b5e51SJohannes Berg } 20e73b5e51SJohannes Berg 21e73b5e51SJohannes Berg void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, 22e73b5e51SJohannes Berg int link_id, 23e73b5e51SJohannes Berg struct ieee80211_link_data *link, 24e73b5e51SJohannes Berg struct ieee80211_bss_conf *link_conf) 25e73b5e51SJohannes Berg { 26e73b5e51SJohannes Berg bool deflink = link_id < 0; 27e73b5e51SJohannes Berg 28e73b5e51SJohannes Berg if (link_id < 0) 29e73b5e51SJohannes Berg link_id = 0; 30e73b5e51SJohannes Berg 31e73b5e51SJohannes Berg rcu_assign_pointer(sdata->vif.link_conf[link_id], link_conf); 32e73b5e51SJohannes Berg rcu_assign_pointer(sdata->link[link_id], link); 33e73b5e51SJohannes Berg 34e73b5e51SJohannes Berg link->sdata = sdata; 35e73b5e51SJohannes Berg link->link_id = link_id; 36e73b5e51SJohannes Berg link->conf = link_conf; 37e73b5e51SJohannes Berg link_conf->link_id = link_id; 38e1f113ccSBenjamin Berg link_conf->vif = &sdata->vif; 39e73b5e51SJohannes Berg 40e73b5e51SJohannes Berg INIT_WORK(&link->csa_finalize_work, 41e73b5e51SJohannes Berg ieee80211_csa_finalize_work); 42e73b5e51SJohannes Berg INIT_WORK(&link->color_change_finalize_work, 43e73b5e51SJohannes Berg ieee80211_color_change_finalize_work); 4492881884SLorenzo Bianconi INIT_DELAYED_WORK(&link->color_collision_detect_work, 4592881884SLorenzo Bianconi ieee80211_color_collision_detection_work); 46e73b5e51SJohannes Berg INIT_LIST_HEAD(&link->assigned_chanctx_list); 47e73b5e51SJohannes Berg INIT_LIST_HEAD(&link->reserved_chanctx_list); 48e73b5e51SJohannes Berg INIT_DELAYED_WORK(&link->dfs_cac_timer_work, 49e73b5e51SJohannes Berg ieee80211_dfs_cac_timer_work); 50e73b5e51SJohannes Berg 51e73b5e51SJohannes Berg if (!deflink) { 52e73b5e51SJohannes Berg switch (sdata->vif.type) { 53e73b5e51SJohannes Berg case NL80211_IFTYPE_AP: 54e73b5e51SJohannes Berg ether_addr_copy(link_conf->addr, 55e73b5e51SJohannes Berg sdata->wdev.links[link_id].addr); 56e73b5e51SJohannes Berg link_conf->bssid = link_conf->addr; 57e73b5e51SJohannes Berg WARN_ON(!(sdata->wdev.valid_links & BIT(link_id))); 58e73b5e51SJohannes Berg break; 59e73b5e51SJohannes Berg case NL80211_IFTYPE_STATION: 60e73b5e51SJohannes Berg /* station sets the bssid in ieee80211_mgd_setup_link */ 61e73b5e51SJohannes Berg break; 62e73b5e51SJohannes Berg default: 63e73b5e51SJohannes Berg WARN_ON(1); 64e73b5e51SJohannes Berg } 65170cd6a6SBenjamin Berg 66170cd6a6SBenjamin Berg ieee80211_link_debugfs_add(link); 67e73b5e51SJohannes Berg } 68e73b5e51SJohannes Berg } 69e73b5e51SJohannes Berg 70e73b5e51SJohannes Berg void ieee80211_link_stop(struct ieee80211_link_data *link) 71e73b5e51SJohannes Berg { 72e73b5e51SJohannes Berg if (link->sdata->vif.type == NL80211_IFTYPE_STATION) 73e73b5e51SJohannes Berg ieee80211_mgd_stop_link(link); 74e73b5e51SJohannes Berg 7592881884SLorenzo Bianconi cancel_delayed_work_sync(&link->color_collision_detect_work); 76e73b5e51SJohannes Berg ieee80211_link_release_channel(link); 77e73b5e51SJohannes Berg } 78e73b5e51SJohannes Berg 79e73b5e51SJohannes Berg struct link_container { 80e73b5e51SJohannes Berg struct ieee80211_link_data data; 81e73b5e51SJohannes Berg struct ieee80211_bss_conf conf; 82e73b5e51SJohannes Berg }; 83e73b5e51SJohannes Berg 84efe9c2bfSJohannes Berg static void ieee80211_tear_down_links(struct ieee80211_sub_if_data *sdata, 85efe9c2bfSJohannes Berg struct link_container **links, u16 mask) 86e73b5e51SJohannes Berg { 87efe9c2bfSJohannes Berg struct ieee80211_link_data *link; 88e73b5e51SJohannes Berg LIST_HEAD(keys); 89e73b5e51SJohannes Berg unsigned int link_id; 90e73b5e51SJohannes Berg 91e73b5e51SJohannes Berg for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 92efe9c2bfSJohannes Berg if (!(mask & BIT(link_id))) 93e73b5e51SJohannes Berg continue; 94efe9c2bfSJohannes Berg link = &links[link_id]->data; 95efe9c2bfSJohannes Berg if (link_id == 0 && !link) 96efe9c2bfSJohannes Berg link = &sdata->deflink; 97efe9c2bfSJohannes Berg if (WARN_ON(!link)) 98efe9c2bfSJohannes Berg continue; 99efe9c2bfSJohannes Berg ieee80211_remove_link_keys(link, &keys); 100170cd6a6SBenjamin Berg ieee80211_link_debugfs_remove(link); 101efe9c2bfSJohannes Berg ieee80211_link_stop(link); 102e73b5e51SJohannes Berg } 103e73b5e51SJohannes Berg 104e73b5e51SJohannes Berg synchronize_rcu(); 105e73b5e51SJohannes Berg 106e73b5e51SJohannes Berg ieee80211_free_key_list(sdata->local, &keys); 107e73b5e51SJohannes Berg } 108efe9c2bfSJohannes Berg 109efe9c2bfSJohannes Berg static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata, 110efe9c2bfSJohannes Berg struct link_container **links) 111efe9c2bfSJohannes Berg { 112efe9c2bfSJohannes Berg unsigned int link_id; 113efe9c2bfSJohannes Berg 114efe9c2bfSJohannes Berg for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) 115efe9c2bfSJohannes Berg kfree(links[link_id]); 116e73b5e51SJohannes Berg } 117e73b5e51SJohannes Berg 118e73b5e51SJohannes Berg static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata) 119e73b5e51SJohannes Berg { 120e73b5e51SJohannes Berg unsigned int i, j; 121e73b5e51SJohannes Berg 122e73b5e51SJohannes Berg for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { 123e73b5e51SJohannes Berg struct ieee80211_link_data *link1; 124e73b5e51SJohannes Berg 125e73b5e51SJohannes Berg link1 = sdata_dereference(sdata->link[i], sdata); 126e73b5e51SJohannes Berg if (!link1) 127e73b5e51SJohannes Berg continue; 128e73b5e51SJohannes Berg for (j = i + 1; j < IEEE80211_MLD_MAX_NUM_LINKS; j++) { 129e73b5e51SJohannes Berg struct ieee80211_link_data *link2; 130e73b5e51SJohannes Berg 131e73b5e51SJohannes Berg link2 = sdata_dereference(sdata->link[j], sdata); 132e73b5e51SJohannes Berg if (!link2) 133e73b5e51SJohannes Berg continue; 134e73b5e51SJohannes Berg 135e73b5e51SJohannes Berg if (ether_addr_equal(link1->conf->addr, 136e73b5e51SJohannes Berg link2->conf->addr)) 137e73b5e51SJohannes Berg return -EALREADY; 138e73b5e51SJohannes Berg } 139e73b5e51SJohannes Berg } 140e73b5e51SJohannes Berg 141e73b5e51SJohannes Berg return 0; 142e73b5e51SJohannes Berg } 143e73b5e51SJohannes Berg 144efe9c2bfSJohannes Berg static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata, 1456d543b34SIlan Peer u16 valid_links, u16 dormant_links) 146efe9c2bfSJohannes Berg { 1476d543b34SIlan Peer sdata->vif.valid_links = valid_links; 1486d543b34SIlan Peer sdata->vif.dormant_links = dormant_links; 149efe9c2bfSJohannes Berg 1506d543b34SIlan Peer if (!valid_links || 1516d543b34SIlan Peer WARN((~valid_links & dormant_links) || 1526d543b34SIlan Peer !(valid_links & ~dormant_links), 1536d543b34SIlan Peer "Invalid links: valid=0x%x, dormant=0x%x", 1546d543b34SIlan Peer valid_links, dormant_links)) { 155efe9c2bfSJohannes Berg sdata->vif.active_links = 0; 1566d543b34SIlan Peer sdata->vif.dormant_links = 0; 157efe9c2bfSJohannes Berg return; 158efe9c2bfSJohannes Berg } 159efe9c2bfSJohannes Berg 160efe9c2bfSJohannes Berg switch (sdata->vif.type) { 161efe9c2bfSJohannes Berg case NL80211_IFTYPE_AP: 162efe9c2bfSJohannes Berg /* in an AP all links are always active */ 1636d543b34SIlan Peer sdata->vif.active_links = valid_links; 1646d543b34SIlan Peer 1656d543b34SIlan Peer /* AP links are not expected to be disabled */ 1666d543b34SIlan Peer WARN_ON(dormant_links); 167efe9c2bfSJohannes Berg break; 168efe9c2bfSJohannes Berg case NL80211_IFTYPE_STATION: 169efe9c2bfSJohannes Berg if (sdata->vif.active_links) 170efe9c2bfSJohannes Berg break; 1716d543b34SIlan Peer sdata->vif.active_links = valid_links & ~dormant_links; 1726d543b34SIlan Peer WARN_ON(hweight16(sdata->vif.active_links) > 1); 173efe9c2bfSJohannes Berg break; 174efe9c2bfSJohannes Berg default: 175efe9c2bfSJohannes Berg WARN_ON(1); 176efe9c2bfSJohannes Berg } 177efe9c2bfSJohannes Berg } 178efe9c2bfSJohannes Berg 179e73b5e51SJohannes Berg static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, 180e73b5e51SJohannes Berg struct link_container **to_free, 1816d543b34SIlan Peer u16 new_links, u16 dormant_links) 182e73b5e51SJohannes Berg { 183e73b5e51SJohannes Berg u16 old_links = sdata->vif.valid_links; 184efe9c2bfSJohannes Berg u16 old_active = sdata->vif.active_links; 185e73b5e51SJohannes Berg unsigned long add = new_links & ~old_links; 186e73b5e51SJohannes Berg unsigned long rem = old_links & ~new_links; 187e73b5e51SJohannes Berg unsigned int link_id; 188e73b5e51SJohannes Berg int ret; 189e73b5e51SJohannes Berg struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link; 190e73b5e51SJohannes Berg struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]; 191e73b5e51SJohannes Berg struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS]; 192e73b5e51SJohannes Berg bool use_deflink = old_links == 0; /* set for error case */ 193e73b5e51SJohannes Berg 194e73b5e51SJohannes Berg sdata_assert_lock(sdata); 195e73b5e51SJohannes Berg 196e73b5e51SJohannes Berg memset(to_free, 0, sizeof(links)); 197e73b5e51SJohannes Berg 198e73b5e51SJohannes Berg if (old_links == new_links) 199e73b5e51SJohannes Berg return 0; 200e73b5e51SJohannes Berg 201e73b5e51SJohannes Berg /* if there were no old links, need to clear the pointers to deflink */ 202e73b5e51SJohannes Berg if (!old_links) 203e73b5e51SJohannes Berg rem |= BIT(0); 204e73b5e51SJohannes Berg 205e73b5e51SJohannes Berg /* allocate new link structures first */ 206e73b5e51SJohannes Berg for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { 207e73b5e51SJohannes Berg link = kzalloc(sizeof(*link), GFP_KERNEL); 208e73b5e51SJohannes Berg if (!link) { 209e73b5e51SJohannes Berg ret = -ENOMEM; 210e73b5e51SJohannes Berg goto free; 211e73b5e51SJohannes Berg } 212e73b5e51SJohannes Berg links[link_id] = link; 213e73b5e51SJohannes Berg } 214e73b5e51SJohannes Berg 215e73b5e51SJohannes Berg /* keep track of the old pointers for the driver */ 216e73b5e51SJohannes Berg BUILD_BUG_ON(sizeof(old) != sizeof(sdata->vif.link_conf)); 217e73b5e51SJohannes Berg memcpy(old, sdata->vif.link_conf, sizeof(old)); 218e73b5e51SJohannes Berg /* and for us in error cases */ 219e73b5e51SJohannes Berg BUILD_BUG_ON(sizeof(old_data) != sizeof(sdata->link)); 220e73b5e51SJohannes Berg memcpy(old_data, sdata->link, sizeof(old_data)); 221e73b5e51SJohannes Berg 222e73b5e51SJohannes Berg /* grab old links to free later */ 223e73b5e51SJohannes Berg for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { 224e73b5e51SJohannes Berg if (rcu_access_pointer(sdata->link[link_id]) != &sdata->deflink) { 225e73b5e51SJohannes Berg /* 226e73b5e51SJohannes Berg * we must have allocated the data through this path so 227e73b5e51SJohannes Berg * we know we can free both at the same time 228e73b5e51SJohannes Berg */ 229e73b5e51SJohannes Berg to_free[link_id] = container_of(rcu_access_pointer(sdata->link[link_id]), 230e73b5e51SJohannes Berg typeof(*links[link_id]), 231e73b5e51SJohannes Berg data); 232e73b5e51SJohannes Berg } 233e73b5e51SJohannes Berg 234e73b5e51SJohannes Berg RCU_INIT_POINTER(sdata->link[link_id], NULL); 235e73b5e51SJohannes Berg RCU_INIT_POINTER(sdata->vif.link_conf[link_id], NULL); 236e73b5e51SJohannes Berg } 237e73b5e51SJohannes Berg 238e73b5e51SJohannes Berg /* link them into data structures */ 239e73b5e51SJohannes Berg for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { 240e73b5e51SJohannes Berg WARN_ON(!use_deflink && 241e73b5e51SJohannes Berg rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink); 242e73b5e51SJohannes Berg 243e73b5e51SJohannes Berg link = links[link_id]; 244e73b5e51SJohannes Berg ieee80211_link_init(sdata, link_id, &link->data, &link->conf); 245e73b5e51SJohannes Berg ieee80211_link_setup(&link->data); 246e73b5e51SJohannes Berg } 247e73b5e51SJohannes Berg 248e73b5e51SJohannes Berg if (new_links == 0) 249e73b5e51SJohannes Berg ieee80211_link_init(sdata, -1, &sdata->deflink, 250e73b5e51SJohannes Berg &sdata->vif.bss_conf); 251e73b5e51SJohannes Berg 252e73b5e51SJohannes Berg ret = ieee80211_check_dup_link_addrs(sdata); 253e73b5e51SJohannes Berg if (!ret) { 254efe9c2bfSJohannes Berg /* for keys we will not be able to undo this */ 255efe9c2bfSJohannes Berg ieee80211_tear_down_links(sdata, to_free, rem); 256efe9c2bfSJohannes Berg 2576d543b34SIlan Peer ieee80211_set_vif_links_bitmaps(sdata, new_links, dormant_links); 258efe9c2bfSJohannes Berg 259e73b5e51SJohannes Berg /* tell the driver */ 260e73b5e51SJohannes Berg ret = drv_change_vif_links(sdata->local, sdata, 261efe9c2bfSJohannes Berg old_links & old_active, 262efe9c2bfSJohannes Berg new_links & sdata->vif.active_links, 263e73b5e51SJohannes Berg old); 264e73b5e51SJohannes Berg } 265e73b5e51SJohannes Berg 266e73b5e51SJohannes Berg if (ret) { 267e73b5e51SJohannes Berg /* restore config */ 268e73b5e51SJohannes Berg memcpy(sdata->link, old_data, sizeof(old_data)); 269e73b5e51SJohannes Berg memcpy(sdata->vif.link_conf, old, sizeof(old)); 2706d543b34SIlan Peer ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links); 271e73b5e51SJohannes Berg /* and free (only) the newly allocated links */ 272e73b5e51SJohannes Berg memset(to_free, 0, sizeof(links)); 273e73b5e51SJohannes Berg goto free; 274e73b5e51SJohannes Berg } 275e73b5e51SJohannes Berg 276e73b5e51SJohannes Berg /* use deflink/bss_conf again if and only if there are no more links */ 277e73b5e51SJohannes Berg use_deflink = new_links == 0; 278e73b5e51SJohannes Berg 279e73b5e51SJohannes Berg goto deinit; 280e73b5e51SJohannes Berg free: 281e73b5e51SJohannes Berg /* if we failed during allocation, only free all */ 282e73b5e51SJohannes Berg for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 283e73b5e51SJohannes Berg kfree(links[link_id]); 284e73b5e51SJohannes Berg links[link_id] = NULL; 285e73b5e51SJohannes Berg } 286e73b5e51SJohannes Berg deinit: 287e73b5e51SJohannes Berg if (use_deflink) 288e73b5e51SJohannes Berg ieee80211_link_init(sdata, -1, &sdata->deflink, 289e73b5e51SJohannes Berg &sdata->vif.bss_conf); 290e73b5e51SJohannes Berg return ret; 291e73b5e51SJohannes Berg } 292e73b5e51SJohannes Berg 293e73b5e51SJohannes Berg int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata, 2946d543b34SIlan Peer u16 new_links, u16 dormant_links) 295e73b5e51SJohannes Berg { 296e73b5e51SJohannes Berg struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS]; 297e73b5e51SJohannes Berg int ret; 298e73b5e51SJohannes Berg 2996d543b34SIlan Peer ret = ieee80211_vif_update_links(sdata, links, new_links, 3006d543b34SIlan Peer dormant_links); 301e73b5e51SJohannes Berg ieee80211_free_links(sdata, links); 302e73b5e51SJohannes Berg 303e73b5e51SJohannes Berg return ret; 304e73b5e51SJohannes Berg } 305e73b5e51SJohannes Berg 306e73b5e51SJohannes Berg void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata) 307e73b5e51SJohannes Berg { 308e73b5e51SJohannes Berg struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS]; 309e73b5e51SJohannes Berg 310e73b5e51SJohannes Berg /* 311e73b5e51SJohannes Berg * The locking here is different because when we free links 312e73b5e51SJohannes Berg * in the station case we need to be able to cancel_work_sync() 313e73b5e51SJohannes Berg * something that also takes the lock. 314e73b5e51SJohannes Berg */ 315e73b5e51SJohannes Berg 316e73b5e51SJohannes Berg sdata_lock(sdata); 3176d543b34SIlan Peer ieee80211_vif_update_links(sdata, links, 0, 0); 318e73b5e51SJohannes Berg sdata_unlock(sdata); 319e73b5e51SJohannes Berg 320e73b5e51SJohannes Berg ieee80211_free_links(sdata, links); 321e73b5e51SJohannes Berg } 3223d901102SJohannes Berg 3233d901102SJohannes Berg static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, 3243d901102SJohannes Berg u16 active_links) 3253d901102SJohannes Berg { 3263d901102SJohannes Berg struct ieee80211_bss_conf *link_confs[IEEE80211_MLD_MAX_NUM_LINKS]; 3273d901102SJohannes Berg struct ieee80211_local *local = sdata->local; 3283d901102SJohannes Berg u16 old_active = sdata->vif.active_links; 3293d901102SJohannes Berg unsigned long rem = old_active & ~active_links; 3303d901102SJohannes Berg unsigned long add = active_links & ~old_active; 3313d901102SJohannes Berg struct sta_info *sta; 3323d901102SJohannes Berg unsigned int link_id; 3333d901102SJohannes Berg int ret, i; 3343d901102SJohannes Berg 3353d901102SJohannes Berg if (!ieee80211_sdata_running(sdata)) 3363d901102SJohannes Berg return -ENETDOWN; 3373d901102SJohannes Berg 3383d901102SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_STATION) 3393d901102SJohannes Berg return -EINVAL; 3403d901102SJohannes Berg 341f1871abdSIlan Peer if (active_links & ~ieee80211_vif_usable_links(&sdata->vif)) 3423d901102SJohannes Berg return -EINVAL; 3433d901102SJohannes Berg 3443d901102SJohannes Berg /* nothing to do */ 3453d901102SJohannes Berg if (old_active == active_links) 3463d901102SJohannes Berg return 0; 3473d901102SJohannes Berg 3483d901102SJohannes Berg for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) 3493d901102SJohannes Berg link_confs[i] = sdata_dereference(sdata->vif.link_conf[i], 3503d901102SJohannes Berg sdata); 3513d901102SJohannes Berg 3523d901102SJohannes Berg if (add) { 3533d901102SJohannes Berg sdata->vif.active_links |= active_links; 3543d901102SJohannes Berg ret = drv_change_vif_links(local, sdata, 3553d901102SJohannes Berg old_active, 3563d901102SJohannes Berg sdata->vif.active_links, 3573d901102SJohannes Berg link_confs); 3583d901102SJohannes Berg if (ret) { 3593d901102SJohannes Berg sdata->vif.active_links = old_active; 3603d901102SJohannes Berg return ret; 3613d901102SJohannes Berg } 3623d901102SJohannes Berg } 3633d901102SJohannes Berg 3643d901102SJohannes Berg for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { 3653d901102SJohannes Berg struct ieee80211_link_data *link; 3663d901102SJohannes Berg 3673d901102SJohannes Berg link = sdata_dereference(sdata->link[link_id], sdata); 3683d901102SJohannes Berg 3693d901102SJohannes Berg /* FIXME: kill TDLS connections on the link */ 3703d901102SJohannes Berg 3713d901102SJohannes Berg ieee80211_link_release_channel(link); 3723d901102SJohannes Berg } 3733d901102SJohannes Berg 3743d901102SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 3753d901102SJohannes Berg if (sdata != sta->sdata) 3763d901102SJohannes Berg continue; 3779b41a9d7SJohannes Berg 3789b41a9d7SJohannes Berg /* this is very temporary, but do it anyway */ 3799b41a9d7SJohannes Berg __ieee80211_sta_recalc_aggregates(sta, 3809b41a9d7SJohannes Berg old_active | active_links); 3819b41a9d7SJohannes Berg 3823d901102SJohannes Berg ret = drv_change_sta_links(local, sdata, &sta->sta, 3833d901102SJohannes Berg old_active, 3843d901102SJohannes Berg old_active | active_links); 3853d901102SJohannes Berg WARN_ON_ONCE(ret); 3863d901102SJohannes Berg } 3873d901102SJohannes Berg 3883d901102SJohannes Berg ret = ieee80211_key_switch_links(sdata, rem, add); 3893d901102SJohannes Berg WARN_ON_ONCE(ret); 3903d901102SJohannes Berg 3913d901102SJohannes Berg list_for_each_entry(sta, &local->sta_list, list) { 3923d901102SJohannes Berg if (sdata != sta->sdata) 3933d901102SJohannes Berg continue; 3949b41a9d7SJohannes Berg 3959b41a9d7SJohannes Berg __ieee80211_sta_recalc_aggregates(sta, active_links); 3969b41a9d7SJohannes Berg 3973d901102SJohannes Berg ret = drv_change_sta_links(local, sdata, &sta->sta, 3983d901102SJohannes Berg old_active | active_links, 3993d901102SJohannes Berg active_links); 4003d901102SJohannes Berg WARN_ON_ONCE(ret); 4019b41a9d7SJohannes Berg 4029b41a9d7SJohannes Berg /* 4039b41a9d7SJohannes Berg * Do it again, just in case - the driver might very 4049b41a9d7SJohannes Berg * well have called ieee80211_sta_recalc_aggregates() 4059b41a9d7SJohannes Berg * from there when filling in the new links, which 4069b41a9d7SJohannes Berg * would set it wrong since the vif's active links are 4079b41a9d7SJohannes Berg * not switched yet... 4089b41a9d7SJohannes Berg */ 4099b41a9d7SJohannes Berg __ieee80211_sta_recalc_aggregates(sta, active_links); 4103d901102SJohannes Berg } 4113d901102SJohannes Berg 4123d901102SJohannes Berg for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { 4133d901102SJohannes Berg struct ieee80211_link_data *link; 4143d901102SJohannes Berg 4153d901102SJohannes Berg link = sdata_dereference(sdata->link[link_id], sdata); 4163d901102SJohannes Berg 4173d901102SJohannes Berg ret = ieee80211_link_use_channel(link, &link->conf->chandef, 4183d901102SJohannes Berg IEEE80211_CHANCTX_SHARED); 4193d901102SJohannes Berg WARN_ON_ONCE(ret); 4203d901102SJohannes Berg 42101605ad6SJohannes Berg ieee80211_mgd_set_link_qos_params(link); 4223d901102SJohannes Berg ieee80211_link_info_change_notify(sdata, link, 4233d901102SJohannes Berg BSS_CHANGED_ERP_CTS_PROT | 4243d901102SJohannes Berg BSS_CHANGED_ERP_PREAMBLE | 4253d901102SJohannes Berg BSS_CHANGED_ERP_SLOT | 4263d901102SJohannes Berg BSS_CHANGED_HT | 4273d901102SJohannes Berg BSS_CHANGED_BASIC_RATES | 4283d901102SJohannes Berg BSS_CHANGED_BSSID | 4293d901102SJohannes Berg BSS_CHANGED_CQM | 4303d901102SJohannes Berg BSS_CHANGED_QOS | 4313d901102SJohannes Berg BSS_CHANGED_TXPOWER | 4323d901102SJohannes Berg BSS_CHANGED_BANDWIDTH | 4333d901102SJohannes Berg BSS_CHANGED_TWT | 4343d901102SJohannes Berg BSS_CHANGED_HE_OBSS_PD | 4353d901102SJohannes Berg BSS_CHANGED_HE_BSS_COLOR); 4363d901102SJohannes Berg } 4373d901102SJohannes Berg 4383d901102SJohannes Berg old_active = sdata->vif.active_links; 4393d901102SJohannes Berg sdata->vif.active_links = active_links; 4403d901102SJohannes Berg 4413d901102SJohannes Berg if (rem) { 4423d901102SJohannes Berg ret = drv_change_vif_links(local, sdata, old_active, 4433d901102SJohannes Berg active_links, link_confs); 4443d901102SJohannes Berg WARN_ON_ONCE(ret); 4453d901102SJohannes Berg } 4463d901102SJohannes Berg 4473d901102SJohannes Berg return 0; 4483d901102SJohannes Berg } 4493d901102SJohannes Berg 450*79973d5cSBenjamin Berg int __ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) 4513d901102SJohannes Berg { 4523d901102SJohannes Berg struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 4533d901102SJohannes Berg struct ieee80211_local *local = sdata->local; 4543d901102SJohannes Berg u16 old_active; 4553d901102SJohannes Berg int ret; 4563d901102SJohannes Berg 457*79973d5cSBenjamin Berg sdata_assert_lock(sdata); 4583d901102SJohannes Berg mutex_lock(&local->sta_mtx); 4593d901102SJohannes Berg mutex_lock(&local->mtx); 4603d901102SJohannes Berg mutex_lock(&local->key_mtx); 4613d901102SJohannes Berg old_active = sdata->vif.active_links; 4623d901102SJohannes Berg if (old_active & active_links) { 4633d901102SJohannes Berg /* 4643d901102SJohannes Berg * if there's at least one link that stays active across 4653d901102SJohannes Berg * the change then switch to it (to those) first, and 4663d901102SJohannes Berg * then enable the additional links 4673d901102SJohannes Berg */ 4683d901102SJohannes Berg ret = _ieee80211_set_active_links(sdata, 4693d901102SJohannes Berg old_active & active_links); 4703d901102SJohannes Berg if (!ret) 4713d901102SJohannes Berg ret = _ieee80211_set_active_links(sdata, active_links); 4723d901102SJohannes Berg } else { 4733d901102SJohannes Berg /* otherwise switch directly */ 4743d901102SJohannes Berg ret = _ieee80211_set_active_links(sdata, active_links); 4753d901102SJohannes Berg } 4763d901102SJohannes Berg mutex_unlock(&local->key_mtx); 4773d901102SJohannes Berg mutex_unlock(&local->mtx); 4783d901102SJohannes Berg mutex_unlock(&local->sta_mtx); 479*79973d5cSBenjamin Berg 480*79973d5cSBenjamin Berg return ret; 481*79973d5cSBenjamin Berg } 482*79973d5cSBenjamin Berg 483*79973d5cSBenjamin Berg int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) 484*79973d5cSBenjamin Berg { 485*79973d5cSBenjamin Berg struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 486*79973d5cSBenjamin Berg int ret; 487*79973d5cSBenjamin Berg 488*79973d5cSBenjamin Berg sdata_lock(sdata); 489*79973d5cSBenjamin Berg ret = __ieee80211_set_active_links(vif, active_links); 4903d901102SJohannes Berg sdata_unlock(sdata); 4913d901102SJohannes Berg 4923d901102SJohannes Berg return ret; 4933d901102SJohannes Berg } 4943d901102SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_set_active_links); 4953d901102SJohannes Berg 4963d901102SJohannes Berg void ieee80211_set_active_links_async(struct ieee80211_vif *vif, 4973d901102SJohannes Berg u16 active_links) 4983d901102SJohannes Berg { 4993d901102SJohannes Berg struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 5003d901102SJohannes Berg 5013d901102SJohannes Berg if (!ieee80211_sdata_running(sdata)) 5023d901102SJohannes Berg return; 5033d901102SJohannes Berg 5043d901102SJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_STATION) 5053d901102SJohannes Berg return; 5063d901102SJohannes Berg 507f1871abdSIlan Peer if (active_links & ~ieee80211_vif_usable_links(&sdata->vif)) 5083d901102SJohannes Berg return; 5093d901102SJohannes Berg 5103d901102SJohannes Berg /* nothing to do */ 5113d901102SJohannes Berg if (sdata->vif.active_links == active_links) 5123d901102SJohannes Berg return; 5133d901102SJohannes Berg 5143d901102SJohannes Berg sdata->desired_active_links = active_links; 5153d901102SJohannes Berg schedule_work(&sdata->activate_links_work); 5163d901102SJohannes Berg } 5173d901102SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_set_active_links_async); 518