aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-04-16 07:17:24 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-04-22 16:57:15 -0400
commit965bedadc01d34027455d5d5b67063ef0209c955 (patch)
tree012bb04559011498e0bdb5db1ba6ffad0ec8b89a /net/mac80211/mlme.c
parentdc7d243d75b906cc964c12caa3b2eebe953a69be (diff)
mac80211: improve powersave implementation
When you have multiple virtual interfaces the current implementation requires setting them up properly from userspace, which is undesirable when we want to default to power save mode. Keep track of powersave requested from userspace per managed mode interface, and only enable powersave globally when exactly one managed mode interface is active and has powersave turned on. Second, only start the dynPS timer when PS is turned on, and properly turn it off when PS is turned off. Third, fix the scan_sdata abuse in the dynps code. Finally, also reorder the code and refactor the code that enables PS or the dynps timer instead of having it copied in two places. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c228
1 files changed, 142 insertions, 86 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 90267afa8e69..06d9a1d23252 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -446,6 +446,145 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
446 ieee80211_tx_skb(sdata, skb, 0); 446 ieee80211_tx_skb(sdata, skb, 0);
447} 447}
448 448
449void ieee80211_send_nullfunc(struct ieee80211_local *local,
450 struct ieee80211_sub_if_data *sdata,
451 int powersave)
452{
453 struct sk_buff *skb;
454 struct ieee80211_hdr *nullfunc;
455 __le16 fc;
456
457 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
458 return;
459
460 skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
461 if (!skb) {
462 printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
463 "frame\n", sdata->dev->name);
464 return;
465 }
466 skb_reserve(skb, local->hw.extra_tx_headroom);
467
468 nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
469 memset(nullfunc, 0, 24);
470 fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
471 IEEE80211_FCTL_TODS);
472 if (powersave)
473 fc |= cpu_to_le16(IEEE80211_FCTL_PM);
474 nullfunc->frame_control = fc;
475 memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
476 memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
477 memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
478
479 ieee80211_tx_skb(sdata, skb, 0);
480}
481
482/* powersave */
483static void ieee80211_enable_ps(struct ieee80211_local *local,
484 struct ieee80211_sub_if_data *sdata)
485{
486 struct ieee80211_conf *conf = &local->hw.conf;
487
488 if (conf->dynamic_ps_timeout > 0 &&
489 !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
490 mod_timer(&local->dynamic_ps_timer, jiffies +
491 msecs_to_jiffies(conf->dynamic_ps_timeout));
492 } else {
493 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
494 ieee80211_send_nullfunc(local, sdata, 1);
495 conf->flags |= IEEE80211_CONF_PS;
496 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
497 }
498}
499
500static void ieee80211_change_ps(struct ieee80211_local *local)
501{
502 struct ieee80211_conf *conf = &local->hw.conf;
503
504 if (local->ps_sdata) {
505 if (!(local->ps_sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
506 return;
507
508 ieee80211_enable_ps(local, local->ps_sdata);
509 } else if (conf->flags & IEEE80211_CONF_PS) {
510 conf->flags &= ~IEEE80211_CONF_PS;
511 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
512 del_timer_sync(&local->dynamic_ps_timer);
513 cancel_work_sync(&local->dynamic_ps_enable_work);
514 }
515}
516
517/* need to hold RTNL or interface lock */
518void ieee80211_recalc_ps(struct ieee80211_local *local)
519{
520 struct ieee80211_sub_if_data *sdata, *found = NULL;
521 int count = 0;
522
523 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
524 local->ps_sdata = NULL;
525 return;
526 }
527
528 list_for_each_entry(sdata, &local->interfaces, list) {
529 if (!netif_running(sdata->dev))
530 continue;
531 if (sdata->vif.type != NL80211_IFTYPE_STATION)
532 continue;
533 found = sdata;
534 count++;
535 }
536
537 if (count == 1 && found->u.mgd.powersave)
538 local->ps_sdata = found;
539 else
540 local->ps_sdata = NULL;
541
542 ieee80211_change_ps(local);
543}
544
545void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
546{
547 struct ieee80211_local *local =
548 container_of(work, struct ieee80211_local,
549 dynamic_ps_disable_work);
550
551 if (local->hw.conf.flags & IEEE80211_CONF_PS) {
552 local->hw.conf.flags &= ~IEEE80211_CONF_PS;
553 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
554 }
555
556 ieee80211_wake_queues_by_reason(&local->hw,
557 IEEE80211_QUEUE_STOP_REASON_PS);
558}
559
560void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
561{
562 struct ieee80211_local *local =
563 container_of(work, struct ieee80211_local,
564 dynamic_ps_enable_work);
565 struct ieee80211_sub_if_data *sdata = local->ps_sdata;
566
567 /* can only happen when PS was just disabled anyway */
568 if (!sdata)
569 return;
570
571 if (local->hw.conf.flags & IEEE80211_CONF_PS)
572 return;
573
574 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
575 ieee80211_send_nullfunc(local, sdata, 1);
576
577 local->hw.conf.flags |= IEEE80211_CONF_PS;
578 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
579}
580
581void ieee80211_dynamic_ps_timer(unsigned long data)
582{
583 struct ieee80211_local *local = (void *) data;
584
585 queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
586}
587
449/* MLME */ 588/* MLME */
450static void ieee80211_sta_wmm_params(struct ieee80211_local *local, 589static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
451 struct ieee80211_if_managed *ifmgd, 590 struct ieee80211_if_managed *ifmgd,
@@ -721,19 +860,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
721 bss_info_changed |= BSS_CHANGED_BASIC_RATES; 860 bss_info_changed |= BSS_CHANGED_BASIC_RATES;
722 ieee80211_bss_info_change_notify(sdata, bss_info_changed); 861 ieee80211_bss_info_change_notify(sdata, bss_info_changed);
723 862
724 if (local->powersave) { 863 /* will be same as sdata */
725 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) && 864 if (local->ps_sdata)
726 local->hw.conf.dynamic_ps_timeout > 0) { 865 ieee80211_enable_ps(local, sdata);
727 mod_timer(&local->dynamic_ps_timer, jiffies +
728 msecs_to_jiffies(
729 local->hw.conf.dynamic_ps_timeout));
730 } else {
731 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
732 ieee80211_send_nullfunc(local, sdata, 1);
733 conf->flags |= IEEE80211_CONF_PS;
734 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
735 }
736 }
737 866
738 netif_tx_start_all_queues(sdata->dev); 867 netif_tx_start_all_queues(sdata->dev);
739 netif_carrier_on(sdata->dev); 868 netif_carrier_on(sdata->dev);
@@ -2195,76 +2324,3 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
2195 ieee80211_restart_sta_timer(sdata); 2324 ieee80211_restart_sta_timer(sdata);
2196 rcu_read_unlock(); 2325 rcu_read_unlock();
2197} 2326}
2198
2199void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
2200{
2201 struct ieee80211_local *local =
2202 container_of(work, struct ieee80211_local,
2203 dynamic_ps_disable_work);
2204
2205 if (local->hw.conf.flags & IEEE80211_CONF_PS) {
2206 local->hw.conf.flags &= ~IEEE80211_CONF_PS;
2207 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
2208 }
2209
2210 ieee80211_wake_queues_by_reason(&local->hw,
2211 IEEE80211_QUEUE_STOP_REASON_PS);
2212}
2213
2214void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
2215{
2216 struct ieee80211_local *local =
2217 container_of(work, struct ieee80211_local,
2218 dynamic_ps_enable_work);
2219 /* XXX: using scan_sdata is completely broken! */
2220 struct ieee80211_sub_if_data *sdata = local->scan_sdata;
2221
2222 if (local->hw.conf.flags & IEEE80211_CONF_PS)
2223 return;
2224
2225 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK && sdata)
2226 ieee80211_send_nullfunc(local, sdata, 1);
2227
2228 local->hw.conf.flags |= IEEE80211_CONF_PS;
2229 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
2230}
2231
2232void ieee80211_dynamic_ps_timer(unsigned long data)
2233{
2234 struct ieee80211_local *local = (void *) data;
2235
2236 queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
2237}
2238
2239void ieee80211_send_nullfunc(struct ieee80211_local *local,
2240 struct ieee80211_sub_if_data *sdata,
2241 int powersave)
2242{
2243 struct sk_buff *skb;
2244 struct ieee80211_hdr *nullfunc;
2245 __le16 fc;
2246
2247 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
2248 return;
2249
2250 skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
2251 if (!skb) {
2252 printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
2253 "frame\n", sdata->dev->name);
2254 return;
2255 }
2256 skb_reserve(skb, local->hw.extra_tx_headroom);
2257
2258 nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
2259 memset(nullfunc, 0, 24);
2260 fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
2261 IEEE80211_FCTL_TODS);
2262 if (powersave)
2263 fc |= cpu_to_le16(IEEE80211_FCTL_PM);
2264 nullfunc->frame_control = fc;
2265 memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
2266 memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
2267 memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
2268
2269 ieee80211_tx_skb(sdata, skb, 0);
2270}