2 * mac80211 - channel management
5 #include <linux/nl80211.h>
6 #include <linux/export.h>
7 #include <linux/rtnetlink.h>
8 #include <net/cfg80211.h>
9 #include "ieee80211_i.h"
10 #include "driver-ops.h"
12 static void ieee80211_change_chandef(struct ieee80211_local
*local
,
13 struct ieee80211_chanctx
*ctx
,
14 const struct cfg80211_chan_def
*chandef
)
16 if (cfg80211_chandef_identical(&ctx
->conf
.def
, chandef
))
19 WARN_ON(!cfg80211_chandef_compatible(&ctx
->conf
.def
, chandef
));
21 ctx
->conf
.def
= *chandef
;
22 drv_change_chanctx(local
, ctx
, IEEE80211_CHANCTX_CHANGE_WIDTH
);
24 if (!local
->use_chanctx
) {
25 local
->_oper_channel_type
= cfg80211_get_chandef_type(chandef
);
26 ieee80211_hw_config(local
, 0);
30 static struct ieee80211_chanctx
*
31 ieee80211_find_chanctx(struct ieee80211_local
*local
,
32 const struct cfg80211_chan_def
*chandef
,
33 enum ieee80211_chanctx_mode mode
)
35 struct ieee80211_chanctx
*ctx
;
37 lockdep_assert_held(&local
->chanctx_mtx
);
39 if (mode
== IEEE80211_CHANCTX_EXCLUSIVE
)
42 list_for_each_entry(ctx
, &local
->chanctx_list
, list
) {
43 const struct cfg80211_chan_def
*compat
;
45 if (ctx
->mode
== IEEE80211_CHANCTX_EXCLUSIVE
)
48 compat
= cfg80211_chandef_compatible(&ctx
->conf
.def
, chandef
);
52 ieee80211_change_chandef(local
, ctx
, compat
);
60 static struct ieee80211_chanctx
*
61 ieee80211_new_chanctx(struct ieee80211_local
*local
,
62 const struct cfg80211_chan_def
*chandef
,
63 enum ieee80211_chanctx_mode mode
)
65 struct ieee80211_chanctx
*ctx
;
68 lockdep_assert_held(&local
->chanctx_mtx
);
70 ctx
= kzalloc(sizeof(*ctx
) + local
->hw
.chanctx_data_size
, GFP_KERNEL
);
72 return ERR_PTR(-ENOMEM
);
74 ctx
->conf
.def
= *chandef
;
75 ctx
->conf
.rx_chains_static
= 1;
76 ctx
->conf
.rx_chains_dynamic
= 1;
79 if (!local
->use_chanctx
) {
80 local
->_oper_channel_type
=
81 cfg80211_get_chandef_type(chandef
);
82 local
->_oper_channel
= chandef
->chan
;
83 ieee80211_hw_config(local
, 0);
85 err
= drv_add_chanctx(local
, ctx
);
92 list_add_rcu(&ctx
->list
, &local
->chanctx_list
);
97 static void ieee80211_free_chanctx(struct ieee80211_local
*local
,
98 struct ieee80211_chanctx
*ctx
)
100 lockdep_assert_held(&local
->chanctx_mtx
);
102 WARN_ON_ONCE(ctx
->refcount
!= 0);
104 if (!local
->use_chanctx
) {
105 local
->_oper_channel_type
= NL80211_CHAN_NO_HT
;
106 ieee80211_hw_config(local
, 0);
108 drv_remove_chanctx(local
, ctx
);
111 list_del_rcu(&ctx
->list
);
112 kfree_rcu(ctx
, rcu_head
);
115 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data
*sdata
,
116 struct ieee80211_chanctx
*ctx
)
118 struct ieee80211_local
*local
= sdata
->local
;
121 lockdep_assert_held(&local
->chanctx_mtx
);
123 ret
= drv_assign_vif_chanctx(local
, sdata
, ctx
);
127 rcu_assign_pointer(sdata
->vif
.chanctx_conf
, &ctx
->conf
);
130 ieee80211_recalc_txpower(sdata
);
135 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local
*local
,
136 struct ieee80211_chanctx
*ctx
)
138 struct ieee80211_chanctx_conf
*conf
= &ctx
->conf
;
139 struct ieee80211_sub_if_data
*sdata
;
140 const struct cfg80211_chan_def
*compat
= NULL
;
142 lockdep_assert_held(&local
->chanctx_mtx
);
145 list_for_each_entry_rcu(sdata
, &local
->interfaces
, list
) {
147 if (!ieee80211_sdata_running(sdata
))
149 if (rcu_access_pointer(sdata
->vif
.chanctx_conf
) != conf
)
153 compat
= &sdata
->vif
.bss_conf
.chandef
;
155 compat
= cfg80211_chandef_compatible(
156 &sdata
->vif
.bss_conf
.chandef
, compat
);
162 if (WARN_ON_ONCE(!compat
))
165 ieee80211_change_chandef(local
, ctx
, compat
);
168 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data
*sdata
,
169 struct ieee80211_chanctx
*ctx
)
171 struct ieee80211_local
*local
= sdata
->local
;
173 lockdep_assert_held(&local
->chanctx_mtx
);
176 rcu_assign_pointer(sdata
->vif
.chanctx_conf
, NULL
);
178 drv_unassign_vif_chanctx(local
, sdata
, ctx
);
180 if (ctx
->refcount
> 0) {
181 ieee80211_recalc_chanctx_chantype(sdata
->local
, ctx
);
182 ieee80211_recalc_smps_chanctx(local
, ctx
);
186 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data
*sdata
)
188 struct ieee80211_local
*local
= sdata
->local
;
189 struct ieee80211_chanctx_conf
*conf
;
190 struct ieee80211_chanctx
*ctx
;
192 lockdep_assert_held(&local
->chanctx_mtx
);
194 conf
= rcu_dereference_protected(sdata
->vif
.chanctx_conf
,
195 lockdep_is_held(&local
->chanctx_mtx
));
199 ctx
= container_of(conf
, struct ieee80211_chanctx
, conf
);
201 if (sdata
->vif
.type
== NL80211_IFTYPE_AP
) {
202 struct ieee80211_sub_if_data
*vlan
;
204 /* for the VLAN list */
206 list_for_each_entry(vlan
, &sdata
->u
.ap
.vlans
, u
.vlan
.list
)
207 rcu_assign_pointer(vlan
->vif
.chanctx_conf
, NULL
);
210 ieee80211_unassign_vif_chanctx(sdata
, ctx
);
211 if (ctx
->refcount
== 0)
212 ieee80211_free_chanctx(local
, ctx
);
215 void ieee80211_recalc_smps_chanctx(struct ieee80211_local
*local
,
216 struct ieee80211_chanctx
*chanctx
)
218 struct ieee80211_sub_if_data
*sdata
;
219 u8 rx_chains_static
, rx_chains_dynamic
;
221 lockdep_assert_held(&local
->chanctx_mtx
);
223 rx_chains_static
= 1;
224 rx_chains_dynamic
= 1;
227 list_for_each_entry_rcu(sdata
, &local
->interfaces
, list
) {
228 u8 needed_static
, needed_dynamic
;
230 if (!ieee80211_sdata_running(sdata
))
233 if (rcu_access_pointer(sdata
->vif
.chanctx_conf
) !=
237 switch (sdata
->vif
.type
) {
238 case NL80211_IFTYPE_P2P_DEVICE
:
240 case NL80211_IFTYPE_STATION
:
241 if (!sdata
->u
.mgd
.associated
)
244 case NL80211_IFTYPE_AP_VLAN
:
246 case NL80211_IFTYPE_AP
:
247 case NL80211_IFTYPE_ADHOC
:
248 case NL80211_IFTYPE_WDS
:
249 case NL80211_IFTYPE_MESH_POINT
:
255 switch (sdata
->smps_mode
) {
257 WARN_ONCE(1, "Invalid SMPS mode %d\n",
260 case IEEE80211_SMPS_OFF
:
261 needed_static
= sdata
->needed_rx_chains
;
262 needed_dynamic
= sdata
->needed_rx_chains
;
264 case IEEE80211_SMPS_DYNAMIC
:
266 needed_dynamic
= sdata
->needed_rx_chains
;
268 case IEEE80211_SMPS_STATIC
:
274 rx_chains_static
= max(rx_chains_static
, needed_static
);
275 rx_chains_dynamic
= max(rx_chains_dynamic
, needed_dynamic
);
279 if (!local
->use_chanctx
) {
280 if (rx_chains_static
> 1)
281 local
->smps_mode
= IEEE80211_SMPS_OFF
;
282 else if (rx_chains_dynamic
> 1)
283 local
->smps_mode
= IEEE80211_SMPS_DYNAMIC
;
285 local
->smps_mode
= IEEE80211_SMPS_STATIC
;
286 ieee80211_hw_config(local
, 0);
289 if (rx_chains_static
== chanctx
->conf
.rx_chains_static
&&
290 rx_chains_dynamic
== chanctx
->conf
.rx_chains_dynamic
)
293 chanctx
->conf
.rx_chains_static
= rx_chains_static
;
294 chanctx
->conf
.rx_chains_dynamic
= rx_chains_dynamic
;
295 drv_change_chanctx(local
, chanctx
, IEEE80211_CHANCTX_CHANGE_RX_CHAINS
);
298 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data
*sdata
,
299 const struct cfg80211_chan_def
*chandef
,
300 enum ieee80211_chanctx_mode mode
)
302 struct ieee80211_local
*local
= sdata
->local
;
303 struct ieee80211_chanctx
*ctx
;
306 WARN_ON(sdata
->dev
&& netif_carrier_ok(sdata
->dev
));
308 mutex_lock(&local
->chanctx_mtx
);
309 __ieee80211_vif_release_channel(sdata
);
311 ctx
= ieee80211_find_chanctx(local
, chandef
, mode
);
313 ctx
= ieee80211_new_chanctx(local
, chandef
, mode
);
319 sdata
->vif
.bss_conf
.chandef
= *chandef
;
321 ret
= ieee80211_assign_vif_chanctx(sdata
, ctx
);
323 /* if assign fails refcount stays the same */
324 if (ctx
->refcount
== 0)
325 ieee80211_free_chanctx(local
, ctx
);
329 if (sdata
->vif
.type
== NL80211_IFTYPE_AP
) {
330 struct ieee80211_sub_if_data
*vlan
;
332 /* for the VLAN list */
334 list_for_each_entry(vlan
, &sdata
->u
.ap
.vlans
, u
.vlan
.list
)
335 rcu_assign_pointer(vlan
->vif
.chanctx_conf
, &ctx
->conf
);
338 ieee80211_recalc_smps_chanctx(local
, ctx
);
340 mutex_unlock(&local
->chanctx_mtx
);
344 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data
*sdata
)
346 WARN_ON(sdata
->dev
&& netif_carrier_ok(sdata
->dev
));
348 mutex_lock(&sdata
->local
->chanctx_mtx
);
349 __ieee80211_vif_release_channel(sdata
);
350 mutex_unlock(&sdata
->local
->chanctx_mtx
);
353 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data
*sdata
)
355 struct ieee80211_local
*local
= sdata
->local
;
356 struct ieee80211_sub_if_data
*ap
;
357 struct ieee80211_chanctx_conf
*conf
;
359 if (WARN_ON(sdata
->vif
.type
!= NL80211_IFTYPE_AP_VLAN
|| !sdata
->bss
))
362 ap
= container_of(sdata
->bss
, struct ieee80211_sub_if_data
, u
.ap
);
364 mutex_lock(&local
->chanctx_mtx
);
366 conf
= rcu_dereference_protected(ap
->vif
.chanctx_conf
,
367 lockdep_is_held(&local
->chanctx_mtx
));
368 rcu_assign_pointer(sdata
->vif
.chanctx_conf
, conf
);
369 mutex_unlock(&local
->chanctx_mtx
);
372 void ieee80211_iter_chan_contexts_atomic(
373 struct ieee80211_hw
*hw
,
374 void (*iter
)(struct ieee80211_hw
*hw
,
375 struct ieee80211_chanctx_conf
*chanctx_conf
,
379 struct ieee80211_local
*local
= hw_to_local(hw
);
380 struct ieee80211_chanctx
*ctx
;
383 list_for_each_entry_rcu(ctx
, &local
->chanctx_list
, list
)
384 iter(hw
, &ctx
->conf
, iter_data
);
387 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic
);