]> Pileus Git - ~andy/linux/blob - net/mac80211/chan.c
pppoatm: do not inline pppoatm_may_send()
[~andy/linux] / net / mac80211 / chan.c
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 bool
12 ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
13                                        enum nl80211_channel_type chantype2,
14                                        enum nl80211_channel_type *compat)
15 {
16         /*
17          * start out with chantype1 being the result,
18          * overwriting later if needed
19          */
20         if (compat)
21                 *compat = chantype1;
22
23         switch (chantype1) {
24         case NL80211_CHAN_NO_HT:
25                 if (compat)
26                         *compat = chantype2;
27                 break;
28         case NL80211_CHAN_HT20:
29                 /*
30                  * allow any change that doesn't go to no-HT
31                  * (if it already is no-HT no change is needed)
32                  */
33                 if (chantype2 == NL80211_CHAN_NO_HT)
34                         break;
35                 if (compat)
36                         *compat = chantype2;
37                 break;
38         case NL80211_CHAN_HT40PLUS:
39         case NL80211_CHAN_HT40MINUS:
40                 /* allow smaller bandwidth and same */
41                 if (chantype2 == NL80211_CHAN_NO_HT)
42                         break;
43                 if (chantype2 == NL80211_CHAN_HT20)
44                         break;
45                 if (chantype2 == chantype1)
46                         break;
47                 return false;
48         }
49
50         return true;
51 }
52
53 static void ieee80211_change_chantype(struct ieee80211_local *local,
54                                       struct ieee80211_chanctx *ctx,
55                                       enum nl80211_channel_type chantype)
56 {
57         if (chantype == ctx->conf.channel_type)
58                 return;
59
60         ctx->conf.channel_type = chantype;
61         drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
62
63         if (!local->use_chanctx) {
64                 local->_oper_channel_type = chantype;
65                 ieee80211_hw_config(local, 0);
66         }
67 }
68
69 static struct ieee80211_chanctx *
70 ieee80211_find_chanctx(struct ieee80211_local *local,
71                        struct ieee80211_channel *channel,
72                        enum nl80211_channel_type channel_type,
73                        enum ieee80211_chanctx_mode mode)
74 {
75         struct ieee80211_chanctx *ctx;
76         enum nl80211_channel_type compat_type;
77
78         lockdep_assert_held(&local->chanctx_mtx);
79
80         if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
81                 return NULL;
82         if (WARN_ON(!channel))
83                 return NULL;
84
85         list_for_each_entry(ctx, &local->chanctx_list, list) {
86                 compat_type = ctx->conf.channel_type;
87
88                 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
89                         continue;
90                 if (ctx->conf.channel != channel)
91                         continue;
92                 if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
93                                                             channel_type,
94                                                             &compat_type))
95                         continue;
96
97                 ieee80211_change_chantype(local, ctx, compat_type);
98
99                 return ctx;
100         }
101
102         return NULL;
103 }
104
105 static struct ieee80211_chanctx *
106 ieee80211_new_chanctx(struct ieee80211_local *local,
107                       struct ieee80211_channel *channel,
108                       enum nl80211_channel_type channel_type,
109                       enum ieee80211_chanctx_mode mode)
110 {
111         struct ieee80211_chanctx *ctx;
112         int err;
113
114         lockdep_assert_held(&local->chanctx_mtx);
115
116         ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
117         if (!ctx)
118                 return ERR_PTR(-ENOMEM);
119
120         ctx->conf.channel = channel;
121         ctx->conf.channel_type = channel_type;
122         ctx->conf.rx_chains_static = 1;
123         ctx->conf.rx_chains_dynamic = 1;
124         ctx->mode = mode;
125
126         if (!local->use_chanctx) {
127                 local->_oper_channel_type = channel_type;
128                 local->_oper_channel = channel;
129                 ieee80211_hw_config(local, 0);
130         } else {
131                 err = drv_add_chanctx(local, ctx);
132                 if (err) {
133                         kfree(ctx);
134                         return ERR_PTR(err);
135                 }
136         }
137
138         list_add_rcu(&ctx->list, &local->chanctx_list);
139
140         return ctx;
141 }
142
143 static void ieee80211_free_chanctx(struct ieee80211_local *local,
144                                    struct ieee80211_chanctx *ctx)
145 {
146         lockdep_assert_held(&local->chanctx_mtx);
147
148         WARN_ON_ONCE(ctx->refcount != 0);
149
150         if (!local->use_chanctx) {
151                 local->_oper_channel_type = NL80211_CHAN_NO_HT;
152                 ieee80211_hw_config(local, 0);
153         } else {
154                 drv_remove_chanctx(local, ctx);
155         }
156
157         list_del_rcu(&ctx->list);
158         kfree_rcu(ctx, rcu_head);
159 }
160
161 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
162                                         struct ieee80211_chanctx *ctx)
163 {
164         struct ieee80211_local *local = sdata->local;
165         int ret;
166
167         lockdep_assert_held(&local->chanctx_mtx);
168
169         ret = drv_assign_vif_chanctx(local, sdata, ctx);
170         if (ret)
171                 return ret;
172
173         rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
174         ctx->refcount++;
175
176         return 0;
177 }
178
179 static enum nl80211_channel_type
180 ieee80211_calc_chantype(struct ieee80211_local *local,
181                         struct ieee80211_chanctx *ctx)
182 {
183         struct ieee80211_chanctx_conf *conf = &ctx->conf;
184         struct ieee80211_sub_if_data *sdata;
185         enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
186
187         lockdep_assert_held(&local->chanctx_mtx);
188
189         rcu_read_lock();
190         list_for_each_entry_rcu(sdata, &local->interfaces, list) {
191                 if (!ieee80211_sdata_running(sdata))
192                         continue;
193                 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
194                         continue;
195
196                 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
197                                         sdata->vif.bss_conf.channel_type,
198                                         result, &result));
199         }
200         rcu_read_unlock();
201
202         return result;
203 }
204
205 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
206                                               struct ieee80211_chanctx *ctx)
207 {
208         enum nl80211_channel_type chantype;
209
210         lockdep_assert_held(&local->chanctx_mtx);
211
212         chantype = ieee80211_calc_chantype(local, ctx);
213         ieee80211_change_chantype(local, ctx, chantype);
214 }
215
216 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
217                                            struct ieee80211_chanctx *ctx)
218 {
219         struct ieee80211_local *local = sdata->local;
220
221         lockdep_assert_held(&local->chanctx_mtx);
222
223         ctx->refcount--;
224         rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
225
226         drv_unassign_vif_chanctx(local, sdata, ctx);
227
228         if (ctx->refcount > 0) {
229                 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
230                 ieee80211_recalc_smps_chanctx(local, ctx);
231         }
232 }
233
234 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
235 {
236         struct ieee80211_local *local = sdata->local;
237         struct ieee80211_chanctx_conf *conf;
238         struct ieee80211_chanctx *ctx;
239
240         lockdep_assert_held(&local->chanctx_mtx);
241
242         conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
243                                          lockdep_is_held(&local->chanctx_mtx));
244         if (!conf)
245                 return;
246
247         ctx = container_of(conf, struct ieee80211_chanctx, conf);
248
249         ieee80211_unassign_vif_chanctx(sdata, ctx);
250         if (ctx->refcount == 0)
251                 ieee80211_free_chanctx(local, ctx);
252 }
253
254 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
255                                    struct ieee80211_chanctx *chanctx)
256 {
257         struct ieee80211_sub_if_data *sdata;
258         u8 rx_chains_static, rx_chains_dynamic;
259
260         lockdep_assert_held(&local->chanctx_mtx);
261
262         rx_chains_static = 1;
263         rx_chains_dynamic = 1;
264
265         rcu_read_lock();
266         list_for_each_entry_rcu(sdata, &local->interfaces, list) {
267                 u8 needed_static, needed_dynamic;
268
269                 if (!ieee80211_sdata_running(sdata))
270                         continue;
271
272                 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
273                                                 &chanctx->conf)
274                         continue;
275
276                 switch (sdata->vif.type) {
277                 case NL80211_IFTYPE_P2P_DEVICE:
278                         continue;
279                 case NL80211_IFTYPE_STATION:
280                         if (!sdata->u.mgd.associated)
281                                 continue;
282                         break;
283                 case NL80211_IFTYPE_AP_VLAN:
284                         continue;
285                 case NL80211_IFTYPE_AP:
286                 case NL80211_IFTYPE_ADHOC:
287                 case NL80211_IFTYPE_WDS:
288                 case NL80211_IFTYPE_MESH_POINT:
289                         break;
290                 default:
291                         WARN_ON_ONCE(1);
292                 }
293
294                 switch (sdata->smps_mode) {
295                 default:
296                         WARN_ONCE(1, "Invalid SMPS mode %d\n",
297                                   sdata->smps_mode);
298                         /* fall through */
299                 case IEEE80211_SMPS_OFF:
300                         needed_static = sdata->needed_rx_chains;
301                         needed_dynamic = sdata->needed_rx_chains;
302                         break;
303                 case IEEE80211_SMPS_DYNAMIC:
304                         needed_static = 1;
305                         needed_dynamic = sdata->needed_rx_chains;
306                         break;
307                 case IEEE80211_SMPS_STATIC:
308                         needed_static = 1;
309                         needed_dynamic = 1;
310                         break;
311                 }
312
313                 rx_chains_static = max(rx_chains_static, needed_static);
314                 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
315         }
316         rcu_read_unlock();
317
318         if (!local->use_chanctx) {
319                 if (rx_chains_static > 1)
320                         local->smps_mode = IEEE80211_SMPS_OFF;
321                 else if (rx_chains_dynamic > 1)
322                         local->smps_mode = IEEE80211_SMPS_DYNAMIC;
323                 else
324                         local->smps_mode = IEEE80211_SMPS_STATIC;
325                 ieee80211_hw_config(local, 0);
326         }
327
328         if (rx_chains_static == chanctx->conf.rx_chains_static &&
329             rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
330                 return;
331
332         chanctx->conf.rx_chains_static = rx_chains_static;
333         chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
334         drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
335 }
336
337 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
338                               struct ieee80211_channel *channel,
339                               enum nl80211_channel_type channel_type,
340                               enum ieee80211_chanctx_mode mode)
341 {
342         struct ieee80211_local *local = sdata->local;
343         struct ieee80211_chanctx *ctx;
344         int ret;
345
346         WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
347
348         mutex_lock(&local->chanctx_mtx);
349         __ieee80211_vif_release_channel(sdata);
350
351         ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
352         if (!ctx)
353                 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
354         if (IS_ERR(ctx)) {
355                 ret = PTR_ERR(ctx);
356                 goto out;
357         }
358
359         sdata->vif.bss_conf.channel_type = channel_type;
360
361         ret = ieee80211_assign_vif_chanctx(sdata, ctx);
362         if (ret) {
363                 /* if assign fails refcount stays the same */
364                 if (ctx->refcount == 0)
365                         ieee80211_free_chanctx(local, ctx);
366                 goto out;
367         }
368
369         ieee80211_recalc_smps_chanctx(local, ctx);
370  out:
371         mutex_unlock(&local->chanctx_mtx);
372         return ret;
373 }
374
375 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
376 {
377         WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
378
379         mutex_lock(&sdata->local->chanctx_mtx);
380         __ieee80211_vif_release_channel(sdata);
381         mutex_unlock(&sdata->local->chanctx_mtx);
382 }
383
384 void ieee80211_iter_chan_contexts_atomic(
385         struct ieee80211_hw *hw,
386         void (*iter)(struct ieee80211_hw *hw,
387                      struct ieee80211_chanctx_conf *chanctx_conf,
388                      void *data),
389         void *iter_data)
390 {
391         struct ieee80211_local *local = hw_to_local(hw);
392         struct ieee80211_chanctx *ctx;
393
394         rcu_read_lock();
395         list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
396                 iter(hw, &ctx->conf, iter_data);
397         rcu_read_unlock();
398 }
399 EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);