Skip to content

Commit 687da13

Browse files
egrumbachjmberg-intel
authored andcommitted
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 <[email protected]> [fix vht streams loop, initialisation] Signed-off-by: Johannes Berg <[email protected]>
1 parent 7ec7c4a commit 687da13

File tree

10 files changed

+289
-38
lines changed

10 files changed

+289
-38
lines changed

net/mac80211/cfg.c

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
10591059
/* abort any running channel switch */
10601060
sdata->vif.csa_active = false;
10611061
cancel_work_sync(&sdata->csa_finalize_work);
1062+
cancel_work_sync(&sdata->u.ap.request_smps_work);
10621063

10631064
/* turn off carrier for this interface and dependent VLANs */
10641065
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
15531554

15541555
mutex_unlock(&local->sta_mtx);
15551556

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+
15561571
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
15571572
params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
15581573
ieee80211_recalc_ps(local, -1);
@@ -2337,15 +2352,102 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
23372352
}
23382353
#endif
23392354

2340-
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
2341-
enum ieee80211_smps_mode smps_mode)
2355+
int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
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)
23422441
{
23432442
const u8 *ap;
23442443
enum ieee80211_smps_mode old_req;
23452444
int err;
23462445

23472446
lockdep_assert_held(&sdata->wdev.mtx);
23482447

2448+
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
2449+
return -EINVAL;
2450+
23492451
old_req = sdata->u.mgd.req_smps;
23502452
sdata->u.mgd.req_smps = smps_mode;
23512453

@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
24022504

24032505
/* no change, but if automatic follow powersave */
24042506
sdata_lock(sdata);
2405-
__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
2507+
__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
24062508
sdata_unlock(sdata);
24072509

24082510
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)

net/mac80211/debugfs_netdev.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
224224
smps_mode == IEEE80211_SMPS_AUTOMATIC))
225225
return -EINVAL;
226226

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)
229229
return -EOPNOTSUPP;
230230

231231
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);
233236
sdata_unlock(sdata);
234237

235238
return err;
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
245248
static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
246249
char *buf, int buflen)
247250
{
248-
if (sdata->vif.type != NL80211_IFTYPE_STATION)
249-
return -EOPNOTSUPP;
250-
251-
return snprintf(buf, buflen, "request: %s\nused: %s\n",
252-
smps_modes[sdata->u.mgd.req_smps],
253-
smps_modes[sdata->smps_mode]);
251+
if (sdata->vif.type == NL80211_IFTYPE_STATION)
252+
return snprintf(buf, buflen, "request: %s\nused: %s\n",
253+
smps_modes[sdata->u.mgd.req_smps],
254+
smps_modes[sdata->smps_mode]);
255+
if (sdata->vif.type == NL80211_IFTYPE_AP)
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;
254260
}
255261

256262
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)
563569
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
564570
{
565571
DEBUGFS_ADD(num_mcast_sta);
572+
DEBUGFS_ADD_MODE(smps, 0600);
566573
DEBUGFS_ADD(num_sta_ps);
567574
DEBUGFS_ADD(dtim_count);
568575
DEBUGFS_ADD(num_buffered_multicast);

net/mac80211/ht.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
448448
return 0;
449449
}
450450

451-
void ieee80211_request_smps_work(struct work_struct *work)
451+
void ieee80211_request_smps_mgd_work(struct work_struct *work)
452452
{
453453
struct ieee80211_sub_if_data *sdata =
454454
container_of(work, struct ieee80211_sub_if_data,
455455
u.mgd.request_smps_work);
456456

457457
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);
459470
sdata_unlock(sdata);
460471
}
461472

@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
464475
{
465476
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
466477

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))
468480
return;
469481

470482
if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
471483
smps_mode = IEEE80211_SMPS_AUTOMATIC;
472484

473-
if (sdata->u.mgd.driver_smps_mode == smps_mode)
474-
return;
475-
476-
sdata->u.mgd.driver_smps_mode = smps_mode;
477-
478-
ieee80211_queue_work(&sdata->local->hw,
479-
&sdata->u.mgd.request_smps_work);
485+
if (vif->type == NL80211_IFTYPE_STATION) {
486+
if (sdata->u.mgd.driver_smps_mode == smps_mode)
487+
return;
488+
sdata->u.mgd.driver_smps_mode = smps_mode;
489+
ieee80211_queue_work(&sdata->local->hw,
490+
&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+
}
480501
}
481502
/* this might change ... don't want non-open drivers using it */
482503
EXPORT_SYMBOL_GPL(ieee80211_request_smps);

net/mac80211/ieee80211_i.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ struct ieee80211_if_ap {
262262

263263
struct ps_data ps;
264264
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;
265269
};
266270

267271
struct ieee80211_if_wds {
@@ -1435,7 +1439,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
14351439
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
14361440
enum ieee80211_smps_mode smps, const u8 *da,
14371441
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);
14391446

14401447
void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
14411448
u16 initiator, u16 reason, bool stop);
@@ -1653,8 +1660,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
16531660
u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
16541661
struct ieee802_11_elems *elems,
16551662
enum ieee80211_band band, u32 *basic_rates);
1656-
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
1657-
enum ieee80211_smps_mode smps_mode);
1663+
int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
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);
16581667
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
16591668

16601669
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,

net/mac80211/iface.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
12931293
case NL80211_IFTYPE_AP:
12941294
skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
12951295
INIT_LIST_HEAD(&sdata->u.ap.vlans);
1296+
INIT_WORK(&sdata->u.ap.request_smps_work,
1297+
ieee80211_request_smps_ap_work);
12961298
sdata->vif.bss_conf.bssid = sdata->vif.addr;
1299+
sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
12971300
break;
12981301
case NL80211_IFTYPE_P2P_CLIENT:
12991302
type = NL80211_IFTYPE_STATION;

net/mac80211/mlme.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3499,7 +3499,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
34993499
ieee80211_beacon_connection_loss_work);
35003500
INIT_WORK(&ifmgd->csa_connection_drop_work,
35013501
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);
35033503
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
35043504
(unsigned long) sdata);
35053505
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,

0 commit comments

Comments
 (0)