aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--net/mac80211/ieee80211_i.h9
-rw-r--r--net/mac80211/iface.c4
-rw-r--r--net/mac80211/mlme.c228
-rw-r--r--net/mac80211/scan.c2
-rw-r--r--net/mac80211/wext.c43
5 files changed, 165 insertions, 121 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 13d6f890ced4..ff40dd7b523a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -295,6 +295,8 @@ struct ieee80211_if_managed {
295 int auth_tries; /* retries for auth req */ 295 int auth_tries; /* retries for auth req */
296 int assoc_tries; /* retries for assoc req */ 296 int assoc_tries; /* retries for assoc req */
297 297
298 bool powersave; /* powersave requested for this iface */
299
298 unsigned long request; 300 unsigned long request;
299 301
300 unsigned long last_probe; 302 unsigned long last_probe;
@@ -739,8 +741,12 @@ struct ieee80211_local {
739 int wifi_wme_noack_test; 741 int wifi_wme_noack_test;
740 unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ 742 unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
741 743
742 bool powersave;
743 bool pspolling; 744 bool pspolling;
745 /*
746 * PS can only be enabled when we have exactly one managed
747 * interface (and monitors) in PS, this then points there.
748 */
749 struct ieee80211_sub_if_data *ps_sdata;
744 struct work_struct dynamic_ps_enable_work; 750 struct work_struct dynamic_ps_enable_work;
745 struct work_struct dynamic_ps_disable_work; 751 struct work_struct dynamic_ps_disable_work;
746 struct timer_list dynamic_ps_timer; 752 struct timer_list dynamic_ps_timer;
@@ -932,6 +938,7 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
932int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); 938int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
933void ieee80211_send_pspoll(struct ieee80211_local *local, 939void ieee80211_send_pspoll(struct ieee80211_local *local,
934 struct ieee80211_sub_if_data *sdata); 940 struct ieee80211_sub_if_data *sdata);
941void ieee80211_recalc_ps(struct ieee80211_local *local);
935 942
936/* IBSS code */ 943/* IBSS code */
937int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata); 944int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 91e8e1bacaaa..6240f76e2a43 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -317,6 +317,8 @@ static int ieee80211_open(struct net_device *dev)
317 ieee80211_set_wmm_default(sdata); 317 ieee80211_set_wmm_default(sdata);
318 } 318 }
319 319
320 ieee80211_recalc_ps(local);
321
320 /* 322 /*
321 * ieee80211_sta_work is disabled while network interface 323 * ieee80211_sta_work is disabled while network interface
322 * is down. Therefore, some configuration changes may not 324 * is down. Therefore, some configuration changes may not
@@ -572,6 +574,8 @@ static int ieee80211_stop(struct net_device *dev)
572 hw_reconf_flags = 0; 574 hw_reconf_flags = 0;
573 } 575 }
574 576
577 ieee80211_recalc_ps(local);
578
575 /* do after stop to avoid reconfiguring when we stop anyway */ 579 /* do after stop to avoid reconfiguring when we stop anyway */
576 if (hw_reconf_flags) 580 if (hw_reconf_flags)
577 ieee80211_hw_config(local, hw_reconf_flags); 581 ieee80211_hw_config(local, hw_reconf_flags);
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}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4ec1bfc7f6a9..20df861c6c4c 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -253,7 +253,7 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
253{ 253{
254 struct ieee80211_local *local = sdata->local; 254 struct ieee80211_local *local = sdata->local;
255 255
256 if (!local->powersave) 256 if (!local->ps_sdata)
257 ieee80211_send_nullfunc(local, sdata, 0); 257 ieee80211_send_nullfunc(local, sdata, 0);
258 else { 258 else {
259 /* 259 /*
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index a52fb3a4a455..81f63e57027f 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -747,7 +747,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
747 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 747 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
748 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 748 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
749 struct ieee80211_conf *conf = &local->hw.conf; 749 struct ieee80211_conf *conf = &local->hw.conf;
750 int ret = 0, timeout = 0; 750 int timeout = 0;
751 bool ps; 751 bool ps;
752 752
753 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) 753 if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -779,42 +779,19 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
779 timeout = wrq->value / 1000; 779 timeout = wrq->value / 1000;
780 780
781 set: 781 set:
782 if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) 782 if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
783 return ret; 783 return 0;
784 784
785 local->powersave = ps; 785 sdata->u.mgd.powersave = ps;
786 conf->dynamic_ps_timeout = timeout; 786 conf->dynamic_ps_timeout = timeout;
787 787
788 if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) 788 if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
789 ret = ieee80211_hw_config(local, 789 ieee80211_hw_config(local,
790 IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); 790 IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
791
792 if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
793 return ret;
794 791
795 if (conf->dynamic_ps_timeout > 0 && 792 ieee80211_recalc_ps(local);
796 !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
797 mod_timer(&local->dynamic_ps_timer, jiffies +
798 msecs_to_jiffies(conf->dynamic_ps_timeout));
799 } else {
800 if (local->powersave) {
801 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
802 ieee80211_send_nullfunc(local, sdata, 1);
803 conf->flags |= IEEE80211_CONF_PS;
804 ret = ieee80211_hw_config(local,
805 IEEE80211_CONF_CHANGE_PS);
806 } else {
807 conf->flags &= ~IEEE80211_CONF_PS;
808 ret = ieee80211_hw_config(local,
809 IEEE80211_CONF_CHANGE_PS);
810 if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
811 ieee80211_send_nullfunc(local, sdata, 0);
812 del_timer_sync(&local->dynamic_ps_timer);
813 cancel_work_sync(&local->dynamic_ps_enable_work);
814 }
815 }
816 793
817 return ret; 794 return 0;
818} 795}
819 796
820static int ieee80211_ioctl_giwpower(struct net_device *dev, 797static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -822,9 +799,9 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
822 union iwreq_data *wrqu, 799 union iwreq_data *wrqu,
823 char *extra) 800 char *extra)
824{ 801{
825 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 802 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
826 803
827 wrqu->power.disabled = !local->powersave; 804 wrqu->power.disabled = !sdata->u.mgd.powersave;
828 805
829 return 0; 806 return 0;
830} 807}