diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 15 | ||||
-rw-r--r-- | net/mac80211/chan.c | 90 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 2 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 3 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 20 | ||||
-rw-r--r-- | net/mac80211/iface.c | 10 | ||||
-rw-r--r-- | net/mac80211/main.c | 18 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 28 | ||||
-rw-r--r-- | net/mac80211/status.c | 15 | ||||
-rw-r--r-- | net/mac80211/trace.h | 13 | ||||
-rw-r--r-- | net/mac80211/util.c | 82 |
11 files changed, 205 insertions, 91 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 09c90627fd19..03216b0408c7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -884,6 +884,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
884 | if (old) | 884 | if (old) |
885 | return -EALREADY; | 885 | return -EALREADY; |
886 | 886 | ||
887 | /* TODO: make hostapd tell us what it wants */ | ||
888 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
889 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
890 | |||
887 | err = ieee80211_vif_use_channel(sdata, params->channel, | 891 | err = ieee80211_vif_use_channel(sdata, params->channel, |
888 | params->channel_type, | 892 | params->channel_type, |
889 | IEEE80211_CHANCTX_SHARED); | 893 | IEEE80211_CHANCTX_SHARED); |
@@ -1673,6 +1677,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, | |||
1673 | if (err) | 1677 | if (err) |
1674 | return err; | 1678 | return err; |
1675 | 1679 | ||
1680 | /* can mesh use other SMPS modes? */ | ||
1681 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
1682 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
1683 | |||
1676 | err = ieee80211_vif_use_channel(sdata, setup->channel, | 1684 | err = ieee80211_vif_use_channel(sdata, setup->channel, |
1677 | setup->channel_type, | 1685 | setup->channel_type, |
1678 | IEEE80211_CHANCTX_SHARED); | 1686 | IEEE80211_CHANCTX_SHARED); |
@@ -2052,13 +2060,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
2052 | 2060 | ||
2053 | /* | 2061 | /* |
2054 | * If not associated, or current association is not an HT | 2062 | * If not associated, or current association is not an HT |
2055 | * association, there's no need to send an action frame. | 2063 | * association, there's no need to do anything, just store |
2064 | * the new value until we associate. | ||
2056 | */ | 2065 | */ |
2057 | if (!sdata->u.mgd.associated || | 2066 | if (!sdata->u.mgd.associated || |
2058 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { | 2067 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) |
2059 | ieee80211_recalc_smps(sdata->local); | ||
2060 | return 0; | 2068 | return 0; |
2061 | } | ||
2062 | 2069 | ||
2063 | ap = sdata->u.mgd.associated->bssid; | 2070 | ap = sdata->u.mgd.associated->bssid; |
2064 | 2071 | ||
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; |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 6d5aec9418ee..34e173976573 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -217,7 +217,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | |||
217 | 217 | ||
218 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | 218 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
219 | smps_modes[sdata->u.mgd.req_smps], | 219 | smps_modes[sdata->u.mgd.req_smps], |
220 | smps_modes[sdata->u.mgd.ap_smps]); | 220 | smps_modes[sdata->smps_mode]); |
221 | } | 221 | } |
222 | 222 | ||
223 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | 223 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 34d9235117d9..291c9e07f1bd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -1132,6 +1132,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
1132 | changed |= BSS_CHANGED_HT; | 1132 | changed |= BSS_CHANGED_HT; |
1133 | ieee80211_bss_info_change_notify(sdata, changed); | 1133 | ieee80211_bss_info_change_notify(sdata, changed); |
1134 | 1134 | ||
1135 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
1136 | sdata->needed_rx_chains = sdata->local->rx_chains; | ||
1137 | |||
1135 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | 1138 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); |
1136 | 1139 | ||
1137 | return 0; | 1140 | return 0; |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6660118b46b3..132577d22928 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -433,7 +433,6 @@ struct ieee80211_if_managed { | |||
433 | bool powersave; /* powersave requested for this iface */ | 433 | bool powersave; /* powersave requested for this iface */ |
434 | bool broken_ap; /* AP is broken -- turn off powersave */ | 434 | bool broken_ap; /* AP is broken -- turn off powersave */ |
435 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | 435 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ |
436 | ap_smps, /* smps mode AP thinks we're in */ | ||
437 | driver_smps_mode; /* smps mode request */ | 436 | driver_smps_mode; /* smps mode request */ |
438 | 437 | ||
439 | struct work_struct request_smps_work; | 438 | struct work_struct request_smps_work; |
@@ -728,11 +727,17 @@ struct ieee80211_sub_if_data { | |||
728 | 727 | ||
729 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; | 728 | struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; |
730 | 729 | ||
730 | /* used to reconfigure hardware SM PS */ | ||
731 | struct work_struct recalc_smps; | ||
732 | |||
731 | struct work_struct work; | 733 | struct work_struct work; |
732 | struct sk_buff_head skb_queue; | 734 | struct sk_buff_head skb_queue; |
733 | 735 | ||
734 | bool arp_filter_state; | 736 | bool arp_filter_state; |
735 | 737 | ||
738 | u8 needed_rx_chains; | ||
739 | enum ieee80211_smps_mode smps_mode; | ||
740 | |||
736 | /* | 741 | /* |
737 | * AP this belongs to: self in AP mode and | 742 | * AP this belongs to: self in AP mode and |
738 | * corresponding AP in VLAN mode, NULL for | 743 | * corresponding AP in VLAN mode, NULL for |
@@ -905,9 +910,6 @@ struct ieee80211_local { | |||
905 | /* used for uploading changed mc list */ | 910 | /* used for uploading changed mc list */ |
906 | struct work_struct reconfig_filter; | 911 | struct work_struct reconfig_filter; |
907 | 912 | ||
908 | /* used to reconfigure hardware SM PS */ | ||
909 | struct work_struct recalc_smps; | ||
910 | |||
911 | /* aggregated multicast list */ | 913 | /* aggregated multicast list */ |
912 | struct netdev_hw_addr_list mc_list; | 914 | struct netdev_hw_addr_list mc_list; |
913 | 915 | ||
@@ -944,6 +946,9 @@ struct ieee80211_local { | |||
944 | /* wowlan is enabled -- don't reconfig on resume */ | 946 | /* wowlan is enabled -- don't reconfig on resume */ |
945 | bool wowlan; | 947 | bool wowlan; |
946 | 948 | ||
949 | /* number of RX chains the hardware has */ | ||
950 | u8 rx_chains; | ||
951 | |||
947 | int tx_headroom; /* required headroom for hardware/radiotap */ | 952 | int tx_headroom; /* required headroom for hardware/radiotap */ |
948 | 953 | ||
949 | /* Tasklet and skb queue to process calls from IRQ mode. All frames | 954 | /* Tasklet and skb queue to process calls from IRQ mode. All frames |
@@ -1408,6 +1413,8 @@ void ieee80211_ba_session_work(struct work_struct *work); | |||
1408 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); | 1413 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); |
1409 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); | 1414 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); |
1410 | 1415 | ||
1416 | u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); | ||
1417 | |||
1411 | /* Spectrum management */ | 1418 | /* Spectrum management */ |
1412 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1419 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, |
1413 | struct ieee80211_mgmt *mgmt, | 1420 | struct ieee80211_mgmt *mgmt, |
@@ -1554,7 +1561,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | |||
1554 | enum ieee80211_band band, u32 *basic_rates); | 1561 | enum ieee80211_band band, u32 *basic_rates); |
1555 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 1562 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, |
1556 | enum ieee80211_smps_mode smps_mode); | 1563 | enum ieee80211_smps_mode smps_mode); |
1557 | void ieee80211_recalc_smps(struct ieee80211_local *local); | 1564 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); |
1558 | 1565 | ||
1559 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | 1566 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, |
1560 | const u8 *ids, int n_ids, size_t offset); | 1567 | const u8 *ids, int n_ids, size_t offset); |
@@ -1585,6 +1592,9 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
1585 | enum ieee80211_chanctx_mode mode); | 1592 | enum ieee80211_chanctx_mode mode); |
1586 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); | 1593 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); |
1587 | 1594 | ||
1595 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | ||
1596 | struct ieee80211_chanctx *chanctx); | ||
1597 | |||
1588 | #ifdef CONFIG_MAC80211_NOINLINE | 1598 | #ifdef CONFIG_MAC80211_NOINLINE |
1589 | #define debug_noinline noinline | 1599 | #define debug_noinline noinline |
1590 | #else | 1600 | #else |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7cb8382b19e5..99f2b19c8f0d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -739,6 +739,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
739 | del_timer_sync(&local->dynamic_ps_timer); | 739 | del_timer_sync(&local->dynamic_ps_timer); |
740 | cancel_work_sync(&local->dynamic_ps_enable_work); | 740 | cancel_work_sync(&local->dynamic_ps_enable_work); |
741 | 741 | ||
742 | cancel_work_sync(&sdata->recalc_smps); | ||
743 | |||
742 | /* APs need special treatment */ | 744 | /* APs need special treatment */ |
743 | if (sdata->vif.type == NL80211_IFTYPE_AP) { | 745 | if (sdata->vif.type == NL80211_IFTYPE_AP) { |
744 | struct ieee80211_sub_if_data *vlan, *tmpsdata; | 746 | struct ieee80211_sub_if_data *vlan, *tmpsdata; |
@@ -1125,6 +1127,13 @@ static void ieee80211_iface_work(struct work_struct *work) | |||
1125 | } | 1127 | } |
1126 | } | 1128 | } |
1127 | 1129 | ||
1130 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
1131 | { | ||
1132 | struct ieee80211_sub_if_data *sdata = | ||
1133 | container_of(work, struct ieee80211_sub_if_data, recalc_smps); | ||
1134 | |||
1135 | ieee80211_recalc_smps(sdata); | ||
1136 | } | ||
1128 | 1137 | ||
1129 | /* | 1138 | /* |
1130 | * Helper function to initialise an interface to a specific type. | 1139 | * Helper function to initialise an interface to a specific type. |
@@ -1153,6 +1162,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
1153 | 1162 | ||
1154 | skb_queue_head_init(&sdata->skb_queue); | 1163 | skb_queue_head_init(&sdata->skb_queue); |
1155 | INIT_WORK(&sdata->work, ieee80211_iface_work); | 1164 | INIT_WORK(&sdata->work, ieee80211_iface_work); |
1165 | INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); | ||
1156 | 1166 | ||
1157 | switch (type) { | 1167 | switch (type) { |
1158 | case NL80211_IFTYPE_P2P_GO: | 1168 | case NL80211_IFTYPE_P2P_GO: |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9cb6280aa2f2..2c8969b67851 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -372,14 +372,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) | |||
372 | } | 372 | } |
373 | EXPORT_SYMBOL(ieee80211_restart_hw); | 373 | EXPORT_SYMBOL(ieee80211_restart_hw); |
374 | 374 | ||
375 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
376 | { | ||
377 | struct ieee80211_local *local = | ||
378 | container_of(work, struct ieee80211_local, recalc_smps); | ||
379 | |||
380 | ieee80211_recalc_smps(local); | ||
381 | } | ||
382 | |||
383 | #ifdef CONFIG_INET | 375 | #ifdef CONFIG_INET |
384 | static int ieee80211_ifa_changed(struct notifier_block *nb, | 376 | static int ieee80211_ifa_changed(struct notifier_block *nb, |
385 | unsigned long data, void *arg) | 377 | unsigned long data, void *arg) |
@@ -667,7 +659,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
667 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 659 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
668 | 660 | ||
669 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 661 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
670 | INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); | ||
671 | local->smps_mode = IEEE80211_SMPS_OFF; | 662 | local->smps_mode = IEEE80211_SMPS_OFF; |
672 | 663 | ||
673 | INIT_WORK(&local->dynamic_ps_enable_work, | 664 | INIT_WORK(&local->dynamic_ps_enable_work, |
@@ -773,6 +764,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
773 | if (hw->max_report_rates == 0) | 764 | if (hw->max_report_rates == 0) |
774 | hw->max_report_rates = hw->max_rates; | 765 | hw->max_report_rates = hw->max_rates; |
775 | 766 | ||
767 | local->rx_chains = 1; | ||
768 | |||
776 | /* | 769 | /* |
777 | * generic code guarantees at least one band, | 770 | * generic code guarantees at least one band, |
778 | * set this very early because much code assumes | 771 | * set this very early because much code assumes |
@@ -804,6 +797,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
804 | max_bitrates = sband->n_bitrates; | 797 | max_bitrates = sband->n_bitrates; |
805 | supp_ht = supp_ht || sband->ht_cap.ht_supported; | 798 | supp_ht = supp_ht || sband->ht_cap.ht_supported; |
806 | supp_vht = supp_vht || sband->vht_cap.vht_supported; | 799 | supp_vht = supp_vht || sband->vht_cap.vht_supported; |
800 | |||
801 | if (sband->ht_cap.ht_supported) | ||
802 | local->rx_chains = | ||
803 | max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), | ||
804 | local->rx_chains); | ||
805 | |||
806 | /* TODO: consider VHT for RX chains, hopefully it's the same */ | ||
807 | } | 807 | } |
808 | 808 | ||
809 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + | 809 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4add50063161..f3f338541b01 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -543,7 +543,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
543 | 543 | ||
544 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 544 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) |
545 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, | 545 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, |
546 | sband, chan, ifmgd->ap_smps); | 546 | sband, chan, sdata->smps_mode); |
547 | 547 | ||
548 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 548 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) |
549 | ieee80211_add_vht_ie(sdata, skb, sband); | 549 | ieee80211_add_vht_ie(sdata, skb, sband); |
@@ -1392,7 +1392,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1392 | ieee80211_recalc_ps(local, -1); | 1392 | ieee80211_recalc_ps(local, -1); |
1393 | mutex_unlock(&local->iflist_mtx); | 1393 | mutex_unlock(&local->iflist_mtx); |
1394 | 1394 | ||
1395 | ieee80211_recalc_smps(local); | 1395 | ieee80211_recalc_smps(sdata); |
1396 | ieee80211_recalc_ps_vif(sdata); | 1396 | ieee80211_recalc_ps_vif(sdata); |
1397 | 1397 | ||
1398 | netif_tx_start_all_queues(sdata->dev); | 1398 | netif_tx_start_all_queues(sdata->dev); |
@@ -3157,6 +3157,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
3157 | } | 3157 | } |
3158 | 3158 | ||
3159 | if (ht_oper) { | 3159 | if (ht_oper) { |
3160 | const u8 *ht_cap_ie; | ||
3161 | const struct ieee80211_ht_cap *ht_cap; | ||
3162 | u8 chains = 1; | ||
3163 | |||
3160 | channel_type = NL80211_CHAN_HT20; | 3164 | channel_type = NL80211_CHAN_HT20; |
3161 | 3165 | ||
3162 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | 3166 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { |
@@ -3170,8 +3174,22 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
3170 | break; | 3174 | break; |
3171 | } | 3175 | } |
3172 | } | 3176 | } |
3177 | |||
3178 | ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, | ||
3179 | cbss->information_elements, | ||
3180 | cbss->len_information_elements); | ||
3181 | if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { | ||
3182 | ht_cap = (void *)(ht_cap_ie + 2); | ||
3183 | chains = ieee80211_mcs_to_chains(&ht_cap->mcs); | ||
3184 | } | ||
3185 | sdata->needed_rx_chains = min(chains, local->rx_chains); | ||
3186 | } else { | ||
3187 | sdata->needed_rx_chains = 1; | ||
3173 | } | 3188 | } |
3174 | 3189 | ||
3190 | /* will change later if needed */ | ||
3191 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
3192 | |||
3175 | ieee80211_vif_release_channel(sdata); | 3193 | ieee80211_vif_release_channel(sdata); |
3176 | return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, | 3194 | return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, |
3177 | IEEE80211_CHANCTX_SHARED); | 3195 | IEEE80211_CHANCTX_SHARED); |
@@ -3485,11 +3503,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3485 | 3503 | ||
3486 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | 3504 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { |
3487 | if (ifmgd->powersave) | 3505 | if (ifmgd->powersave) |
3488 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | 3506 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; |
3489 | else | 3507 | else |
3490 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | 3508 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
3491 | } else | 3509 | } else |
3492 | ifmgd->ap_smps = ifmgd->req_smps; | 3510 | sdata->smps_mode = ifmgd->req_smps; |
3493 | 3511 | ||
3494 | assoc_data->capability = req->bss->capability; | 3512 | assoc_data->capability = req->bss->capability; |
3495 | assoc_data->wmm = bss->wmm_used && | 3513 | assoc_data->wmm = bss->wmm_used && |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3af0cc4130f1..21fa5c72ea14 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -189,30 +189,31 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | |||
189 | } | 189 | } |
190 | 190 | ||
191 | if (ieee80211_is_action(mgmt->frame_control) && | 191 | if (ieee80211_is_action(mgmt->frame_control) && |
192 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
193 | mgmt->u.action.category == WLAN_CATEGORY_HT && | 192 | mgmt->u.action.category == WLAN_CATEGORY_HT && |
194 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { | 193 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && |
194 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
195 | ieee80211_sdata_running(sdata)) { | ||
195 | /* | 196 | /* |
196 | * This update looks racy, but isn't -- if we come | 197 | * This update looks racy, but isn't -- if we come |
197 | * here we've definitely got a station that we're | 198 | * here we've definitely got a station that we're |
198 | * talking to, and on a managed interface that can | 199 | * talking to, and on a managed interface that can |
199 | * only be the AP. And the only other place updating | 200 | * only be the AP. And the only other place updating |
200 | * this variable is before we're associated. | 201 | * this variable in managed mode is before association. |
201 | */ | 202 | */ |
202 | switch (mgmt->u.action.u.ht_smps.smps_control) { | 203 | switch (mgmt->u.action.u.ht_smps.smps_control) { |
203 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | 204 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: |
204 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; | 205 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; |
205 | break; | 206 | break; |
206 | case WLAN_HT_SMPS_CONTROL_STATIC: | 207 | case WLAN_HT_SMPS_CONTROL_STATIC: |
207 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; | 208 | sdata->smps_mode = IEEE80211_SMPS_STATIC; |
208 | break; | 209 | break; |
209 | case WLAN_HT_SMPS_CONTROL_DISABLED: | 210 | case WLAN_HT_SMPS_CONTROL_DISABLED: |
210 | default: /* shouldn't happen since we don't send that */ | 211 | default: /* shouldn't happen since we don't send that */ |
211 | sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; | 212 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
212 | break; | 213 | break; |
213 | } | 214 | } |
214 | 215 | ||
215 | ieee80211_queue_work(&local->hw, &local->recalc_smps); | 216 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); |
216 | } | 217 | } |
217 | } | 218 | } |
218 | 219 | ||
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a3f5fe2a84a8..629364705f7b 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
@@ -29,11 +29,16 @@ | |||
29 | #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" | 29 | #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" |
30 | 30 | ||
31 | #define CHANCTX_ENTRY __field(int, freq) \ | 31 | #define CHANCTX_ENTRY __field(int, freq) \ |
32 | __field(int, chantype) | 32 | __field(int, chantype) \ |
33 | __field(u8, rx_chains_static) \ | ||
34 | __field(u8, rx_chains_dynamic) | ||
33 | #define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ | 35 | #define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ |
34 | __entry->chantype = ctx->conf.channel_type | 36 | __entry->chantype = ctx->conf.channel_type; \ |
35 | #define CHANCTX_PR_FMT " freq:%d MHz chantype:%d" | 37 | __entry->rx_chains_static = ctx->conf.rx_chains_static; \ |
36 | #define CHANCTX_PR_ARG __entry->freq, __entry->chantype | 38 | __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic |
39 | #define CHANCTX_PR_FMT " freq:%d MHz chantype:%d chains:%d/%d" | ||
40 | #define CHANCTX_PR_ARG __entry->freq, __entry->chantype, \ | ||
41 | __entry->rx_chains_static, __entry->rx_chains_dynamic | ||
37 | 42 | ||
38 | 43 | ||
39 | 44 | ||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7d737071dedb..b732e219b107 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1618,68 +1618,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif) | |||
1618 | } | 1618 | } |
1619 | EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); | 1619 | EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); |
1620 | 1620 | ||
1621 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | 1621 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) |
1622 | enum ieee80211_smps_mode *smps_mode) | ||
1623 | { | 1622 | { |
1624 | if (ifmgd->associated) { | 1623 | struct ieee80211_local *local = sdata->local; |
1625 | *smps_mode = ifmgd->ap_smps; | 1624 | struct ieee80211_chanctx_conf *chanctx_conf; |
1626 | 1625 | struct ieee80211_chanctx *chanctx; | |
1627 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
1628 | if (ifmgd->powersave) | ||
1629 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
1630 | else | ||
1631 | *smps_mode = IEEE80211_SMPS_OFF; | ||
1632 | } | ||
1633 | |||
1634 | return 1; | ||
1635 | } | ||
1636 | |||
1637 | return 0; | ||
1638 | } | ||
1639 | |||
1640 | void ieee80211_recalc_smps(struct ieee80211_local *local) | ||
1641 | { | ||
1642 | struct ieee80211_sub_if_data *sdata; | ||
1643 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
1644 | int count = 0; | ||
1645 | |||
1646 | mutex_lock(&local->iflist_mtx); | ||
1647 | |||
1648 | /* | ||
1649 | * This function could be improved to handle multiple | ||
1650 | * interfaces better, but right now it makes any | ||
1651 | * non-station interfaces force SM PS to be turned | ||
1652 | * off. If there are multiple station interfaces it | ||
1653 | * could also use the best possible mode, e.g. if | ||
1654 | * one is in static and the other in dynamic then | ||
1655 | * dynamic is ok. | ||
1656 | */ | ||
1657 | |||
1658 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
1659 | if (!ieee80211_sdata_running(sdata)) | ||
1660 | continue; | ||
1661 | if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) | ||
1662 | continue; | ||
1663 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
1664 | goto set; | ||
1665 | 1626 | ||
1666 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | 1627 | mutex_lock(&local->chanctx_mtx); |
1667 | 1628 | ||
1668 | if (count > 1) { | 1629 | chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
1669 | smps_mode = IEEE80211_SMPS_OFF; | 1630 | lockdep_is_held(&local->chanctx_mtx)); |
1670 | break; | ||
1671 | } | ||
1672 | } | ||
1673 | 1631 | ||
1674 | if (smps_mode == local->smps_mode) | 1632 | if (WARN_ON_ONCE(!chanctx_conf)) |
1675 | goto unlock; | 1633 | goto unlock; |
1676 | 1634 | ||
1677 | set: | 1635 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); |
1678 | local->smps_mode = smps_mode; | 1636 | ieee80211_recalc_smps_chanctx(local, chanctx); |
1679 | /* changed flag is auto-detected for this */ | ||
1680 | ieee80211_hw_config(local, 0); | ||
1681 | unlock: | 1637 | unlock: |
1682 | mutex_unlock(&local->iflist_mtx); | 1638 | mutex_unlock(&local->chanctx_mtx); |
1683 | } | 1639 | } |
1684 | 1640 | ||
1685 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | 1641 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) |
@@ -1978,3 +1934,19 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif) | |||
1978 | return ifmgd->ave_beacon_signal; | 1934 | return ifmgd->ave_beacon_signal; |
1979 | } | 1935 | } |
1980 | EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); | 1936 | EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); |
1937 | |||
1938 | u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) | ||
1939 | { | ||
1940 | if (!mcs) | ||
1941 | return 1; | ||
1942 | |||
1943 | /* TODO: consider rx_highest */ | ||
1944 | |||
1945 | if (mcs->rx_mask[3]) | ||
1946 | return 4; | ||
1947 | if (mcs->rx_mask[2]) | ||
1948 | return 3; | ||
1949 | if (mcs->rx_mask[1]) | ||
1950 | return 2; | ||
1951 | return 1; | ||
1952 | } | ||