diff options
author | Kalle Valo <kalle.valo@nokia.com> | 2008-12-18 16:35:27 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-12-19 15:24:00 -0500 |
commit | 520eb82076993b7f55ef9b80771d264272e5127b (patch) | |
tree | f98b74dbe404d4c3a55b5f649c25ca24958e62ba /net/mac80211/mlme.c | |
parent | ce7c9111a97492d04c504f40736a669c235d664a (diff) |
mac80211: implement dynamic power save
This patch implements dynamic power save for mac80211. Basically it
means enabling power save mode after an idle period. Implementing it
dynamically gives a good compromise of low power consumption and low
latency. Some hardware have support for this in firmware, but some
require the host to do it.
The dynamic power save is implemented by adding an timeout to
ieee80211_subif_start_xmit(). The timeout can be enabled from userspace
with Wireless Extensions. For example, the command below enables the
dynamic power save and sets the time timeout to 500 ms:
iwconfig wlan0 power timeout 500m
Power save now only works with devices which handle power save in firmware.
It's also disabled by default and the heuristics when and how to enable is
considered as a policy decision and will be left for the userspace to handle.
In case the firmware has support for this, drivers can disable this feature
with IEEE80211_HW_NO_STACK_DYNAMIC_PS.
Big thanks to Johannes Berg for the help with the design and code.
Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
Acked-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.c | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index dac8bd37dcf5..5ba721b6a399 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -745,8 +745,14 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
745 | ieee80211_bss_info_change_notify(sdata, bss_info_changed); | 745 | ieee80211_bss_info_change_notify(sdata, bss_info_changed); |
746 | 746 | ||
747 | if (local->powersave) { | 747 | if (local->powersave) { |
748 | local->hw.conf.flags |= IEEE80211_CONF_PS; | 748 | if (local->dynamic_ps_timeout > 0) |
749 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | 749 | mod_timer(&local->dynamic_ps_timer, jiffies + |
750 | msecs_to_jiffies(local->dynamic_ps_timeout)); | ||
751 | else { | ||
752 | conf->flags |= IEEE80211_CONF_PS; | ||
753 | ieee80211_hw_config(local, | ||
754 | IEEE80211_CONF_CHANGE_PS); | ||
755 | } | ||
750 | } | 756 | } |
751 | 757 | ||
752 | netif_tx_start_all_queues(sdata->dev); | 758 | netif_tx_start_all_queues(sdata->dev); |
@@ -866,6 +872,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
866 | local->oper_channel_type = NL80211_CHAN_NO_HT; | 872 | local->oper_channel_type = NL80211_CHAN_NO_HT; |
867 | config_changed |= IEEE80211_CONF_CHANGE_HT; | 873 | config_changed |= IEEE80211_CONF_CHANGE_HT; |
868 | 874 | ||
875 | del_timer_sync(&local->dynamic_ps_timer); | ||
876 | cancel_work_sync(&local->dynamic_ps_enable_work); | ||
877 | |||
869 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | 878 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
870 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | 879 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
871 | config_changed |= IEEE80211_CONF_CHANGE_PS; | 880 | config_changed |= IEEE80211_CONF_CHANGE_PS; |
@@ -2593,3 +2602,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) | |||
2593 | ieee80211_restart_sta_timer(sdata); | 2602 | ieee80211_restart_sta_timer(sdata); |
2594 | rcu_read_unlock(); | 2603 | rcu_read_unlock(); |
2595 | } | 2604 | } |
2605 | |||
2606 | void ieee80211_dynamic_ps_disable_work(struct work_struct *work) | ||
2607 | { | ||
2608 | struct ieee80211_local *local = | ||
2609 | container_of(work, struct ieee80211_local, | ||
2610 | dynamic_ps_disable_work); | ||
2611 | |||
2612 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | ||
2613 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | ||
2614 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
2615 | } | ||
2616 | |||
2617 | ieee80211_wake_queues_by_reason(&local->hw, | ||
2618 | IEEE80211_QUEUE_STOP_REASON_PS); | ||
2619 | } | ||
2620 | |||
2621 | void ieee80211_dynamic_ps_enable_work(struct work_struct *work) | ||
2622 | { | ||
2623 | struct ieee80211_local *local = | ||
2624 | container_of(work, struct ieee80211_local, | ||
2625 | dynamic_ps_enable_work); | ||
2626 | |||
2627 | if (local->hw.conf.flags & IEEE80211_CONF_PS) | ||
2628 | return; | ||
2629 | |||
2630 | local->hw.conf.flags |= IEEE80211_CONF_PS; | ||
2631 | |||
2632 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
2633 | } | ||
2634 | |||
2635 | void ieee80211_dynamic_ps_timer(unsigned long data) | ||
2636 | { | ||
2637 | struct ieee80211_local *local = (void *) data; | ||
2638 | |||
2639 | queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); | ||
2640 | } | ||