1 /* 2 * mac80211 - channel management 3 */ 4 5 #include <linux/nl80211.h> 6 #include <linux/export.h> 7 #include <net/cfg80211.h> 8 #include "ieee80211_i.h" 9 #include "driver-ops.h" 10 11 static void ieee80211_change_chandef(struct ieee80211_local *local, 12 struct ieee80211_chanctx *ctx, 13 const struct cfg80211_chan_def *chandef) 14 { 15 if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) 16 return; 17 18 WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 19 20 ctx->conf.def = *chandef; 21 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 22 23 if (!local->use_chanctx) { 24 local->_oper_channel_type = cfg80211_get_chandef_type(chandef); 25 ieee80211_hw_config(local, 0); 26 } 27 } 28 29 static struct ieee80211_chanctx * 30 ieee80211_find_chanctx(struct ieee80211_local *local, 31 const struct cfg80211_chan_def *chandef, 32 enum ieee80211_chanctx_mode mode) 33 { 34 struct ieee80211_chanctx *ctx; 35 36 lockdep_assert_held(&local->chanctx_mtx); 37 38 if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 39 return NULL; 40 41 list_for_each_entry(ctx, &local->chanctx_list, list) { 42 const struct cfg80211_chan_def *compat; 43 44 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 45 continue; 46 47 compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 48 if (!compat) 49 continue; 50 51 ieee80211_change_chandef(local, ctx, compat); 52 53 return ctx; 54 } 55 56 return NULL; 57 } 58 59 static struct ieee80211_chanctx * 60 ieee80211_new_chanctx(struct ieee80211_local *local, 61 const struct cfg80211_chan_def *chandef, 62 enum ieee80211_chanctx_mode mode) 63 { 64 struct ieee80211_chanctx *ctx; 65 int err; 66 67 lockdep_assert_held(&local->chanctx_mtx); 68 69 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 70 if (!ctx) 71 return ERR_PTR(-ENOMEM); 72 73 ctx->conf.def = *chandef; 74 ctx->conf.rx_chains_static = 1; 75 ctx->conf.rx_chains_dynamic = 1; 76 ctx->mode = mode; 77 78 if (!local->use_chanctx) { 79 local->_oper_channel_type = 80 cfg80211_get_chandef_type(chandef); 81 local->_oper_channel = chandef->chan; 82 ieee80211_hw_config(local, 0); 83 } else { 84 err = drv_add_chanctx(local, ctx); 85 if (err) { 86 kfree(ctx); 87 return ERR_PTR(err); 88 } 89 } 90 91 list_add_rcu(&ctx->list, &local->chanctx_list); 92 93 return ctx; 94 } 95 96 static void ieee80211_free_chanctx(struct ieee80211_local *local, 97 struct ieee80211_chanctx *ctx) 98 { 99 lockdep_assert_held(&local->chanctx_mtx); 100 101 WARN_ON_ONCE(ctx->refcount != 0); 102 103 if (!local->use_chanctx) { 104 local->_oper_channel_type = NL80211_CHAN_NO_HT; 105 ieee80211_hw_config(local, 0); 106 } else { 107 drv_remove_chanctx(local, ctx); 108 } 109 110 list_del_rcu(&ctx->list); 111 kfree_rcu(ctx, rcu_head); 112 } 113 114 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 115 struct ieee80211_chanctx *ctx) 116 { 117 struct ieee80211_local *local = sdata->local; 118 int ret; 119 120 lockdep_assert_held(&local->chanctx_mtx); 121 122 ret = drv_assign_vif_chanctx(local, sdata, ctx); 123 if (ret) 124 return ret; 125 126 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 127 ctx->refcount++; 128 129 ieee80211_recalc_txpower(sdata); 130 131 return 0; 132 } 133 134 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 135 struct ieee80211_chanctx *ctx) 136 { 137 struct ieee80211_chanctx_conf *conf = &ctx->conf; 138 struct ieee80211_sub_if_data *sdata; 139 const struct cfg80211_chan_def *compat = NULL; 140 141 lockdep_assert_held(&local->chanctx_mtx); 142 143 rcu_read_lock(); 144 list_for_each_entry_rcu(sdata, &local->interfaces, list) { 145 146 if (!ieee80211_sdata_running(sdata)) 147 continue; 148 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 149 continue; 150 151 if (!compat) 152 compat = &sdata->vif.bss_conf.chandef; 153 154 compat = cfg80211_chandef_compatible( 155 &sdata->vif.bss_conf.chandef, compat); 156 if (!compat) 157 break; 158 } 159 rcu_read_unlock(); 160 161 if (WARN_ON_ONCE(!compat)) 162 return; 163 164 ieee80211_change_chandef(local, ctx, compat); 165 } 166 167 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 168 struct ieee80211_chanctx *ctx) 169 { 170 struct ieee80211_local *local = sdata->local; 171 172 lockdep_assert_held(&local->chanctx_mtx); 173 174 ctx->refcount--; 175 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); 176 177 drv_unassign_vif_chanctx(local, sdata, ctx); 178 179 if (ctx->refcount > 0) { 180 ieee80211_recalc_chanctx_chantype(sdata->local, ctx); 181 ieee80211_recalc_smps_chanctx(local, ctx); 182 } 183 } 184 185 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 186 { 187 struct ieee80211_local *local = sdata->local; 188 struct ieee80211_chanctx_conf *conf; 189 struct ieee80211_chanctx *ctx; 190 191 lockdep_assert_held(&local->chanctx_mtx); 192 193 conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 194 lockdep_is_held(&local->chanctx_mtx)); 195 if (!conf) 196 return; 197 198 ctx = container_of(conf, struct ieee80211_chanctx, conf); 199 200 ieee80211_unassign_vif_chanctx(sdata, ctx); 201 if (ctx->refcount == 0) 202 ieee80211_free_chanctx(local, ctx); 203 } 204 205 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 206 struct ieee80211_chanctx *chanctx) 207 { 208 struct ieee80211_sub_if_data *sdata; 209 u8 rx_chains_static, rx_chains_dynamic; 210 211 lockdep_assert_held(&local->chanctx_mtx); 212 213 rx_chains_static = 1; 214 rx_chains_dynamic = 1; 215 216 rcu_read_lock(); 217 list_for_each_entry_rcu(sdata, &local->interfaces, list) { 218 u8 needed_static, needed_dynamic; 219 220 if (!ieee80211_sdata_running(sdata)) 221 continue; 222 223 if (rcu_access_pointer(sdata->vif.chanctx_conf) != 224 &chanctx->conf) 225 continue; 226 227 switch (sdata->vif.type) { 228 case NL80211_IFTYPE_P2P_DEVICE: 229 continue; 230 case NL80211_IFTYPE_STATION: 231 if (!sdata->u.mgd.associated) 232 continue; 233 break; 234 case NL80211_IFTYPE_AP_VLAN: 235 continue; 236 case NL80211_IFTYPE_AP: 237 case NL80211_IFTYPE_ADHOC: 238 case NL80211_IFTYPE_WDS: 239 case NL80211_IFTYPE_MESH_POINT: 240 break; 241 default: 242 WARN_ON_ONCE(1); 243 } 244 245 switch (sdata->smps_mode) { 246 default: 247 WARN_ONCE(1, "Invalid SMPS mode %d\n", 248 sdata->smps_mode); 249 /* fall through */ 250 case IEEE80211_SMPS_OFF: 251 needed_static = sdata->needed_rx_chains; 252 needed_dynamic = sdata->needed_rx_chains; 253 break; 254 case IEEE80211_SMPS_DYNAMIC: 255 needed_static = 1; 256 needed_dynamic = sdata->needed_rx_chains; 257 break; 258 case IEEE80211_SMPS_STATIC: 259 needed_static = 1; 260 needed_dynamic = 1; 261 break; 262 } 263 264 rx_chains_static = max(rx_chains_static, needed_static); 265 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 266 } 267 rcu_read_unlock(); 268 269 if (!local->use_chanctx) { 270 if (rx_chains_static > 1) 271 local->smps_mode = IEEE80211_SMPS_OFF; 272 else if (rx_chains_dynamic > 1) 273 local->smps_mode = IEEE80211_SMPS_DYNAMIC; 274 else 275 local->smps_mode = IEEE80211_SMPS_STATIC; 276 ieee80211_hw_config(local, 0); 277 } 278 279 if (rx_chains_static == chanctx->conf.rx_chains_static && 280 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 281 return; 282 283 chanctx->conf.rx_chains_static = rx_chains_static; 284 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 285 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 286 } 287 288 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 289 const struct cfg80211_chan_def *chandef, 290 enum ieee80211_chanctx_mode mode) 291 { 292 struct ieee80211_local *local = sdata->local; 293 struct ieee80211_chanctx *ctx; 294 int ret; 295 296 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 297 298 mutex_lock(&local->chanctx_mtx); 299 __ieee80211_vif_release_channel(sdata); 300 301 ctx = ieee80211_find_chanctx(local, chandef, mode); 302 if (!ctx) 303 ctx = ieee80211_new_chanctx(local, chandef, mode); 304 if (IS_ERR(ctx)) { 305 ret = PTR_ERR(ctx); 306 goto out; 307 } 308 309 sdata->vif.bss_conf.chandef = *chandef; 310 311 ret = ieee80211_assign_vif_chanctx(sdata, ctx); 312 if (ret) { 313 /* if assign fails refcount stays the same */ 314 if (ctx->refcount == 0) 315 ieee80211_free_chanctx(local, ctx); 316 goto out; 317 } 318 319 ieee80211_recalc_smps_chanctx(local, ctx); 320 out: 321 mutex_unlock(&local->chanctx_mtx); 322 return ret; 323 } 324 325 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 326 { 327 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 328 329 mutex_lock(&sdata->local->chanctx_mtx); 330 __ieee80211_vif_release_channel(sdata); 331 mutex_unlock(&sdata->local->chanctx_mtx); 332 } 333 334 void ieee80211_iter_chan_contexts_atomic( 335 struct ieee80211_hw *hw, 336 void (*iter)(struct ieee80211_hw *hw, 337 struct ieee80211_chanctx_conf *chanctx_conf, 338 void *data), 339 void *iter_data) 340 { 341 struct ieee80211_local *local = hw_to_local(hw); 342 struct ieee80211_chanctx *ctx; 343 344 rcu_read_lock(); 345 list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 346 iter(hw, &ctx->conf, iter_data); 347 rcu_read_unlock(); 348 } 349 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 350