diff options
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r-- | net/mac80211/chan.c | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 41e1aa69f7aa..bfaa486d928c 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
@@ -118,6 +118,8 @@ ieee80211_new_chanctx(struct ieee80211_local *local, | |||
118 | 118 | ||
119 | ctx->conf.channel = channel; | 119 | ctx->conf.channel = channel; |
120 | ctx->conf.channel_type = channel_type; | 120 | ctx->conf.channel_type = channel_type; |
121 | ctx->conf.rx_chains_static = 1; | ||
122 | ctx->conf.rx_chains_dynamic = 1; | ||
121 | ctx->mode = mode; | 123 | ctx->mode = mode; |
122 | 124 | ||
123 | if (!local->use_chanctx) { | 125 | if (!local->use_chanctx) { |
@@ -222,8 +224,10 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | |||
222 | 224 | ||
223 | drv_unassign_vif_chanctx(local, sdata, ctx); | 225 | drv_unassign_vif_chanctx(local, sdata, ctx); |
224 | 226 | ||
225 | if (ctx->refcount > 0) | 227 | if (ctx->refcount > 0) { |
226 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); | 228 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); |
229 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
230 | } | ||
227 | } | 231 | } |
228 | 232 | ||
229 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | 233 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) |
@@ -246,6 +250,89 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | |||
246 | ieee80211_free_chanctx(local, ctx); | 250 | ieee80211_free_chanctx(local, ctx); |
247 | } | 251 | } |
248 | 252 | ||
253 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | ||
254 | struct ieee80211_chanctx *chanctx) | ||
255 | { | ||
256 | struct ieee80211_sub_if_data *sdata; | ||
257 | u8 rx_chains_static, rx_chains_dynamic; | ||
258 | |||
259 | lockdep_assert_held(&local->chanctx_mtx); | ||
260 | |||
261 | rx_chains_static = 1; | ||
262 | rx_chains_dynamic = 1; | ||
263 | |||
264 | rcu_read_lock(); | ||
265 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | ||
266 | u8 needed_static, needed_dynamic; | ||
267 | |||
268 | if (!ieee80211_sdata_running(sdata)) | ||
269 | continue; | ||
270 | |||
271 | if (rcu_access_pointer(sdata->vif.chanctx_conf) != | ||
272 | &chanctx->conf) | ||
273 | continue; | ||
274 | |||
275 | switch (sdata->vif.type) { | ||
276 | case NL80211_IFTYPE_P2P_DEVICE: | ||
277 | continue; | ||
278 | case NL80211_IFTYPE_STATION: | ||
279 | if (!sdata->u.mgd.associated) | ||
280 | continue; | ||
281 | break; | ||
282 | case NL80211_IFTYPE_AP_VLAN: | ||
283 | continue; | ||
284 | case NL80211_IFTYPE_AP: | ||
285 | case NL80211_IFTYPE_ADHOC: | ||
286 | case NL80211_IFTYPE_WDS: | ||
287 | case NL80211_IFTYPE_MESH_POINT: | ||
288 | break; | ||
289 | default: | ||
290 | WARN_ON_ONCE(1); | ||
291 | } | ||
292 | |||
293 | switch (sdata->smps_mode) { | ||
294 | default: | ||
295 | WARN_ONCE(1, "Invalid SMPS mode %d\n", | ||
296 | sdata->smps_mode); | ||
297 | /* fall through */ | ||
298 | case IEEE80211_SMPS_OFF: | ||
299 | needed_static = sdata->needed_rx_chains; | ||
300 | needed_dynamic = sdata->needed_rx_chains; | ||
301 | break; | ||
302 | case IEEE80211_SMPS_DYNAMIC: | ||
303 | needed_static = 1; | ||
304 | needed_dynamic = sdata->needed_rx_chains; | ||
305 | break; | ||
306 | case IEEE80211_SMPS_STATIC: | ||
307 | needed_static = 1; | ||
308 | needed_dynamic = 1; | ||
309 | break; | ||
310 | } | ||
311 | |||
312 | rx_chains_static = max(rx_chains_static, needed_static); | ||
313 | rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); | ||
314 | } | ||
315 | rcu_read_unlock(); | ||
316 | |||
317 | if (!local->use_chanctx) { | ||
318 | if (rx_chains_static > 1) | ||
319 | local->smps_mode = IEEE80211_SMPS_OFF; | ||
320 | else if (rx_chains_dynamic > 1) | ||
321 | local->smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
322 | else | ||
323 | local->smps_mode = IEEE80211_SMPS_STATIC; | ||
324 | ieee80211_hw_config(local, 0); | ||
325 | } | ||
326 | |||
327 | if (rx_chains_static == chanctx->conf.rx_chains_static && | ||
328 | rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) | ||
329 | return; | ||
330 | |||
331 | chanctx->conf.rx_chains_static = rx_chains_static; | ||
332 | chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; | ||
333 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); | ||
334 | } | ||
335 | |||
249 | int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | 336 | int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, |
250 | struct ieee80211_channel *channel, | 337 | struct ieee80211_channel *channel, |
251 | enum nl80211_channel_type channel_type, | 338 | enum nl80211_channel_type channel_type, |
@@ -278,6 +365,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
278 | goto out; | 365 | goto out; |
279 | } | 366 | } |
280 | 367 | ||
368 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
281 | out: | 369 | out: |
282 | mutex_unlock(&local->chanctx_mtx); | 370 | mutex_unlock(&local->chanctx_mtx); |
283 | return ret; | 371 | return ret; |