1f444de05SJohannes Berg /* 2f444de05SJohannes Berg * mac80211 - channel management 3f444de05SJohannes Berg */ 4f444de05SJohannes Berg 50aaffa9bSJohannes Berg #include <linux/nl80211.h> 63448c005SJohannes Berg #include <linux/export.h> 74d76d21bSJohannes Berg #include <linux/rtnetlink.h> 83117bbdbSPaul Stewart #include <net/cfg80211.h> 9f444de05SJohannes Berg #include "ieee80211_i.h" 1035f2fce9SMichal Kazior #include "driver-ops.h" 11f444de05SJohannes Berg 124bf88530SJohannes Berg static void ieee80211_change_chandef(struct ieee80211_local *local, 13e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 144bf88530SJohannes Berg const struct cfg80211_chan_def *chandef) 15e89a96f5SMichal Kazior { 164bf88530SJohannes Berg if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) 17e89a96f5SMichal Kazior return; 18e89a96f5SMichal Kazior 194bf88530SJohannes Berg WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 204bf88530SJohannes Berg 214bf88530SJohannes Berg ctx->conf.def = *chandef; 224bf88530SJohannes Berg drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 2355de908aSJohannes Berg 2455de908aSJohannes Berg if (!local->use_chanctx) { 254bf88530SJohannes Berg local->_oper_channel_type = cfg80211_get_chandef_type(chandef); 2655de908aSJohannes Berg ieee80211_hw_config(local, 0); 2755de908aSJohannes Berg } 280aaffa9bSJohannes Berg } 29d01a1e65SMichal Kazior 30d01a1e65SMichal Kazior static struct ieee80211_chanctx * 31d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 324bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 33d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 34d01a1e65SMichal Kazior { 35d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 36d01a1e65SMichal Kazior 37d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 38d01a1e65SMichal Kazior 39d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 40d01a1e65SMichal Kazior return NULL; 41d01a1e65SMichal Kazior 42d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 434bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 44e89a96f5SMichal Kazior 45d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 46d01a1e65SMichal Kazior continue; 474bf88530SJohannes Berg 484bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 494bf88530SJohannes Berg if (!compat) 50d01a1e65SMichal Kazior continue; 51d01a1e65SMichal Kazior 524bf88530SJohannes Berg ieee80211_change_chandef(local, ctx, compat); 53e89a96f5SMichal Kazior 54d01a1e65SMichal Kazior return ctx; 55d01a1e65SMichal Kazior } 56d01a1e65SMichal Kazior 57d01a1e65SMichal Kazior return NULL; 58d01a1e65SMichal Kazior } 59d01a1e65SMichal Kazior 60d01a1e65SMichal Kazior static struct ieee80211_chanctx * 61d01a1e65SMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 624bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 63d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 64d01a1e65SMichal Kazior { 65d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 6635f2fce9SMichal Kazior int err; 67d01a1e65SMichal Kazior 68d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 69d01a1e65SMichal Kazior 70d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 71d01a1e65SMichal Kazior if (!ctx) 72d01a1e65SMichal Kazior return ERR_PTR(-ENOMEM); 73d01a1e65SMichal Kazior 744bf88530SJohannes Berg ctx->conf.def = *chandef; 7504ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 7604ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 77d01a1e65SMichal Kazior ctx->mode = mode; 78d01a1e65SMichal Kazior 7955de908aSJohannes Berg if (!local->use_chanctx) { 804bf88530SJohannes Berg local->_oper_channel_type = 814bf88530SJohannes Berg cfg80211_get_chandef_type(chandef); 824bf88530SJohannes Berg local->_oper_channel = chandef->chan; 8355de908aSJohannes Berg ieee80211_hw_config(local, 0); 8455de908aSJohannes Berg } else { 8535f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 8635f2fce9SMichal Kazior if (err) { 8735f2fce9SMichal Kazior kfree(ctx); 8835f2fce9SMichal Kazior return ERR_PTR(err); 8935f2fce9SMichal Kazior } 9055de908aSJohannes Berg } 9135f2fce9SMichal Kazior 923448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 93d01a1e65SMichal Kazior 94fd0f979aSJohannes Berg mutex_lock(&local->mtx); 95fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 96fd0f979aSJohannes Berg mutex_unlock(&local->mtx); 97fd0f979aSJohannes Berg 98d01a1e65SMichal Kazior return ctx; 99d01a1e65SMichal Kazior } 100d01a1e65SMichal Kazior 101d01a1e65SMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 102d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 103d01a1e65SMichal Kazior { 104d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 105d01a1e65SMichal Kazior 106d01a1e65SMichal Kazior WARN_ON_ONCE(ctx->refcount != 0); 107d01a1e65SMichal Kazior 10855de908aSJohannes Berg if (!local->use_chanctx) { 10955de908aSJohannes Berg local->_oper_channel_type = NL80211_CHAN_NO_HT; 11055de908aSJohannes Berg ieee80211_hw_config(local, 0); 11155de908aSJohannes Berg } else { 11235f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 11355de908aSJohannes Berg } 11435f2fce9SMichal Kazior 1153448c005SJohannes Berg list_del_rcu(&ctx->list); 116d01a1e65SMichal Kazior kfree_rcu(ctx, rcu_head); 117fd0f979aSJohannes Berg 118fd0f979aSJohannes Berg mutex_lock(&local->mtx); 119fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 120fd0f979aSJohannes Berg mutex_unlock(&local->mtx); 121d01a1e65SMichal Kazior } 122d01a1e65SMichal Kazior 123d01a1e65SMichal Kazior static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 124d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 125d01a1e65SMichal Kazior { 12635f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 12735f2fce9SMichal Kazior int ret; 128d01a1e65SMichal Kazior 129d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 130d01a1e65SMichal Kazior 13135f2fce9SMichal Kazior ret = drv_assign_vif_chanctx(local, sdata, ctx); 13235f2fce9SMichal Kazior if (ret) 13335f2fce9SMichal Kazior return ret; 13435f2fce9SMichal Kazior 135d01a1e65SMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 136d01a1e65SMichal Kazior ctx->refcount++; 137d01a1e65SMichal Kazior 1381ea6f9c0SJohannes Berg ieee80211_recalc_txpower(sdata); 139fd0f979aSJohannes Berg sdata->vif.bss_conf.idle = false; 140fd0f979aSJohannes Berg ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); 1411ea6f9c0SJohannes Berg 142d01a1e65SMichal Kazior return 0; 143d01a1e65SMichal Kazior } 144d01a1e65SMichal Kazior 1454bf88530SJohannes Berg static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 146e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 147e89a96f5SMichal Kazior { 148e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 149e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 1504bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 151e89a96f5SMichal Kazior 152e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 153e89a96f5SMichal Kazior 154e89a96f5SMichal Kazior rcu_read_lock(); 155e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 1564bf88530SJohannes Berg 157e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 158e89a96f5SMichal Kazior continue; 159e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 160e89a96f5SMichal Kazior continue; 161e89a96f5SMichal Kazior 1624bf88530SJohannes Berg if (!compat) 1634bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 1644bf88530SJohannes Berg 1654bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 1664bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 1674bf88530SJohannes Berg if (!compat) 1684bf88530SJohannes Berg break; 169e89a96f5SMichal Kazior } 170e89a96f5SMichal Kazior rcu_read_unlock(); 171e89a96f5SMichal Kazior 1724bf88530SJohannes Berg if (WARN_ON_ONCE(!compat)) 1734bf88530SJohannes Berg return; 174e89a96f5SMichal Kazior 1754bf88530SJohannes Berg ieee80211_change_chandef(local, ctx, compat); 176e89a96f5SMichal Kazior } 177e89a96f5SMichal Kazior 178d01a1e65SMichal Kazior static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 179d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 180d01a1e65SMichal Kazior { 18135f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 182d01a1e65SMichal Kazior 183d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 184d01a1e65SMichal Kazior 185d01a1e65SMichal Kazior ctx->refcount--; 186d01a1e65SMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); 18735f2fce9SMichal Kazior 188fd0f979aSJohannes Berg sdata->vif.bss_conf.idle = true; 189fd0f979aSJohannes Berg ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); 190fd0f979aSJohannes Berg 19135f2fce9SMichal Kazior drv_unassign_vif_chanctx(local, sdata, ctx); 192e89a96f5SMichal Kazior 19304ecd257SJohannes Berg if (ctx->refcount > 0) { 194e89a96f5SMichal Kazior ieee80211_recalc_chanctx_chantype(sdata->local, ctx); 19504ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 196164eb02dSSimon Wunderlich ieee80211_recalc_radar_chanctx(local, ctx); 19704ecd257SJohannes Berg } 198d01a1e65SMichal Kazior } 199d01a1e65SMichal Kazior 200d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 201d01a1e65SMichal Kazior { 202d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 203d01a1e65SMichal Kazior struct ieee80211_chanctx_conf *conf; 204d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 205d01a1e65SMichal Kazior 206d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 207d01a1e65SMichal Kazior 208d01a1e65SMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 209d01a1e65SMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 210d01a1e65SMichal Kazior if (!conf) 211d01a1e65SMichal Kazior return; 212d01a1e65SMichal Kazior 213d01a1e65SMichal Kazior ctx = container_of(conf, struct ieee80211_chanctx, conf); 214d01a1e65SMichal Kazior 215d01a1e65SMichal Kazior ieee80211_unassign_vif_chanctx(sdata, ctx); 216d01a1e65SMichal Kazior if (ctx->refcount == 0) 217d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 218d01a1e65SMichal Kazior } 219d01a1e65SMichal Kazior 220164eb02dSSimon Wunderlich void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 221164eb02dSSimon Wunderlich struct ieee80211_chanctx *chanctx) 222164eb02dSSimon Wunderlich { 223164eb02dSSimon Wunderlich struct ieee80211_sub_if_data *sdata; 224164eb02dSSimon Wunderlich bool radar_enabled = false; 225164eb02dSSimon Wunderlich 226164eb02dSSimon Wunderlich lockdep_assert_held(&local->chanctx_mtx); 227164eb02dSSimon Wunderlich 228164eb02dSSimon Wunderlich rcu_read_lock(); 229164eb02dSSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 230164eb02dSSimon Wunderlich if (sdata->radar_required) { 231164eb02dSSimon Wunderlich radar_enabled = true; 232164eb02dSSimon Wunderlich break; 233164eb02dSSimon Wunderlich } 234164eb02dSSimon Wunderlich } 235164eb02dSSimon Wunderlich rcu_read_unlock(); 236164eb02dSSimon Wunderlich 237164eb02dSSimon Wunderlich if (radar_enabled == chanctx->conf.radar_enabled) 238164eb02dSSimon Wunderlich return; 239164eb02dSSimon Wunderlich 240164eb02dSSimon Wunderlich chanctx->conf.radar_enabled = radar_enabled; 241164eb02dSSimon Wunderlich local->radar_detect_enabled = chanctx->conf.radar_enabled; 242164eb02dSSimon Wunderlich 243164eb02dSSimon Wunderlich if (!local->use_chanctx) { 244164eb02dSSimon Wunderlich local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 245164eb02dSSimon Wunderlich ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 246164eb02dSSimon Wunderlich } 247164eb02dSSimon Wunderlich 248164eb02dSSimon Wunderlich drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 249164eb02dSSimon Wunderlich } 250164eb02dSSimon Wunderlich 25104ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 25204ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 25304ecd257SJohannes Berg { 25404ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 25504ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 25604ecd257SJohannes Berg 25704ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 25804ecd257SJohannes Berg 25904ecd257SJohannes Berg rx_chains_static = 1; 26004ecd257SJohannes Berg rx_chains_dynamic = 1; 26104ecd257SJohannes Berg 26204ecd257SJohannes Berg rcu_read_lock(); 26304ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 26404ecd257SJohannes Berg u8 needed_static, needed_dynamic; 26504ecd257SJohannes Berg 26604ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 26704ecd257SJohannes Berg continue; 26804ecd257SJohannes Berg 26904ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 27004ecd257SJohannes Berg &chanctx->conf) 27104ecd257SJohannes Berg continue; 27204ecd257SJohannes Berg 27304ecd257SJohannes Berg switch (sdata->vif.type) { 27404ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 27504ecd257SJohannes Berg continue; 27604ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 27704ecd257SJohannes Berg if (!sdata->u.mgd.associated) 27804ecd257SJohannes Berg continue; 27904ecd257SJohannes Berg break; 28004ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 28104ecd257SJohannes Berg continue; 28204ecd257SJohannes Berg case NL80211_IFTYPE_AP: 28304ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 28404ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 28504ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 28604ecd257SJohannes Berg break; 28704ecd257SJohannes Berg default: 28804ecd257SJohannes Berg WARN_ON_ONCE(1); 28904ecd257SJohannes Berg } 29004ecd257SJohannes Berg 29104ecd257SJohannes Berg switch (sdata->smps_mode) { 29204ecd257SJohannes Berg default: 29304ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 29404ecd257SJohannes Berg sdata->smps_mode); 29504ecd257SJohannes Berg /* fall through */ 29604ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 29704ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 29804ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 29904ecd257SJohannes Berg break; 30004ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 30104ecd257SJohannes Berg needed_static = 1; 30204ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 30304ecd257SJohannes Berg break; 30404ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 30504ecd257SJohannes Berg needed_static = 1; 30604ecd257SJohannes Berg needed_dynamic = 1; 30704ecd257SJohannes Berg break; 30804ecd257SJohannes Berg } 30904ecd257SJohannes Berg 31004ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 31104ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 31204ecd257SJohannes Berg } 31304ecd257SJohannes Berg rcu_read_unlock(); 31404ecd257SJohannes Berg 31504ecd257SJohannes Berg if (!local->use_chanctx) { 31604ecd257SJohannes Berg if (rx_chains_static > 1) 31704ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 31804ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 31904ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 32004ecd257SJohannes Berg else 32104ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 32204ecd257SJohannes Berg ieee80211_hw_config(local, 0); 32304ecd257SJohannes Berg } 32404ecd257SJohannes Berg 32504ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 32604ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 32704ecd257SJohannes Berg return; 32804ecd257SJohannes Berg 32904ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 33004ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 33104ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 33204ecd257SJohannes Berg } 33304ecd257SJohannes Berg 334d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 3354bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 336d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 337d01a1e65SMichal Kazior { 338d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 339d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 340d01a1e65SMichal Kazior int ret; 341d01a1e65SMichal Kazior 34255de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 34355de908aSJohannes Berg 344d01a1e65SMichal Kazior mutex_lock(&local->chanctx_mtx); 345d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 346d01a1e65SMichal Kazior 3474bf88530SJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 348d01a1e65SMichal Kazior if (!ctx) 3494bf88530SJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 350d01a1e65SMichal Kazior if (IS_ERR(ctx)) { 351d01a1e65SMichal Kazior ret = PTR_ERR(ctx); 352d01a1e65SMichal Kazior goto out; 353d01a1e65SMichal Kazior } 354d01a1e65SMichal Kazior 3554bf88530SJohannes Berg sdata->vif.bss_conf.chandef = *chandef; 35655de908aSJohannes Berg 357d01a1e65SMichal Kazior ret = ieee80211_assign_vif_chanctx(sdata, ctx); 358d01a1e65SMichal Kazior if (ret) { 359d01a1e65SMichal Kazior /* if assign fails refcount stays the same */ 360d01a1e65SMichal Kazior if (ctx->refcount == 0) 361d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 362d01a1e65SMichal Kazior goto out; 363d01a1e65SMichal Kazior } 364d01a1e65SMichal Kazior 36504ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 366164eb02dSSimon Wunderlich ieee80211_recalc_radar_chanctx(local, ctx); 367d01a1e65SMichal Kazior out: 368d01a1e65SMichal Kazior mutex_unlock(&local->chanctx_mtx); 369d01a1e65SMichal Kazior return ret; 370d01a1e65SMichal Kazior } 371d01a1e65SMichal Kazior 372d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 373d01a1e65SMichal Kazior { 37455de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 37555de908aSJohannes Berg 376d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 377d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 378d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 379d01a1e65SMichal Kazior } 3803448c005SJohannes Berg 3814d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 3824d76d21bSJohannes Berg { 3834d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 3844d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 3854d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 3864d76d21bSJohannes Berg 3874d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 3884d76d21bSJohannes Berg return; 3894d76d21bSJohannes Berg 3904d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 3914d76d21bSJohannes Berg 3924d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 3934d76d21bSJohannes Berg 3944d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 3954d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 3964d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 3974d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 3984d76d21bSJohannes Berg } 3994d76d21bSJohannes Berg 4001f4ac5a6SJohannes Berg void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 4011f4ac5a6SJohannes Berg bool clear) 4021f4ac5a6SJohannes Berg { 4031f4ac5a6SJohannes Berg struct ieee80211_local *local = sdata->local; 4041f4ac5a6SJohannes Berg struct ieee80211_sub_if_data *vlan; 4051f4ac5a6SJohannes Berg struct ieee80211_chanctx_conf *conf; 4061f4ac5a6SJohannes Berg 4071f4ac5a6SJohannes Berg ASSERT_RTNL(); 4081f4ac5a6SJohannes Berg 4091f4ac5a6SJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 4101f4ac5a6SJohannes Berg return; 4111f4ac5a6SJohannes Berg 4121f4ac5a6SJohannes Berg mutex_lock(&local->chanctx_mtx); 4131f4ac5a6SJohannes Berg 4141f4ac5a6SJohannes Berg /* 4151f4ac5a6SJohannes Berg * Check that conf exists, even when clearing this function 4161f4ac5a6SJohannes Berg * must be called with the AP's channel context still there 4171f4ac5a6SJohannes Berg * as it would otherwise cause VLANs to have an invalid 4181f4ac5a6SJohannes Berg * channel context pointer for a while, possibly pointing 4191f4ac5a6SJohannes Berg * to a channel context that has already been freed. 4201f4ac5a6SJohannes Berg */ 4211f4ac5a6SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 4221f4ac5a6SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 4231f4ac5a6SJohannes Berg WARN_ON(!conf); 4241f4ac5a6SJohannes Berg 4251f4ac5a6SJohannes Berg if (clear) 4261f4ac5a6SJohannes Berg conf = NULL; 4271f4ac5a6SJohannes Berg 4281f4ac5a6SJohannes Berg list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 4291f4ac5a6SJohannes Berg rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 4301f4ac5a6SJohannes Berg 4311f4ac5a6SJohannes Berg mutex_unlock(&local->chanctx_mtx); 4321f4ac5a6SJohannes Berg } 4331f4ac5a6SJohannes Berg 4343448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 4353448c005SJohannes Berg struct ieee80211_hw *hw, 4363448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 4373448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 4383448c005SJohannes Berg void *data), 4393448c005SJohannes Berg void *iter_data) 4403448c005SJohannes Berg { 4413448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 4423448c005SJohannes Berg struct ieee80211_chanctx *ctx; 4433448c005SJohannes Berg 4443448c005SJohannes Berg rcu_read_lock(); 4453448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 4468a61af65SJohannes Berg if (ctx->driver_present) 4473448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 4483448c005SJohannes Berg rcu_read_unlock(); 4493448c005SJohannes Berg } 4503448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 451