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