diff options
-rw-r--r-- | net/mac80211/ieee80211_i.h | 9 | ||||
-rw-r--r-- | net/mac80211/iface.c | 4 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 228 | ||||
-rw-r--r-- | net/mac80211/scan.c | 2 | ||||
-rw-r--r-- | net/mac80211/wext.c | 43 |
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 | |||
932 | int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); | 938 | int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); |
933 | void ieee80211_send_pspoll(struct ieee80211_local *local, | 939 | void ieee80211_send_pspoll(struct ieee80211_local *local, |
934 | struct ieee80211_sub_if_data *sdata); | 940 | struct ieee80211_sub_if_data *sdata); |
941 | void ieee80211_recalc_ps(struct ieee80211_local *local); | ||
935 | 942 | ||
936 | /* IBSS code */ | 943 | /* IBSS code */ |
937 | int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata); | 944 | int 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 | ||
449 | void 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 */ | ||
483 | static 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 | |||
500 | static 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 */ | ||
518 | void 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 | |||
545 | void 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 | |||
560 | void 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 | |||
581 | void 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 */ |
450 | static void ieee80211_sta_wmm_params(struct ieee80211_local *local, | 589 | static 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 | |||
2199 | void 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 | |||
2214 | void 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 | |||
2232 | void 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 | |||
2239 | void 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 | ||
820 | static int ieee80211_ioctl_giwpower(struct net_device *dev, | 797 | static 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 | } |