2 * mac80211 - channel management
5 #include <linux/nl80211.h>
6 #include <net/cfg80211.h>
7 #include "ieee80211_i.h"
8 #include "driver-ops.h"
10 static enum ieee80211_chan_mode
11 __ieee80211_get_channel_mode(struct ieee80211_local *local,
12 struct ieee80211_sub_if_data *ignore)
14 struct ieee80211_sub_if_data *sdata;
16 lockdep_assert_held(&local->iflist_mtx);
18 list_for_each_entry(sdata, &local->interfaces, list) {
22 if (!ieee80211_sdata_running(sdata))
25 switch (sdata->vif.type) {
26 case NL80211_IFTYPE_MONITOR:
28 case NL80211_IFTYPE_STATION:
29 if (!sdata->u.mgd.associated)
32 case NL80211_IFTYPE_ADHOC:
33 if (!sdata->u.ibss.ssid_len)
35 if (!sdata->u.ibss.fixed_channel)
36 return CHAN_MODE_HOPPING;
38 case NL80211_IFTYPE_AP_VLAN:
39 /* will also have _AP interface */
41 case NL80211_IFTYPE_AP:
42 if (!sdata->u.ap.beacon)
45 case NL80211_IFTYPE_MESH_POINT:
46 if (!sdata->wdev.mesh_id_len)
53 return CHAN_MODE_FIXED;
56 return CHAN_MODE_UNDEFINED;
59 enum ieee80211_chan_mode
60 ieee80211_get_channel_mode(struct ieee80211_local *local,
61 struct ieee80211_sub_if_data *ignore)
63 enum ieee80211_chan_mode mode;
65 mutex_lock(&local->iflist_mtx);
66 mode = __ieee80211_get_channel_mode(local, ignore);
67 mutex_unlock(&local->iflist_mtx);
72 static enum nl80211_channel_type
73 ieee80211_get_superchan(struct ieee80211_local *local,
74 struct ieee80211_sub_if_data *sdata)
76 enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
77 struct ieee80211_sub_if_data *tmp;
79 mutex_lock(&local->iflist_mtx);
80 list_for_each_entry(tmp, &local->interfaces, list) {
84 if (!ieee80211_sdata_running(tmp))
87 switch (tmp->vif.bss_conf.channel_type) {
88 case NL80211_CHAN_NO_HT:
89 case NL80211_CHAN_HT20:
90 if (superchan > tmp->vif.bss_conf.channel_type)
93 superchan = tmp->vif.bss_conf.channel_type;
95 case NL80211_CHAN_HT40PLUS:
96 WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
97 superchan = NL80211_CHAN_HT40PLUS;
99 case NL80211_CHAN_HT40MINUS:
100 WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
101 superchan = NL80211_CHAN_HT40MINUS;
105 mutex_unlock(&local->iflist_mtx);
111 ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
112 enum nl80211_channel_type chantype2,
113 enum nl80211_channel_type *compat)
116 * start out with chantype1 being the result,
117 * overwriting later if needed
123 case NL80211_CHAN_NO_HT:
127 case NL80211_CHAN_HT20:
129 * allow any change that doesn't go to no-HT
130 * (if it already is no-HT no change is needed)
132 if (chantype2 == NL80211_CHAN_NO_HT)
137 case NL80211_CHAN_HT40PLUS:
138 case NL80211_CHAN_HT40MINUS:
139 /* allow smaller bandwidth and same */
140 if (chantype2 == NL80211_CHAN_NO_HT)
142 if (chantype2 == NL80211_CHAN_HT20)
144 if (chantype2 == chantype1)
152 bool ieee80211_set_channel_type(struct ieee80211_local *local,
153 struct ieee80211_sub_if_data *sdata,
154 enum nl80211_channel_type chantype)
156 enum nl80211_channel_type superchan;
157 enum nl80211_channel_type compatchan;
159 superchan = ieee80211_get_superchan(local, sdata);
160 if (!ieee80211_channel_types_are_compatible(superchan, chantype,
164 local->_oper_channel_type = compatchan;
167 sdata->vif.bss_conf.channel_type = chantype;
173 static struct ieee80211_chanctx *
174 ieee80211_find_chanctx(struct ieee80211_local *local,
175 struct ieee80211_channel *channel,
176 enum nl80211_channel_type channel_type,
177 enum ieee80211_chanctx_mode mode)
179 struct ieee80211_chanctx *ctx;
181 lockdep_assert_held(&local->chanctx_mtx);
183 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
185 if (WARN_ON(!channel))
188 list_for_each_entry(ctx, &local->chanctx_list, list) {
189 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
191 if (ctx->conf.channel != channel)
193 if (ctx->conf.channel_type != channel_type)
202 static struct ieee80211_chanctx *
203 ieee80211_new_chanctx(struct ieee80211_local *local,
204 struct ieee80211_channel *channel,
205 enum nl80211_channel_type channel_type,
206 enum ieee80211_chanctx_mode mode)
208 struct ieee80211_chanctx *ctx;
211 lockdep_assert_held(&local->chanctx_mtx);
213 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
215 return ERR_PTR(-ENOMEM);
217 ctx->conf.channel = channel;
218 ctx->conf.channel_type = channel_type;
221 err = drv_add_chanctx(local, ctx);
227 list_add(&ctx->list, &local->chanctx_list);
232 static void ieee80211_free_chanctx(struct ieee80211_local *local,
233 struct ieee80211_chanctx *ctx)
235 lockdep_assert_held(&local->chanctx_mtx);
237 WARN_ON_ONCE(ctx->refcount != 0);
239 drv_remove_chanctx(local, ctx);
241 list_del(&ctx->list);
242 kfree_rcu(ctx, rcu_head);
245 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
246 struct ieee80211_chanctx *ctx)
248 struct ieee80211_local *local = sdata->local;
251 lockdep_assert_held(&local->chanctx_mtx);
253 ret = drv_assign_vif_chanctx(local, sdata, ctx);
257 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
263 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
264 struct ieee80211_chanctx *ctx)
266 struct ieee80211_local *local = sdata->local;
268 lockdep_assert_held(&local->chanctx_mtx);
271 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
273 drv_unassign_vif_chanctx(local, sdata, ctx);
276 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
278 struct ieee80211_local *local = sdata->local;
279 struct ieee80211_chanctx_conf *conf;
280 struct ieee80211_chanctx *ctx;
282 lockdep_assert_held(&local->chanctx_mtx);
284 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
285 lockdep_is_held(&local->chanctx_mtx));
289 ctx = container_of(conf, struct ieee80211_chanctx, conf);
291 ieee80211_unassign_vif_chanctx(sdata, ctx);
292 if (ctx->refcount == 0)
293 ieee80211_free_chanctx(local, ctx);
296 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
297 struct ieee80211_channel *channel,
298 enum nl80211_channel_type channel_type,
299 enum ieee80211_chanctx_mode mode)
301 struct ieee80211_local *local = sdata->local;
302 struct ieee80211_chanctx *ctx;
305 mutex_lock(&local->chanctx_mtx);
306 __ieee80211_vif_release_channel(sdata);
308 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
310 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
316 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
318 /* if assign fails refcount stays the same */
319 if (ctx->refcount == 0)
320 ieee80211_free_chanctx(local, ctx);
325 mutex_unlock(&local->chanctx_mtx);
329 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
331 mutex_lock(&sdata->local->chanctx_mtx);
332 __ieee80211_vif_release_channel(sdata);
333 mutex_unlock(&sdata->local->chanctx_mtx);