chan.c (e89a96f5cc4c39c268c771f52d675e15e3ba8123) chan.c (55de908ab292c03f1eb280f51170ddb9c6b57e31)
1/*
2 * mac80211 - channel management
3 */
4
5#include <linux/nl80211.h>
6#include <net/cfg80211.h>
7#include "ieee80211_i.h"
8#include "driver-ops.h"
9
1/*
2 * mac80211 - channel management
3 */
4
5#include <linux/nl80211.h>
6#include <net/cfg80211.h>
7#include "ieee80211_i.h"
8#include "driver-ops.h"
9
10static enum ieee80211_chan_mode
11__ieee80211_get_channel_mode(struct ieee80211_local *local,
12 struct ieee80211_sub_if_data *ignore)
13{
14 struct ieee80211_sub_if_data *sdata;
15
16 lockdep_assert_held(&local->iflist_mtx);
17
18 list_for_each_entry(sdata, &local->interfaces, list) {
19 if (sdata == ignore)
20 continue;
21
22 if (!ieee80211_sdata_running(sdata))
23 continue;
24
25 switch (sdata->vif.type) {
26 case NL80211_IFTYPE_MONITOR:
27 continue;
28 case NL80211_IFTYPE_STATION:
29 if (!sdata->u.mgd.associated)
30 continue;
31 break;
32 case NL80211_IFTYPE_ADHOC:
33 if (!sdata->u.ibss.ssid_len)
34 continue;
35 if (!sdata->u.ibss.fixed_channel)
36 return CHAN_MODE_HOPPING;
37 break;
38 case NL80211_IFTYPE_AP_VLAN:
39 /* will also have _AP interface */
40 continue;
41 case NL80211_IFTYPE_AP:
42 if (!sdata->u.ap.beacon)
43 continue;
44 break;
45 case NL80211_IFTYPE_MESH_POINT:
46 if (!sdata->wdev.mesh_id_len)
47 continue;
48 break;
49 default:
50 break;
51 }
52
53 return CHAN_MODE_FIXED;
54 }
55
56 return CHAN_MODE_UNDEFINED;
57}
58
59enum ieee80211_chan_mode
60ieee80211_get_channel_mode(struct ieee80211_local *local,
61 struct ieee80211_sub_if_data *ignore)
62{
63 enum ieee80211_chan_mode mode;
64
65 mutex_lock(&local->iflist_mtx);
66 mode = __ieee80211_get_channel_mode(local, ignore);
67 mutex_unlock(&local->iflist_mtx);
68
69 return mode;
70}
71
72static enum nl80211_channel_type
73ieee80211_get_superchan(struct ieee80211_local *local,
74 struct ieee80211_sub_if_data *sdata)
75{
76 enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
77 struct ieee80211_sub_if_data *tmp;
78
79 mutex_lock(&local->iflist_mtx);
80 list_for_each_entry(tmp, &local->interfaces, list) {
81 if (tmp == sdata)
82 continue;
83
84 if (!ieee80211_sdata_running(tmp))
85 continue;
86
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)
91 break;
92
93 superchan = tmp->vif.bss_conf.channel_type;
94 break;
95 case NL80211_CHAN_HT40PLUS:
96 WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
97 superchan = NL80211_CHAN_HT40PLUS;
98 break;
99 case NL80211_CHAN_HT40MINUS:
100 WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
101 superchan = NL80211_CHAN_HT40MINUS;
102 break;
103 }
104 }
105 mutex_unlock(&local->iflist_mtx);
106
107 return superchan;
108}
109
110static bool
111ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
112 enum nl80211_channel_type chantype2,
113 enum nl80211_channel_type *compat)
114{
115 /*
116 * start out with chantype1 being the result,
117 * overwriting later if needed

--- 26 unchanged lines hidden (view full) ---

144 if (chantype2 == chantype1)
145 break;
146 return false;
147 }
148
149 return true;
150}
151
10static bool
11ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
12 enum nl80211_channel_type chantype2,
13 enum nl80211_channel_type *compat)
14{
15 /*
16 * start out with chantype1 being the result,
17 * overwriting later if needed

--- 26 unchanged lines hidden (view full) ---

44 if (chantype2 == chantype1)
45 break;
46 return false;
47 }
48
49 return true;
50}
51
152bool ieee80211_set_channel_type(struct ieee80211_local *local,
153 struct ieee80211_sub_if_data *sdata,
154 enum nl80211_channel_type chantype)
155{
156 enum nl80211_channel_type superchan;
157 enum nl80211_channel_type compatchan;
158
159 superchan = ieee80211_get_superchan(local, sdata);
160 if (!ieee80211_channel_types_are_compatible(superchan, chantype,
161 &compatchan))
162 return false;
163
164 local->_oper_channel_type = compatchan;
165
166 if (sdata)
167 sdata->vif.bss_conf.channel_type = chantype;
168
169 return true;
170}
171
172static void ieee80211_change_chantype(struct ieee80211_local *local,
173 struct ieee80211_chanctx *ctx,
174 enum nl80211_channel_type chantype)
175{
176 if (chantype == ctx->conf.channel_type)
177 return;
178
179 ctx->conf.channel_type = chantype;
180 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
52static void ieee80211_change_chantype(struct ieee80211_local *local,
53 struct ieee80211_chanctx *ctx,
54 enum nl80211_channel_type chantype)
55{
56 if (chantype == ctx->conf.channel_type)
57 return;
58
59 ctx->conf.channel_type = chantype;
60 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
61
62 if (!local->use_chanctx) {
63 local->_oper_channel_type = chantype;
64 ieee80211_hw_config(local, 0);
65 }
181}
182
183static struct ieee80211_chanctx *
184ieee80211_find_chanctx(struct ieee80211_local *local,
185 struct ieee80211_channel *channel,
186 enum nl80211_channel_type channel_type,
187 enum ieee80211_chanctx_mode mode)
188{

--- 41 unchanged lines hidden (view full) ---

230 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
231 if (!ctx)
232 return ERR_PTR(-ENOMEM);
233
234 ctx->conf.channel = channel;
235 ctx->conf.channel_type = channel_type;
236 ctx->mode = mode;
237
66}
67
68static struct ieee80211_chanctx *
69ieee80211_find_chanctx(struct ieee80211_local *local,
70 struct ieee80211_channel *channel,
71 enum nl80211_channel_type channel_type,
72 enum ieee80211_chanctx_mode mode)
73{

--- 41 unchanged lines hidden (view full) ---

115 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
116 if (!ctx)
117 return ERR_PTR(-ENOMEM);
118
119 ctx->conf.channel = channel;
120 ctx->conf.channel_type = channel_type;
121 ctx->mode = mode;
122
238 err = drv_add_chanctx(local, ctx);
239 if (err) {
240 kfree(ctx);
241 return ERR_PTR(err);
123 if (!local->use_chanctx) {
124 local->_oper_channel_type = channel_type;
125 local->_oper_channel = channel;
126 ieee80211_hw_config(local, 0);
127 } else {
128 err = drv_add_chanctx(local, ctx);
129 if (err) {
130 kfree(ctx);
131 return ERR_PTR(err);
132 }
242 }
243
244 list_add(&ctx->list, &local->chanctx_list);
245
246 return ctx;
247}
248
249static void ieee80211_free_chanctx(struct ieee80211_local *local,
250 struct ieee80211_chanctx *ctx)
251{
252 lockdep_assert_held(&local->chanctx_mtx);
253
254 WARN_ON_ONCE(ctx->refcount != 0);
255
133 }
134
135 list_add(&ctx->list, &local->chanctx_list);
136
137 return ctx;
138}
139
140static void ieee80211_free_chanctx(struct ieee80211_local *local,
141 struct ieee80211_chanctx *ctx)
142{
143 lockdep_assert_held(&local->chanctx_mtx);
144
145 WARN_ON_ONCE(ctx->refcount != 0);
146
256 drv_remove_chanctx(local, ctx);
147 if (!local->use_chanctx) {
148 local->_oper_channel_type = NL80211_CHAN_NO_HT;
149 ieee80211_hw_config(local, 0);
150 } else {
151 drv_remove_chanctx(local, ctx);
152 }
257
258 list_del(&ctx->list);
259 kfree_rcu(ctx, rcu_head);
260}
261
262static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
263 struct ieee80211_chanctx *ctx)
264{

--- 89 unchanged lines hidden (view full) ---

354 struct ieee80211_channel *channel,
355 enum nl80211_channel_type channel_type,
356 enum ieee80211_chanctx_mode mode)
357{
358 struct ieee80211_local *local = sdata->local;
359 struct ieee80211_chanctx *ctx;
360 int ret;
361
153
154 list_del(&ctx->list);
155 kfree_rcu(ctx, rcu_head);
156}
157
158static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
159 struct ieee80211_chanctx *ctx)
160{

--- 89 unchanged lines hidden (view full) ---

250 struct ieee80211_channel *channel,
251 enum nl80211_channel_type channel_type,
252 enum ieee80211_chanctx_mode mode)
253{
254 struct ieee80211_local *local = sdata->local;
255 struct ieee80211_chanctx *ctx;
256 int ret;
257
258 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
259
362 mutex_lock(&local->chanctx_mtx);
363 __ieee80211_vif_release_channel(sdata);
364
365 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
366 if (!ctx)
367 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
368 if (IS_ERR(ctx)) {
369 ret = PTR_ERR(ctx);
370 goto out;
371 }
372
260 mutex_lock(&local->chanctx_mtx);
261 __ieee80211_vif_release_channel(sdata);
262
263 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
264 if (!ctx)
265 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
266 if (IS_ERR(ctx)) {
267 ret = PTR_ERR(ctx);
268 goto out;
269 }
270
271 sdata->vif.bss_conf.channel_type = channel_type;
272
373 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
374 if (ret) {
375 /* if assign fails refcount stays the same */
376 if (ctx->refcount == 0)
377 ieee80211_free_chanctx(local, ctx);
378 goto out;
379 }
380
381 out:
382 mutex_unlock(&local->chanctx_mtx);
383 return ret;
384}
385
386void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
387{
273 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
274 if (ret) {
275 /* if assign fails refcount stays the same */
276 if (ctx->refcount == 0)
277 ieee80211_free_chanctx(local, ctx);
278 goto out;
279 }
280
281 out:
282 mutex_unlock(&local->chanctx_mtx);
283 return ret;
284}
285
286void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
287{
288 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
289
388 mutex_lock(&sdata->local->chanctx_mtx);
389 __ieee80211_vif_release_channel(sdata);
390 mutex_unlock(&sdata->local->chanctx_mtx);
391}
290 mutex_lock(&sdata->local->chanctx_mtx);
291 __ieee80211_vif_release_channel(sdata);
292 mutex_unlock(&sdata->local->chanctx_mtx);
293}