aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorKalle Valo <kalle.valo@nokia.com>2008-12-18 16:35:27 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-12-19 15:24:00 -0500
commit520eb82076993b7f55ef9b80771d264272e5127b (patch)
treef98b74dbe404d4c3a55b5f649c25ca24958e62ba /net/mac80211/mlme.c
parentce7c9111a97492d04c504f40736a669c235d664a (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.c49
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
2606void 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
2621void 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
2635void 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}