diff options
-rw-r--r-- | net/mac80211/cfg.c | 108 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 25 | ||||
-rw-r--r-- | net/mac80211/ht.c | 41 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 15 | ||||
-rw-r--r-- | net/mac80211/iface.c | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 72 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 5 | ||||
-rw-r--r-- | net/mac80211/status.c | 31 | ||||
-rw-r--r-- | net/mac80211/util.c | 25 |
10 files changed, 289 insertions, 38 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ac28af74a414..4a3d5a414a25 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) | |||
1059 | /* abort any running channel switch */ | 1059 | /* abort any running channel switch */ |
1060 | sdata->vif.csa_active = false; | 1060 | sdata->vif.csa_active = false; |
1061 | cancel_work_sync(&sdata->csa_finalize_work); | 1061 | cancel_work_sync(&sdata->csa_finalize_work); |
1062 | cancel_work_sync(&sdata->u.ap.request_smps_work); | ||
1062 | 1063 | ||
1063 | /* turn off carrier for this interface and dependent VLANs */ | 1064 | /* turn off carrier for this interface and dependent VLANs */ |
1064 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | 1065 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) |
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy, | |||
1553 | 1554 | ||
1554 | mutex_unlock(&local->sta_mtx); | 1555 | mutex_unlock(&local->sta_mtx); |
1555 | 1556 | ||
1557 | if ((sdata->vif.type == NL80211_IFTYPE_AP || | ||
1558 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && | ||
1559 | sta->known_smps_mode != sta->sdata->bss->req_smps && | ||
1560 | test_sta_flag(sta, WLAN_STA_AUTHORIZED) && | ||
1561 | sta_info_tx_streams(sta) != 1) { | ||
1562 | ht_dbg(sta->sdata, | ||
1563 | "%pM just authorized and MIMO capable - update SMPS\n", | ||
1564 | sta->sta.addr); | ||
1565 | ieee80211_send_smps_action(sta->sdata, | ||
1566 | sta->sdata->bss->req_smps, | ||
1567 | sta->sta.addr, | ||
1568 | sta->sdata->vif.bss_conf.bssid); | ||
1569 | } | ||
1570 | |||
1556 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | 1571 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
1557 | params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { | 1572 | params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { |
1558 | ieee80211_recalc_ps(local, -1); | 1573 | ieee80211_recalc_ps(local, -1); |
@@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy, | |||
2337 | } | 2352 | } |
2338 | #endif | 2353 | #endif |
2339 | 2354 | ||
2340 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 2355 | int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, |
2341 | enum ieee80211_smps_mode smps_mode) | 2356 | enum ieee80211_smps_mode smps_mode) |
2357 | { | ||
2358 | struct sta_info *sta; | ||
2359 | enum ieee80211_smps_mode old_req; | ||
2360 | int i; | ||
2361 | |||
2362 | if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
2363 | return -EINVAL; | ||
2364 | |||
2365 | if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) | ||
2366 | return 0; | ||
2367 | |||
2368 | old_req = sdata->u.ap.req_smps; | ||
2369 | sdata->u.ap.req_smps = smps_mode; | ||
2370 | |||
2371 | /* AUTOMATIC doesn't mean much for AP - don't allow it */ | ||
2372 | if (old_req == smps_mode || | ||
2373 | smps_mode == IEEE80211_SMPS_AUTOMATIC) | ||
2374 | return 0; | ||
2375 | |||
2376 | /* If no associated stations, there's no need to do anything */ | ||
2377 | if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { | ||
2378 | sdata->smps_mode = smps_mode; | ||
2379 | ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); | ||
2380 | return 0; | ||
2381 | } | ||
2382 | |||
2383 | ht_dbg(sdata, | ||
2384 | "SMSP %d requested in AP mode, sending Action frame to %d stations\n", | ||
2385 | smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); | ||
2386 | |||
2387 | mutex_lock(&sdata->local->sta_mtx); | ||
2388 | for (i = 0; i < STA_HASH_SIZE; i++) { | ||
2389 | for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], | ||
2390 | lockdep_is_held(&sdata->local->sta_mtx)); | ||
2391 | sta; | ||
2392 | sta = rcu_dereference_protected(sta->hnext, | ||
2393 | lockdep_is_held(&sdata->local->sta_mtx))) { | ||
2394 | /* | ||
2395 | * Only stations associated to our AP and | ||
2396 | * associated VLANs | ||
2397 | */ | ||
2398 | if (sta->sdata->bss != &sdata->u.ap) | ||
2399 | continue; | ||
2400 | |||
2401 | /* This station doesn't support MIMO - skip it */ | ||
2402 | if (sta_info_tx_streams(sta) == 1) | ||
2403 | continue; | ||
2404 | |||
2405 | /* | ||
2406 | * Don't wake up a STA just to send the action frame | ||
2407 | * unless we are getting more restrictive. | ||
2408 | */ | ||
2409 | if (test_sta_flag(sta, WLAN_STA_PS_STA) && | ||
2410 | !ieee80211_smps_is_restrictive(sta->known_smps_mode, | ||
2411 | smps_mode)) { | ||
2412 | ht_dbg(sdata, | ||
2413 | "Won't send SMPS to sleeping STA %pM\n", | ||
2414 | sta->sta.addr); | ||
2415 | continue; | ||
2416 | } | ||
2417 | |||
2418 | /* | ||
2419 | * If the STA is not authorized, wait until it gets | ||
2420 | * authorized and the action frame will be sent then. | ||
2421 | */ | ||
2422 | if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) | ||
2423 | continue; | ||
2424 | |||
2425 | ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); | ||
2426 | ieee80211_send_smps_action(sdata, smps_mode, | ||
2427 | sta->sta.addr, | ||
2428 | sdata->vif.bss_conf.bssid); | ||
2429 | } | ||
2430 | } | ||
2431 | mutex_unlock(&sdata->local->sta_mtx); | ||
2432 | |||
2433 | sdata->smps_mode = smps_mode; | ||
2434 | ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); | ||
2435 | |||
2436 | return 0; | ||
2437 | } | ||
2438 | |||
2439 | int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, | ||
2440 | enum ieee80211_smps_mode smps_mode) | ||
2342 | { | 2441 | { |
2343 | const u8 *ap; | 2442 | const u8 *ap; |
2344 | enum ieee80211_smps_mode old_req; | 2443 | enum ieee80211_smps_mode old_req; |
@@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
2346 | 2445 | ||
2347 | lockdep_assert_held(&sdata->wdev.mtx); | 2446 | lockdep_assert_held(&sdata->wdev.mtx); |
2348 | 2447 | ||
2448 | if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) | ||
2449 | return -EINVAL; | ||
2450 | |||
2349 | old_req = sdata->u.mgd.req_smps; | 2451 | old_req = sdata->u.mgd.req_smps; |
2350 | sdata->u.mgd.req_smps = smps_mode; | 2452 | sdata->u.mgd.req_smps = smps_mode; |
2351 | 2453 | ||
@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
2402 | 2504 | ||
2403 | /* no change, but if automatic follow powersave */ | 2505 | /* no change, but if automatic follow powersave */ |
2404 | sdata_lock(sdata); | 2506 | sdata_lock(sdata); |
2405 | __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); | 2507 | __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps); |
2406 | sdata_unlock(sdata); | 2508 | sdata_unlock(sdata); |
2407 | 2509 | ||
2408 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) | 2510 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cafe614ef93d..04b5a14c8a05 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, | |||
224 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) | 224 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) |
225 | return -EINVAL; | 225 | return -EINVAL; |
226 | 226 | ||
227 | /* supported only on managed interfaces for now */ | 227 | if (sdata->vif.type != NL80211_IFTYPE_STATION && |
228 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 228 | sdata->vif.type != NL80211_IFTYPE_AP) |
229 | return -EOPNOTSUPP; | 229 | return -EOPNOTSUPP; |
230 | 230 | ||
231 | sdata_lock(sdata); | 231 | sdata_lock(sdata); |
232 | err = __ieee80211_request_smps(sdata, smps_mode); | 232 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
233 | err = __ieee80211_request_smps_mgd(sdata, smps_mode); | ||
234 | else | ||
235 | err = __ieee80211_request_smps_ap(sdata, smps_mode); | ||
233 | sdata_unlock(sdata); | 236 | sdata_unlock(sdata); |
234 | 237 | ||
235 | return err; | 238 | return err; |
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { | |||
245 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | 248 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, |
246 | char *buf, int buflen) | 249 | char *buf, int buflen) |
247 | { | 250 | { |
248 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 251 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
249 | return -EOPNOTSUPP; | 252 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
250 | 253 | smps_modes[sdata->u.mgd.req_smps], | |
251 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | 254 | smps_modes[sdata->smps_mode]); |
252 | smps_modes[sdata->u.mgd.req_smps], | 255 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
253 | smps_modes[sdata->smps_mode]); | 256 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
257 | smps_modes[sdata->u.ap.req_smps], | ||
258 | smps_modes[sdata->smps_mode]); | ||
259 | return -EINVAL; | ||
254 | } | 260 | } |
255 | 261 | ||
256 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | 262 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, |
@@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) | |||
563 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) | 569 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) |
564 | { | 570 | { |
565 | DEBUGFS_ADD(num_mcast_sta); | 571 | DEBUGFS_ADD(num_mcast_sta); |
572 | DEBUGFS_ADD_MODE(smps, 0600); | ||
566 | DEBUGFS_ADD(num_sta_ps); | 573 | DEBUGFS_ADD(num_sta_ps); |
567 | DEBUGFS_ADD(dtim_count); | 574 | DEBUGFS_ADD(dtim_count); |
568 | DEBUGFS_ADD(num_buffered_multicast); | 575 | DEBUGFS_ADD(num_buffered_multicast); |
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 529bf58bc145..9a8be8f69224 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | |||
448 | return 0; | 448 | return 0; |
449 | } | 449 | } |
450 | 450 | ||
451 | void ieee80211_request_smps_work(struct work_struct *work) | 451 | void ieee80211_request_smps_mgd_work(struct work_struct *work) |
452 | { | 452 | { |
453 | struct ieee80211_sub_if_data *sdata = | 453 | struct ieee80211_sub_if_data *sdata = |
454 | container_of(work, struct ieee80211_sub_if_data, | 454 | container_of(work, struct ieee80211_sub_if_data, |
455 | u.mgd.request_smps_work); | 455 | u.mgd.request_smps_work); |
456 | 456 | ||
457 | sdata_lock(sdata); | 457 | sdata_lock(sdata); |
458 | __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); | 458 | __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); |
459 | sdata_unlock(sdata); | ||
460 | } | ||
461 | |||
462 | void ieee80211_request_smps_ap_work(struct work_struct *work) | ||
463 | { | ||
464 | struct ieee80211_sub_if_data *sdata = | ||
465 | container_of(work, struct ieee80211_sub_if_data, | ||
466 | u.ap.request_smps_work); | ||
467 | |||
468 | sdata_lock(sdata); | ||
469 | __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode); | ||
459 | sdata_unlock(sdata); | 470 | sdata_unlock(sdata); |
460 | } | 471 | } |
461 | 472 | ||
@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, | |||
464 | { | 475 | { |
465 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 476 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
466 | 477 | ||
467 | if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) | 478 | if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && |
479 | vif->type != NL80211_IFTYPE_AP)) | ||
468 | return; | 480 | return; |
469 | 481 | ||
470 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) | 482 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) |
471 | smps_mode = IEEE80211_SMPS_AUTOMATIC; | 483 | smps_mode = IEEE80211_SMPS_AUTOMATIC; |
472 | 484 | ||
473 | if (sdata->u.mgd.driver_smps_mode == smps_mode) | 485 | if (vif->type == NL80211_IFTYPE_STATION) { |
474 | return; | 486 | if (sdata->u.mgd.driver_smps_mode == smps_mode) |
475 | 487 | return; | |
476 | sdata->u.mgd.driver_smps_mode = smps_mode; | 488 | sdata->u.mgd.driver_smps_mode = smps_mode; |
477 | 489 | ieee80211_queue_work(&sdata->local->hw, | |
478 | ieee80211_queue_work(&sdata->local->hw, | 490 | &sdata->u.mgd.request_smps_work); |
479 | &sdata->u.mgd.request_smps_work); | 491 | } else { |
492 | /* AUTOMATIC is meaningless in AP mode */ | ||
493 | if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) | ||
494 | return; | ||
495 | if (sdata->u.ap.driver_smps_mode == smps_mode) | ||
496 | return; | ||
497 | sdata->u.ap.driver_smps_mode = smps_mode; | ||
498 | ieee80211_queue_work(&sdata->local->hw, | ||
499 | &sdata->u.ap.request_smps_work); | ||
500 | } | ||
480 | } | 501 | } |
481 | /* this might change ... don't want non-open drivers using it */ | 502 | /* this might change ... don't want non-open drivers using it */ |
482 | EXPORT_SYMBOL_GPL(ieee80211_request_smps); | 503 | EXPORT_SYMBOL_GPL(ieee80211_request_smps); |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3a87c8976a32..8340d4939f1b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -262,6 +262,10 @@ struct ieee80211_if_ap { | |||
262 | 262 | ||
263 | struct ps_data ps; | 263 | struct ps_data ps; |
264 | atomic_t num_mcast_sta; /* number of stations receiving multicast */ | 264 | atomic_t num_mcast_sta; /* number of stations receiving multicast */ |
265 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | ||
266 | driver_smps_mode; /* smps mode request */ | ||
267 | |||
268 | struct work_struct request_smps_work; | ||
265 | }; | 269 | }; |
266 | 270 | ||
267 | struct ieee80211_if_wds { | 271 | struct ieee80211_if_wds { |
@@ -1435,7 +1439,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | |||
1435 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | 1439 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, |
1436 | enum ieee80211_smps_mode smps, const u8 *da, | 1440 | enum ieee80211_smps_mode smps, const u8 *da, |
1437 | const u8 *bssid); | 1441 | const u8 *bssid); |
1438 | void ieee80211_request_smps_work(struct work_struct *work); | 1442 | void ieee80211_request_smps_ap_work(struct work_struct *work); |
1443 | void ieee80211_request_smps_mgd_work(struct work_struct *work); | ||
1444 | bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, | ||
1445 | enum ieee80211_smps_mode smps_mode_new); | ||
1439 | 1446 | ||
1440 | void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | 1447 | void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, |
1441 | u16 initiator, u16 reason, bool stop); | 1448 | u16 initiator, u16 reason, bool stop); |
@@ -1653,8 +1660,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
1653 | u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, | 1660 | u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, |
1654 | struct ieee802_11_elems *elems, | 1661 | struct ieee802_11_elems *elems, |
1655 | enum ieee80211_band band, u32 *basic_rates); | 1662 | enum ieee80211_band band, u32 *basic_rates); |
1656 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 1663 | int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, |
1657 | enum ieee80211_smps_mode smps_mode); | 1664 | enum ieee80211_smps_mode smps_mode); |
1665 | int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, | ||
1666 | enum ieee80211_smps_mode smps_mode); | ||
1658 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); | 1667 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); |
1659 | 1668 | ||
1660 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | 1669 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e48f103b9ade..ff101ea1d9ae 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
1293 | case NL80211_IFTYPE_AP: | 1293 | case NL80211_IFTYPE_AP: |
1294 | skb_queue_head_init(&sdata->u.ap.ps.bc_buf); | 1294 | skb_queue_head_init(&sdata->u.ap.ps.bc_buf); |
1295 | INIT_LIST_HEAD(&sdata->u.ap.vlans); | 1295 | INIT_LIST_HEAD(&sdata->u.ap.vlans); |
1296 | INIT_WORK(&sdata->u.ap.request_smps_work, | ||
1297 | ieee80211_request_smps_ap_work); | ||
1296 | sdata->vif.bss_conf.bssid = sdata->vif.addr; | 1298 | sdata->vif.bss_conf.bssid = sdata->vif.addr; |
1299 | sdata->u.ap.req_smps = IEEE80211_SMPS_OFF; | ||
1297 | break; | 1300 | break; |
1298 | case NL80211_IFTYPE_P2P_CLIENT: | 1301 | case NL80211_IFTYPE_P2P_CLIENT: |
1299 | type = NL80211_IFTYPE_STATION; | 1302 | type = NL80211_IFTYPE_STATION; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 91cc8281e266..5cc1c274dd61 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -3499,7 +3499,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
3499 | ieee80211_beacon_connection_loss_work); | 3499 | ieee80211_beacon_connection_loss_work); |
3500 | INIT_WORK(&ifmgd->csa_connection_drop_work, | 3500 | INIT_WORK(&ifmgd->csa_connection_drop_work, |
3501 | ieee80211_csa_connection_drop_work); | 3501 | ieee80211_csa_connection_drop_work); |
3502 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); | 3502 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work); |
3503 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, | 3503 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, |
3504 | (unsigned long) sdata); | 3504 | (unsigned long) sdata); |
3505 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, | 3505 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aeb967a0aeed..1eb66e26e49d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
385 | sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); | 385 | sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); |
386 | 386 | ||
387 | sta->sta.smps_mode = IEEE80211_SMPS_OFF; | 387 | sta->sta.smps_mode = IEEE80211_SMPS_OFF; |
388 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
389 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||
390 | struct ieee80211_supported_band *sband = | ||
391 | local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; | ||
392 | u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> | ||
393 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
394 | /* | ||
395 | * Assume that hostapd advertises our caps in the beacon and | ||
396 | * this is the known_smps_mode for a station that just assciated | ||
397 | */ | ||
398 | switch (smps) { | ||
399 | case WLAN_HT_SMPS_CONTROL_DISABLED: | ||
400 | sta->known_smps_mode = IEEE80211_SMPS_OFF; | ||
401 | break; | ||
402 | case WLAN_HT_SMPS_CONTROL_STATIC: | ||
403 | sta->known_smps_mode = IEEE80211_SMPS_STATIC; | ||
404 | break; | ||
405 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | ||
406 | sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
407 | break; | ||
408 | default: | ||
409 | WARN_ON(1); | ||
410 | } | ||
411 | } | ||
388 | 412 | ||
389 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); | 413 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); |
390 | 414 | ||
@@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1069 | 1093 | ||
1070 | ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); | 1094 | ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); |
1071 | 1095 | ||
1096 | /* This station just woke up and isn't aware of our SMPS state */ | ||
1097 | if (!ieee80211_smps_is_restrictive(sta->known_smps_mode, | ||
1098 | sdata->smps_mode) && | ||
1099 | sta->known_smps_mode != sdata->bss->req_smps && | ||
1100 | sta_info_tx_streams(sta) != 1) { | ||
1101 | ht_dbg(sdata, | ||
1102 | "%pM just woke up and MIMO capable - update SMPS\n", | ||
1103 | sta->sta.addr); | ||
1104 | ieee80211_send_smps_action(sdata, sdata->bss->req_smps, | ||
1105 | sta->sta.addr, | ||
1106 | sdata->vif.bss_conf.bssid); | ||
1107 | } | ||
1108 | |||
1072 | local->total_ps_buffered -= buffered; | 1109 | local->total_ps_buffered -= buffered; |
1073 | 1110 | ||
1074 | sta_info_recalc_tim(sta); | 1111 | sta_info_recalc_tim(sta); |
@@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta, | |||
1520 | 1557 | ||
1521 | return 0; | 1558 | return 0; |
1522 | } | 1559 | } |
1560 | |||
1561 | u8 sta_info_tx_streams(struct sta_info *sta) | ||
1562 | { | ||
1563 | struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; | ||
1564 | u8 rx_streams; | ||
1565 | |||
1566 | if (!sta->sta.ht_cap.ht_supported) | ||
1567 | return 1; | ||
1568 | |||
1569 | if (sta->sta.vht_cap.vht_supported) { | ||
1570 | int i; | ||
1571 | u16 tx_mcs_map = | ||
1572 | le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); | ||
1573 | |||
1574 | for (i = 7; i >= 0; i--) | ||
1575 | if ((tx_mcs_map & (0x3 << (i * 2))) != | ||
1576 | IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
1577 | return i + 1; | ||
1578 | } | ||
1579 | |||
1580 | if (ht_cap->mcs.rx_mask[3]) | ||
1581 | rx_streams = 4; | ||
1582 | else if (ht_cap->mcs.rx_mask[2]) | ||
1583 | rx_streams = 3; | ||
1584 | else if (ht_cap->mcs.rx_mask[1]) | ||
1585 | rx_streams = 2; | ||
1586 | else | ||
1587 | rx_streams = 1; | ||
1588 | |||
1589 | if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) | ||
1590 | return rx_streams; | ||
1591 | |||
1592 | return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) | ||
1593 | >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; | ||
1594 | } | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4208dbd5861f..3ef06a26b9cb 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -301,6 +301,8 @@ struct sta_ampdu_mlme { | |||
301 | * @chains: chains ever used for RX from this station | 301 | * @chains: chains ever used for RX from this station |
302 | * @chain_signal_last: last signal (per chain) | 302 | * @chain_signal_last: last signal (per chain) |
303 | * @chain_signal_avg: signal average (per chain) | 303 | * @chain_signal_avg: signal average (per chain) |
304 | * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for | ||
305 | * AP only. | ||
304 | */ | 306 | */ |
305 | struct sta_info { | 307 | struct sta_info { |
306 | /* General information, mostly static */ | 308 | /* General information, mostly static */ |
@@ -411,6 +413,8 @@ struct sta_info { | |||
411 | unsigned int lost_packets; | 413 | unsigned int lost_packets; |
412 | unsigned int beacon_loss_count; | 414 | unsigned int beacon_loss_count; |
413 | 415 | ||
416 | enum ieee80211_smps_mode known_smps_mode; | ||
417 | |||
414 | /* keep last! */ | 418 | /* keep last! */ |
415 | struct ieee80211_sta sta; | 419 | struct ieee80211_sta sta; |
416 | }; | 420 | }; |
@@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta, | |||
613 | struct rate_info *rinfo); | 617 | struct rate_info *rinfo); |
614 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | 618 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, |
615 | unsigned long exp_time); | 619 | unsigned long exp_time); |
620 | u8 sta_info_tx_streams(struct sta_info *sta); | ||
616 | 621 | ||
617 | void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); | 622 | void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); |
618 | void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); | 623 | void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 368837fe3b80..1ced74c73d2f 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -191,29 +191,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | |||
191 | if (ieee80211_is_action(mgmt->frame_control) && | 191 | if (ieee80211_is_action(mgmt->frame_control) && |
192 | mgmt->u.action.category == WLAN_CATEGORY_HT && | 192 | mgmt->u.action.category == WLAN_CATEGORY_HT && |
193 | 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)) { | 194 | ieee80211_sdata_running(sdata)) { |
196 | /* | 195 | enum ieee80211_smps_mode smps_mode; |
197 | * This update looks racy, but isn't -- if we come | 196 | |
198 | * here we've definitely got a station that we're | ||
199 | * talking to, and on a managed interface that can | ||
200 | * only be the AP. And the only other place updating | ||
201 | * this variable in managed mode is before association. | ||
202 | */ | ||
203 | switch (mgmt->u.action.u.ht_smps.smps_control) { | 197 | switch (mgmt->u.action.u.ht_smps.smps_control) { |
204 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | 198 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: |
205 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; | 199 | smps_mode = IEEE80211_SMPS_DYNAMIC; |
206 | break; | 200 | break; |
207 | case WLAN_HT_SMPS_CONTROL_STATIC: | 201 | case WLAN_HT_SMPS_CONTROL_STATIC: |
208 | sdata->smps_mode = IEEE80211_SMPS_STATIC; | 202 | smps_mode = IEEE80211_SMPS_STATIC; |
209 | break; | 203 | break; |
210 | case WLAN_HT_SMPS_CONTROL_DISABLED: | 204 | case WLAN_HT_SMPS_CONTROL_DISABLED: |
211 | default: /* shouldn't happen since we don't send that */ | 205 | default: /* shouldn't happen since we don't send that */ |
212 | sdata->smps_mode = IEEE80211_SMPS_OFF; | 206 | smps_mode = IEEE80211_SMPS_OFF; |
213 | break; | 207 | break; |
214 | } | 208 | } |
215 | 209 | ||
216 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); | 210 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
211 | /* | ||
212 | * This update looks racy, but isn't -- if we come | ||
213 | * here we've definitely got a station that we're | ||
214 | * talking to, and on a managed interface that can | ||
215 | * only be the AP. And the only other place updating | ||
216 | * this variable in managed mode is before association. | ||
217 | */ | ||
218 | sdata->smps_mode = smps_mode; | ||
219 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); | ||
220 | } else if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
221 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||
222 | sta->known_smps_mode = smps_mode; | ||
223 | } | ||
217 | } | 224 | } |
218 | } | 225 | } |
219 | 226 | ||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 550a6880625d..b763e4ccaf5e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -2353,3 +2353,28 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) | |||
2353 | 2353 | ||
2354 | return ret; | 2354 | return ret; |
2355 | } | 2355 | } |
2356 | |||
2357 | /* | ||
2358 | * Returns true if smps_mode_new is strictly more restrictive than | ||
2359 | * smps_mode_old. | ||
2360 | */ | ||
2361 | bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, | ||
2362 | enum ieee80211_smps_mode smps_mode_new) | ||
2363 | { | ||
2364 | if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || | ||
2365 | smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) | ||
2366 | return false; | ||
2367 | |||
2368 | switch (smps_mode_old) { | ||
2369 | case IEEE80211_SMPS_STATIC: | ||
2370 | return false; | ||
2371 | case IEEE80211_SMPS_DYNAMIC: | ||
2372 | return smps_mode_new == IEEE80211_SMPS_STATIC; | ||
2373 | case IEEE80211_SMPS_OFF: | ||
2374 | return smps_mode_new != IEEE80211_SMPS_OFF; | ||
2375 | default: | ||
2376 | WARN_ON(1); | ||
2377 | } | ||
2378 | |||
2379 | return false; | ||
2380 | } | ||