aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-09-11 08:34:12 -0400
committerJohannes Berg <johannes.berg@intel.com>2012-10-17 05:02:09 -0400
commit04ecd2578e712c301fa1369d2a8f298a2b4b146a (patch)
tree81fc8135db27831f4456b61c3aeb5d332848b449 /net
parent55de908ab292c03f1eb280f51170ddb9c6b57e31 (diff)
mac80211: track needed RX chains for channel contexts
On each channel that the device is operating on, it may need to listen using one or more chains depending on the SMPS settings of the interfaces using it. The previous channel context changes completely removed this ability (before, it was available as the SMPS mode). Add per-context tracking of the required static and dynamic RX chains and notify the driver on changes. To achieve this, track the chains and SMPS mode used on each virtual interface and update the channel context whenever this changes. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c15
-rw-r--r--net/mac80211/chan.c90
-rw-r--r--net/mac80211/debugfs_netdev.c2
-rw-r--r--net/mac80211/ibss.c3
-rw-r--r--net/mac80211/ieee80211_i.h20
-rw-r--r--net/mac80211/iface.c10
-rw-r--r--net/mac80211/main.c18
-rw-r--r--net/mac80211/mlme.c28
-rw-r--r--net/mac80211/status.c15
-rw-r--r--net/mac80211/trace.h13
-rw-r--r--net/mac80211/util.c82
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
229static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 233static 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
253void 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
249int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 336int 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
223static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, 223static 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);
1408void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); 1413void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
1409void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); 1414void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
1410 1415
1416u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
1417
1411/* Spectrum management */ 1418/* Spectrum management */
1412void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 1419void 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);
1555int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, 1562int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
1556 enum ieee80211_smps_mode smps_mode); 1563 enum ieee80211_smps_mode smps_mode);
1557void ieee80211_recalc_smps(struct ieee80211_local *local); 1564void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
1558 1565
1559size_t ieee80211_ie_split(const u8 *ies, size_t ielen, 1566size_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);
1586void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); 1593void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
1587 1594
1595void 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
1130static 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}
373EXPORT_SYMBOL(ieee80211_restart_hw); 373EXPORT_SYMBOL(ieee80211_restart_hw);
374 374
375static 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
384static int ieee80211_ifa_changed(struct notifier_block *nb, 376static 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}
1619EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 1619EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
1620 1620
1621static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, 1621void 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
1640void 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
1685static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) 1641static 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}
1980EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 1936EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
1937
1938u8 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}