aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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}