2 * mac80211 - channel management
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"
11 static void ieee80211_change_chandef(struct ieee80211_local *local,
12 struct ieee80211_chanctx *ctx,
13 const struct cfg80211_chan_def *chandef)
15 if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
18 WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
20 ctx->conf.def = *chandef;
21 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
23 if (!local->use_chanctx) {
24 local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
25 ieee80211_hw_config(local, 0);
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)
34 struct ieee80211_chanctx *ctx;
36 lockdep_assert_held(&local->chanctx_mtx);
38 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
41 list_for_each_entry(ctx, &local->chanctx_list, list) {
42 const struct cfg80211_chan_def *compat;
44 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
47 compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
51 ieee80211_change_chandef(local, ctx, compat);
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)
64 struct ieee80211_chanctx *ctx;
67 lockdep_assert_held(&local->chanctx_mtx);
69 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
71 return ERR_PTR(-ENOMEM);
73 ctx->conf.def = *chandef;
74 ctx->conf.rx_chains_static = 1;
75 ctx->conf.rx_chains_dynamic = 1;
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);
84 err = drv_add_chanctx(local, ctx);
91 list_add_rcu(&ctx->list, &local->chanctx_list);
96 static void ieee80211_free_chanctx(struct ieee80211_local *local,
97 struct ieee80211_chanctx *ctx)
99 lockdep_assert_held(&local->chanctx_mtx);
101 WARN_ON_ONCE(ctx->refcount != 0);
103 if (!local->use_chanctx) {
104 local->_oper_channel_type = NL80211_CHAN_NO_HT;
105 ieee80211_hw_config(local, 0);
107 drv_remove_chanctx(local, ctx);
110 list_del_rcu(&ctx->list);
111 kfree_rcu(ctx, rcu_head);
114 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
115 struct ieee80211_chanctx *ctx)
117 struct ieee80211_local *local = sdata->local;
120 lockdep_assert_held(&local->chanctx_mtx);
122 ret = drv_assign_vif_chanctx(local, sdata, ctx);
126 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
129 ieee80211_recalc_txpower(sdata);
134 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
135 struct ieee80211_chanctx *ctx)
137 struct ieee80211_chanctx_conf *conf = &ctx->conf;
138 struct ieee80211_sub_if_data *sdata;
139 const struct cfg80211_chan_def *compat = NULL;
141 lockdep_assert_held(&local->chanctx_mtx);
144 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
146 if (!ieee80211_sdata_running(sdata))
148 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
152 compat = &sdata->vif.bss_conf.chandef;
154 compat = cfg80211_chandef_compatible(
155 &sdata->vif.bss_conf.chandef, compat);
161 if (WARN_ON_ONCE(!compat))
164 ieee80211_change_chandef(local, ctx, compat);
167 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
168 struct ieee80211_chanctx *ctx)
170 struct ieee80211_local *local = sdata->local;
172 lockdep_assert_held(&local->chanctx_mtx);
175 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
177 drv_unassign_vif_chanctx(local, sdata, ctx);
179 if (ctx->refcount > 0) {
180 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
181 ieee80211_recalc_smps_chanctx(local, ctx);
185 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
187 struct ieee80211_local *local = sdata->local;
188 struct ieee80211_chanctx_conf *conf;
189 struct ieee80211_chanctx *ctx;
191 lockdep_assert_held(&local->chanctx_mtx);
193 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
194 lockdep_is_held(&local->chanctx_mtx));
198 ctx = container_of(conf, struct ieee80211_chanctx, conf);
200 ieee80211_unassign_vif_chanctx(sdata, ctx);
201 if (ctx->refcount == 0)
202 ieee80211_free_chanctx(local, ctx);
205 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
206 struct ieee80211_chanctx *chanctx)
208 struct ieee80211_sub_if_data *sdata;
209 u8 rx_chains_static, rx_chains_dynamic;
211 lockdep_assert_held(&local->chanctx_mtx);
213 rx_chains_static = 1;
214 rx_chains_dynamic = 1;
217 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
218 u8 needed_static, needed_dynamic;
220 if (!ieee80211_sdata_running(sdata))
223 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
227 switch (sdata->vif.type) {
228 case NL80211_IFTYPE_P2P_DEVICE:
230 case NL80211_IFTYPE_STATION:
231 if (!sdata->u.mgd.associated)
234 case NL80211_IFTYPE_AP_VLAN:
236 case NL80211_IFTYPE_AP:
237 case NL80211_IFTYPE_ADHOC:
238 case NL80211_IFTYPE_WDS:
239 case NL80211_IFTYPE_MESH_POINT:
245 switch (sdata->smps_mode) {
247 WARN_ONCE(1, "Invalid SMPS mode %d\n",
250 case IEEE80211_SMPS_OFF:
251 needed_static = sdata->needed_rx_chains;
252 needed_dynamic = sdata->needed_rx_chains;
254 case IEEE80211_SMPS_DYNAMIC:
256 needed_dynamic = sdata->needed_rx_chains;
258 case IEEE80211_SMPS_STATIC:
264 rx_chains_static = max(rx_chains_static, needed_static);
265 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
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;
275 local->smps_mode = IEEE80211_SMPS_STATIC;
276 ieee80211_hw_config(local, 0);
279 if (rx_chains_static == chanctx->conf.rx_chains_static &&
280 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
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);
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)
292 struct ieee80211_local *local = sdata->local;
293 struct ieee80211_chanctx *ctx;
296 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
298 mutex_lock(&local->chanctx_mtx);
299 __ieee80211_vif_release_channel(sdata);
301 ctx = ieee80211_find_chanctx(local, chandef, mode);
303 ctx = ieee80211_new_chanctx(local, chandef, mode);
309 sdata->vif.bss_conf.chandef = *chandef;
311 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
313 /* if assign fails refcount stays the same */
314 if (ctx->refcount == 0)
315 ieee80211_free_chanctx(local, ctx);
319 ieee80211_recalc_smps_chanctx(local, ctx);
321 mutex_unlock(&local->chanctx_mtx);
325 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
327 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
329 mutex_lock(&sdata->local->chanctx_mtx);
330 __ieee80211_vif_release_channel(sdata);
331 mutex_unlock(&sdata->local->chanctx_mtx);
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,
341 struct ieee80211_local *local = hw_to_local(hw);
342 struct ieee80211_chanctx *ctx;
345 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
346 iter(hw, &ctx->conf, iter_data);
349 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);