Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: implement SMPS for AP

When the driver requests to move to STATIC or DYNAMIC SMPS,
we send an action frame to each associated station and
reconfigure the channel context / driver.
Of course, non-MIMO stations are ignored.

The beacon isn't updated. The association response will
include the original capabilities. Stations that associate
while in non-OFF SMPS mode will get an action frame right
after association to inform them about our current state.
Note that we wait until the end of the EAPOL. Sending an
action frame before the EAPOL is finished can be an issue
for a few clients. Clients aren't likely to send EAPOL
frames in MIMO anyway.

When the SMPS configuration gets more permissive (e.g.
STATIC -> OFF), we don't wake up stations that are asleep
We remember that they don't know about the change and send
the action frame when they wake up.

When the SMPS configuration gets more restrictive (e.g.
OFF -> STATIC), we set the TIM bit for every sleeping STA.
uAPSD stations might send MIMO until they poll the action
frame, but this is for a short period of time.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
[fix vht streams loop, initialisation]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Emmanuel Grumbach and committed by
Johannes Berg
687da132 7ec7c4a9

+289 -38
+105 -3
net/mac80211/cfg.c
··· 1059 1059 /* abort any running channel switch */ 1060 1060 sdata->vif.csa_active = false; 1061 1061 cancel_work_sync(&sdata->csa_finalize_work); 1062 + cancel_work_sync(&sdata->u.ap.request_smps_work); 1062 1063 1063 1064 /* turn off carrier for this interface and dependent VLANs */ 1064 1065 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) ··· 1553 1552 rate_control_rate_init(sta); 1554 1553 1555 1554 mutex_unlock(&local->sta_mtx); 1555 + 1556 + if ((sdata->vif.type == NL80211_IFTYPE_AP || 1557 + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && 1558 + sta->known_smps_mode != sta->sdata->bss->req_smps && 1559 + test_sta_flag(sta, WLAN_STA_AUTHORIZED) && 1560 + sta_info_tx_streams(sta) != 1) { 1561 + ht_dbg(sta->sdata, 1562 + "%pM just authorized and MIMO capable - update SMPS\n", 1563 + sta->sta.addr); 1564 + ieee80211_send_smps_action(sta->sdata, 1565 + sta->sdata->bss->req_smps, 1566 + sta->sta.addr, 1567 + sta->sdata->vif.bss_conf.bssid); 1568 + } 1556 1569 1557 1570 if (sdata->vif.type == NL80211_IFTYPE_STATION && 1558 1571 params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { ··· 2352 2337 } 2353 2338 #endif 2354 2339 2355 - int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, 2356 - enum ieee80211_smps_mode smps_mode) 2340 + int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, 2341 + enum ieee80211_smps_mode smps_mode) 2342 + { 2343 + struct sta_info *sta; 2344 + enum ieee80211_smps_mode old_req; 2345 + int i; 2346 + 2347 + if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) 2348 + return -EINVAL; 2349 + 2350 + if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) 2351 + return 0; 2352 + 2353 + old_req = sdata->u.ap.req_smps; 2354 + sdata->u.ap.req_smps = smps_mode; 2355 + 2356 + /* AUTOMATIC doesn't mean much for AP - don't allow it */ 2357 + if (old_req == smps_mode || 2358 + smps_mode == IEEE80211_SMPS_AUTOMATIC) 2359 + return 0; 2360 + 2361 + /* If no associated stations, there's no need to do anything */ 2362 + if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { 2363 + sdata->smps_mode = smps_mode; 2364 + ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); 2365 + return 0; 2366 + } 2367 + 2368 + ht_dbg(sdata, 2369 + "SMSP %d requested in AP mode, sending Action frame to %d stations\n", 2370 + smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); 2371 + 2372 + mutex_lock(&sdata->local->sta_mtx); 2373 + for (i = 0; i < STA_HASH_SIZE; i++) { 2374 + for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], 2375 + lockdep_is_held(&sdata->local->sta_mtx)); 2376 + sta; 2377 + sta = rcu_dereference_protected(sta->hnext, 2378 + lockdep_is_held(&sdata->local->sta_mtx))) { 2379 + /* 2380 + * Only stations associated to our AP and 2381 + * associated VLANs 2382 + */ 2383 + if (sta->sdata->bss != &sdata->u.ap) 2384 + continue; 2385 + 2386 + /* This station doesn't support MIMO - skip it */ 2387 + if (sta_info_tx_streams(sta) == 1) 2388 + continue; 2389 + 2390 + /* 2391 + * Don't wake up a STA just to send the action frame 2392 + * unless we are getting more restrictive. 2393 + */ 2394 + if (test_sta_flag(sta, WLAN_STA_PS_STA) && 2395 + !ieee80211_smps_is_restrictive(sta->known_smps_mode, 2396 + smps_mode)) { 2397 + ht_dbg(sdata, 2398 + "Won't send SMPS to sleeping STA %pM\n", 2399 + sta->sta.addr); 2400 + continue; 2401 + } 2402 + 2403 + /* 2404 + * If the STA is not authorized, wait until it gets 2405 + * authorized and the action frame will be sent then. 2406 + */ 2407 + if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) 2408 + continue; 2409 + 2410 + ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); 2411 + ieee80211_send_smps_action(sdata, smps_mode, 2412 + sta->sta.addr, 2413 + sdata->vif.bss_conf.bssid); 2414 + } 2415 + } 2416 + mutex_unlock(&sdata->local->sta_mtx); 2417 + 2418 + sdata->smps_mode = smps_mode; 2419 + ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); 2420 + 2421 + return 0; 2422 + } 2423 + 2424 + int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, 2425 + enum ieee80211_smps_mode smps_mode) 2357 2426 { 2358 2427 const u8 *ap; 2359 2428 enum ieee80211_smps_mode old_req; 2360 2429 int err; 2361 2430 2362 2431 lockdep_assert_held(&sdata->wdev.mtx); 2432 + 2433 + if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) 2434 + return -EINVAL; 2363 2435 2364 2436 old_req = sdata->u.mgd.req_smps; 2365 2437 sdata->u.mgd.req_smps = smps_mode; ··· 2504 2402 2505 2403 /* no change, but if automatic follow powersave */ 2506 2404 sdata_lock(sdata); 2507 - __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); 2405 + __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps); 2508 2406 sdata_unlock(sdata); 2509 2407 2510 2408 if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+16 -9
net/mac80211/debugfs_netdev.c
··· 224 224 smps_mode == IEEE80211_SMPS_AUTOMATIC)) 225 225 return -EINVAL; 226 226 227 - /* supported only on managed interfaces for now */ 228 - if (sdata->vif.type != NL80211_IFTYPE_STATION) 227 + if (sdata->vif.type != NL80211_IFTYPE_STATION && 228 + sdata->vif.type != NL80211_IFTYPE_AP) 229 229 return -EOPNOTSUPP; 230 230 231 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 236 sdata_unlock(sdata); 234 237 235 238 return err; ··· 248 245 static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, 249 246 char *buf, int buflen) 250 247 { 251 - if (sdata->vif.type != NL80211_IFTYPE_STATION) 252 - return -EOPNOTSUPP; 253 - 254 - return snprintf(buf, buflen, "request: %s\nused: %s\n", 255 - smps_modes[sdata->u.mgd.req_smps], 256 - smps_modes[sdata->smps_mode]); 248 + if (sdata->vif.type == NL80211_IFTYPE_STATION) 249 + return snprintf(buf, buflen, "request: %s\nused: %s\n", 250 + smps_modes[sdata->u.mgd.req_smps], 251 + smps_modes[sdata->smps_mode]); 252 + if (sdata->vif.type == NL80211_IFTYPE_AP) 253 + return snprintf(buf, buflen, "request: %s\nused: %s\n", 254 + smps_modes[sdata->u.ap.req_smps], 255 + smps_modes[sdata->smps_mode]); 256 + return -EINVAL; 257 257 } 258 258 259 259 static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, ··· 569 563 static void add_ap_files(struct ieee80211_sub_if_data *sdata) 570 564 { 571 565 DEBUGFS_ADD(num_mcast_sta); 566 + DEBUGFS_ADD_MODE(smps, 0600); 572 567 DEBUGFS_ADD(num_sta_ps); 573 568 DEBUGFS_ADD(dtim_count); 574 569 DEBUGFS_ADD(num_buffered_multicast);
+31 -10
net/mac80211/ht.c
··· 448 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 453 struct ieee80211_sub_if_data *sdata = 454 454 container_of(work, struct ieee80211_sub_if_data, 455 455 u.mgd.request_smps_work); 456 456 457 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 470 sdata_unlock(sdata); 460 471 } 461 472 ··· 475 464 { 476 465 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 477 466 478 - if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 467 + if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && 468 + vif->type != NL80211_IFTYPE_AP)) 479 469 return; 480 470 481 471 if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) 482 472 smps_mode = IEEE80211_SMPS_AUTOMATIC; 483 473 484 - if (sdata->u.mgd.driver_smps_mode == smps_mode) 485 - return; 486 - 487 - sdata->u.mgd.driver_smps_mode = smps_mode; 488 - 489 - ieee80211_queue_work(&sdata->local->hw, 490 - &sdata->u.mgd.request_smps_work); 474 + if (vif->type == NL80211_IFTYPE_STATION) { 475 + if (sdata->u.mgd.driver_smps_mode == smps_mode) 476 + return; 477 + sdata->u.mgd.driver_smps_mode = smps_mode; 478 + ieee80211_queue_work(&sdata->local->hw, 479 + &sdata->u.mgd.request_smps_work); 480 + } else { 481 + /* AUTOMATIC is meaningless in AP mode */ 482 + if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) 483 + return; 484 + if (sdata->u.ap.driver_smps_mode == smps_mode) 485 + return; 486 + sdata->u.ap.driver_smps_mode = smps_mode; 487 + ieee80211_queue_work(&sdata->local->hw, 488 + &sdata->u.ap.request_smps_work); 489 + } 491 490 } 492 491 /* this might change ... don't want non-open drivers using it */ 493 492 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
+12 -3
net/mac80211/ieee80211_i.h
··· 262 262 263 263 struct ps_data ps; 264 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 271 struct ieee80211_if_wds { ··· 1439 1435 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, 1440 1436 enum ieee80211_smps_mode smps, const u8 *da, 1441 1437 const u8 *bssid); 1442 - void ieee80211_request_smps_work(struct work_struct *work); 1438 + void ieee80211_request_smps_ap_work(struct work_struct *work); 1439 + void ieee80211_request_smps_mgd_work(struct work_struct *work); 1440 + bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, 1441 + enum ieee80211_smps_mode smps_mode_new); 1443 1442 1444 1443 void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, 1445 1444 u16 initiator, u16 reason, bool stop); ··· 1660 1653 u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 1661 1654 struct ieee802_11_elems *elems, 1662 1655 enum ieee80211_band band, u32 *basic_rates); 1663 - int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, 1664 - enum ieee80211_smps_mode smps_mode); 1656 + int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, 1657 + enum ieee80211_smps_mode smps_mode); 1658 + int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, 1659 + enum ieee80211_smps_mode smps_mode); 1665 1660 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); 1666 1661 1667 1662 size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+3
net/mac80211/iface.c
··· 1293 1293 case NL80211_IFTYPE_AP: 1294 1294 skb_queue_head_init(&sdata->u.ap.ps.bc_buf); 1295 1295 INIT_LIST_HEAD(&sdata->u.ap.vlans); 1296 + INIT_WORK(&sdata->u.ap.request_smps_work, 1297 + ieee80211_request_smps_ap_work); 1296 1298 sdata->vif.bss_conf.bssid = sdata->vif.addr; 1299 + sdata->u.ap.req_smps = IEEE80211_SMPS_OFF; 1297 1300 break; 1298 1301 case NL80211_IFTYPE_P2P_CLIENT: 1299 1302 type = NL80211_IFTYPE_STATION;
+1 -1
net/mac80211/mlme.c
··· 3499 3499 ieee80211_beacon_connection_loss_work); 3500 3500 INIT_WORK(&ifmgd->csa_connection_drop_work, 3501 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 3503 setup_timer(&ifmgd->timer, ieee80211_sta_timer, 3504 3504 (unsigned long) sdata); 3505 3505 setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
+72
net/mac80211/sta_info.c
··· 385 385 sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); 386 386 387 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 413 sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); 390 414 ··· 1093 1069 1094 1070 ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); 1095 1071 1072 + /* This station just woke up and isn't aware of our SMPS state */ 1073 + if (!ieee80211_smps_is_restrictive(sta->known_smps_mode, 1074 + sdata->smps_mode) && 1075 + sta->known_smps_mode != sdata->bss->req_smps && 1076 + sta_info_tx_streams(sta) != 1) { 1077 + ht_dbg(sdata, 1078 + "%pM just woke up and MIMO capable - update SMPS\n", 1079 + sta->sta.addr); 1080 + ieee80211_send_smps_action(sdata, sdata->bss->req_smps, 1081 + sta->sta.addr, 1082 + sdata->vif.bss_conf.bssid); 1083 + } 1084 + 1096 1085 local->total_ps_buffered -= buffered; 1097 1086 1098 1087 sta_info_recalc_tim(sta); ··· 1556 1519 sta->sta_state = new_state; 1557 1520 1558 1521 return 0; 1522 + } 1523 + 1524 + u8 sta_info_tx_streams(struct sta_info *sta) 1525 + { 1526 + struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; 1527 + u8 rx_streams; 1528 + 1529 + if (!sta->sta.ht_cap.ht_supported) 1530 + return 1; 1531 + 1532 + if (sta->sta.vht_cap.vht_supported) { 1533 + int i; 1534 + u16 tx_mcs_map = 1535 + le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); 1536 + 1537 + for (i = 7; i >= 0; i--) 1538 + if ((tx_mcs_map & (0x3 << (i * 2))) != 1539 + IEEE80211_VHT_MCS_NOT_SUPPORTED) 1540 + return i + 1; 1541 + } 1542 + 1543 + if (ht_cap->mcs.rx_mask[3]) 1544 + rx_streams = 4; 1545 + else if (ht_cap->mcs.rx_mask[2]) 1546 + rx_streams = 3; 1547 + else if (ht_cap->mcs.rx_mask[1]) 1548 + rx_streams = 2; 1549 + else 1550 + rx_streams = 1; 1551 + 1552 + if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) 1553 + return rx_streams; 1554 + 1555 + return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) 1556 + >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; 1559 1557 }
+5
net/mac80211/sta_info.h
··· 301 301 * @chains: chains ever used for RX from this station 302 302 * @chain_signal_last: last signal (per chain) 303 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 307 struct sta_info { 306 308 /* General information, mostly static */ ··· 412 410 413 411 unsigned int lost_packets; 414 412 unsigned int beacon_loss_count; 413 + 414 + enum ieee80211_smps_mode known_smps_mode; 415 415 416 416 /* keep last! */ 417 417 struct ieee80211_sta sta; ··· 617 613 struct rate_info *rinfo); 618 614 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, 619 615 unsigned long exp_time); 616 + u8 sta_info_tx_streams(struct sta_info *sta); 620 617 621 618 void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); 622 619 void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
+19 -12
net/mac80211/status.c
··· 191 191 if (ieee80211_is_action(mgmt->frame_control) && 192 192 mgmt->u.action.category == WLAN_CATEGORY_HT && 193 193 mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && 194 - sdata->vif.type == NL80211_IFTYPE_STATION && 195 194 ieee80211_sdata_running(sdata)) { 196 - /* 197 - * This update looks racy, but isn't -- if we come 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 - */ 195 + enum ieee80211_smps_mode smps_mode; 196 + 203 197 switch (mgmt->u.action.u.ht_smps.smps_control) { 204 198 case WLAN_HT_SMPS_CONTROL_DYNAMIC: 205 - sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; 199 + smps_mode = IEEE80211_SMPS_DYNAMIC; 206 200 break; 207 201 case WLAN_HT_SMPS_CONTROL_STATIC: 208 - sdata->smps_mode = IEEE80211_SMPS_STATIC; 202 + smps_mode = IEEE80211_SMPS_STATIC; 209 203 break; 210 204 case WLAN_HT_SMPS_CONTROL_DISABLED: 211 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 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
+25
net/mac80211/util.c
··· 2353 2353 2354 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 + }