summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-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}